ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBCustomDataSet.pas
Revision: 214
Committed: Thu Mar 15 13:07:49 2018 UTC (6 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 150751 byte(s)
Log Message:
Fixes Merged

File Contents

# Content
1 {************************************************************************}
2 { }
3 { Borland Delphi Visual Component Library }
4 { InterBase Express core components }
5 { }
6 { Copyright (c) 1998-2000 Inprise Corporation }
7 { }
8 { InterBase Express is based in part on the product }
9 { Free IB Components, written by Gregory H. Deatz for }
10 { Hoagland, Longo, Moran, Dunst & Doukas Company. }
11 { Free IB Components is used under license. }
12 { }
13 { The contents of this file are subject to the InterBase }
14 { Public License Version 1.0 (the "License"); you may not }
15 { use this file except in compliance with the License. You }
16 { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
17 { Software distributed under the License is distributed on }
18 { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
19 { express or implied. See the License for the specific language }
20 { governing rights and limitations under the License. }
21 { The Original Code was created by InterBase Software Corporation }
22 { and its successors. }
23 { Portions created by Inprise Corporation are Copyright (C) Inprise }
24 { Corporation. All Rights Reserved. }
25 { Contributor(s): Jeff Overcash }
26 { }
27 { IBX For Lazarus (Firebird Express) }
28 { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29 { Portions created by MWA Software are copyright McCallum Whyman }
30 { Associates Ltd 2011 - 2015 }
31 { }
32 {************************************************************************}
33
34 unit IBCustomDataSet;
35
36 {$R-}
37
38 {$IFDEF FPC}
39 {$Mode Delphi}
40 {$codepage UTF8}
41 {$ENDIF}
42
43 {$IFDEF DELPHI}
44 {$DEFINE TDBDFIELD_IS_BCD}
45 {$ENDIF}
46
47 interface
48
49 uses
50 {$IFDEF WINDOWS }
51 Windows,
52 {$ELSE}
53 unix,
54 {$ENDIF}
55 SysUtils, Classes, IBDatabase, IBExternals, IB, IBSQL, Db,
56 IBUtils, IBBlob, IBSQLParser, IBDatabaseInfo, fpTimer;
57
58 const
59 BufferCacheSize = 1000; { Allocate cache in this many record chunks}
60 UniCache = 2; { Uni-directional cache is 2 records big }
61
62 type
63 TIBCustomDataSet = class;
64 TIBDataSet = class;
65
66 { TIBDataSetUpdateObject }
67
68 TIBDataSetUpdateObject = class(TComponent)
69 private
70 FRefreshSQL: TStrings;
71 procedure SetRefreshSQL(value: TStrings);
72 protected
73 function GetDataSet: TIBCustomDataSet; virtual; abstract;
74 procedure SetDataSet(ADataSet: TIBCustomDataSet); virtual; abstract;
75 procedure Apply(UpdateKind: TUpdateKind; buff: PChar); virtual; abstract;
76 function GetSQL(UpdateKind: TUpdateKind): TStrings; virtual; abstract;
77 procedure InternalSetParams(Params: ISQLParams; buff: PChar); overload;
78 procedure InternalSetParams(Query: TIBSQL; buff: PChar); overload;
79 procedure UpdateRecordFromQuery(UpdateKind: TUpdateKind; QryResults: IResults; Buffer: PChar);
80 property DataSet: TIBCustomDataSet read GetDataSet write SetDataSet;
81 public
82 constructor Create(AOwner: TComponent); override;
83 destructor Destroy; override;
84 function GetRowsAffected(var SelectCount, InsertCount, UpdateCount,
85 DeleteCount: integer): boolean; virtual;
86 published
87 property RefreshSQL: TStrings read FRefreshSQL write SetRefreshSQL;
88 end;
89
90 TBlobDataArray = array[0..0] of TIBBlobStream;
91 PBlobDataArray = ^TBlobDataArray;
92 TIBArrayField = class;
93
94 { TIBArray }
95
96 {Wrapper class to support array cache in TIBCustomDataset and event handling}
97
98 TIBArray = class
99 private
100 FArray: IArray;
101 FRecNo: integer;
102 FField: TIBArrayField;
103 procedure EventHandler(Sender: IArray; Reason: TArrayEventReason);
104 public
105 constructor Create(aField: TIBArrayField; anArray: IArray);
106 destructor Destroy; override;
107 property ArrayIntf: IArray read FArray;
108 end;
109
110 TArrayDataArray = array [0..0] of TIBArray;
111 PArrayDataArray = ^TArrayDataArray;
112
113 { TIBCustomDataSet }
114
115 TCachedUpdateStatus = (
116 cusUnmodified, cusModified, cusInserted,
117 cusDeleted, cusUninserted
118 );
119 TIBDBKey = record
120 DBKey: array[0..7] of Byte;
121 end;
122 PIBDBKey = ^TIBDBKey;
123
124 PFieldData = ^TFieldData;
125 TFieldData = record
126 fdIsNull: Boolean;
127 fdDataLength: Short;
128 end;
129
130 PColumnData = ^TColumnData;
131 TColumnData = record
132 fdDataType: Short;
133 fdDataScale: Short;
134 fdNullable: Boolean;
135 fdDataSize: Short;
136 fdDataOfs: Integer;
137 fdCodePage: TSystemCodePage;
138 end;
139
140 PFieldColumns = ^TFieldColumns;
141 TFieldColumns = array[1..1] of TColumnData;
142
143 TRecordData = record
144 rdBookmarkFlag: TBookmarkFlag;
145 rdFieldCount: Short;
146 rdRecordNumber: Integer;
147 rdCachedUpdateStatus: TCachedUpdateStatus;
148 rdUpdateStatus: TUpdateStatus;
149 rdSavedOffset: DWORD;
150 rdDBKey: TIBDBKey;
151 rdFields: array[1..1] of TFieldData;
152 end;
153 PRecordData = ^TRecordData;
154
155 { TIBArrayField }
156
157 TIBArrayField = class(TField)
158 private
159 FArrayBounds: TArrayBounds;
160 FArrayDimensions: integer;
161 FRelationName: string;
162 FCacheOffset: word;
163 function GetArrayID: TISC_QUAD;
164 function GetArrayIntf: IArray;
165 procedure SetArrayIntf(AValue: IArray);
166 protected
167 class procedure CheckTypeSize(AValue: Longint); override;
168 function GetAsString: string; override;
169 function GetDataSize: Integer; override;
170 procedure Bind(Binding: Boolean); override;
171 public
172 constructor Create(AOwner: TComponent); override;
173 function CreateArray: IArray;
174 property ArrayID: TISC_QUAD read GetArrayID;
175 property ArrayIntf: IArray read GetArrayIntf write SetArrayIntf;
176 property ArrayDimensions: integer read FArrayDimensions write FArrayDimensions;
177 property ArrayBounds: TArrayBounds read FArrayBounds write FArrayBounds;
178 end;
179
180 { TIBStringField allows us to have strings longer than 8196 }
181
182 TIBStringField = class(TStringField)
183 private
184 FCharacterSetName: RawByteString;
185 FCharacterSetSize: integer;
186 FAutoFieldSize: boolean;
187 FCodePage: TSystemCodePage;
188 FDataSize: integer;
189 protected
190 procedure Bind(Binding: Boolean); override;
191 function GetDataSize: Integer; override;
192 public
193 constructor Create(aOwner: TComponent); override;
194 class procedure CheckTypeSize(Value: Integer); override;
195 function GetAsString: string; override;
196 function GetAsVariant: Variant; override;
197 function GetValue(var Value: string): Boolean;
198 procedure SetAsString(const Value: string); override;
199 property CharacterSetName: RawByteString read FCharacterSetName write FCharacterSetName;
200 property CharacterSetSize: integer read FCharacterSetSize write FCharacterSetSize;
201 property CodePage: TSystemCodePage read FCodePage write FCodePage;
202 published
203 property AutoFieldSize: boolean read FAutoFieldSize write FAutoFieldSize default true;
204 end;
205
206 { TIBBCDField }
207 { Actually, there is no BCD involved in this type,
208 instead it deals with currency types.
209 In IB, this is an encapsulation of Numeric (x, y)
210 where x < 18 and y <= 4.
211 Note: y > 4 will default to Floats
212 }
213 TIBBCDField = class(TBCDField)
214 private
215 FIdentityColumn: boolean;
216 protected
217 procedure Bind(Binding: Boolean); override;
218 class procedure CheckTypeSize(Value: Integer); override;
219 function GetAsCurrency: Currency; override;
220 function GetAsString: string; override;
221 function GetAsVariant: Variant; override;
222 function GetDataSize: Integer; override;
223 public
224 constructor Create(AOwner: TComponent); override;
225 property IdentityColumn: boolean read FIdentityColumn;
226 published
227 property Size default 8;
228 end;
229
230 {The following integer field types extend the built in versions to enable IBX appplications
231 to check for an Identity column}
232
233 { TIBSmallintField }
234
235 TIBSmallintField = class(TSmallintField)
236 private
237 FIdentityColumn: boolean;
238 protected
239 procedure Bind(Binding: Boolean); override;
240 public
241 property IdentityColumn: boolean read FIdentityColumn;
242 end;
243
244 { TIBIntegerField }
245
246 TIBIntegerField = class(TIntegerField)
247 private
248 FIdentityColumn: boolean;
249 protected
250 procedure Bind(Binding: Boolean); override;
251 public
252 property IdentityColumn: boolean read FIdentityColumn;
253 end;
254
255 { TIBLargeIntField }
256
257 TIBLargeIntField = class(TLargeIntField)
258 private
259 FIdentityColumn: boolean;
260 protected
261 procedure Bind(Binding: Boolean); override;
262 public
263 property IdentityColumn: boolean read FIdentityColumn;
264 end;
265
266 {TIBMemoField}
267 {Allows us to show truncated text in DBGrids and anything else that uses
268 DisplayText}
269
270 TIBMemoField = class(TMemoField)
271 private
272 FCharacterSetName: RawByteString;
273 FCharacterSetSize: integer;
274 FDisplayTextAsClassName: boolean;
275 function GetTruncatedText: string;
276 protected
277 procedure Bind(Binding: Boolean); override;
278 function GetAsString: string; override;
279 function GetDefaultWidth: Longint; override;
280 procedure GetText(var AText: string; ADisplayText: Boolean); override;
281 procedure SetAsString(const AValue: string); override;
282 public
283 constructor Create(AOwner: TComponent); override;
284 property CharacterSetName: RawByteString read FCharacterSetName write FCharacterSetName;
285 property CharacterSetSize: integer read FCharacterSetSize write FCharacterSetSize;
286 published
287 property DisplayTextAsClassName: boolean read FDisplayTextAsClassName
288 write FDisplayTextAsClassName;
289 private
290 FCodePage: TSystemCodePage;
291 FFCodePage: TSystemCodePage;
292 public
293 property CodePage: TSystemCodePage read FFCodePage write FFCodePage;
294 end;
295
296 { TIBDataLink }
297
298 TIBDataLink = class(TDetailDataLink)
299 private
300 FDataSet: TIBCustomDataSet;
301 FDelayTimerValue: integer;
302 FTimer: TFPTimer;
303 procedure HandleRefreshTimer(Sender: TObject);
304 procedure SetDelayTimerValue(AValue: integer);
305 protected
306 procedure ActiveChanged; override;
307 procedure RecordChanged(Field: TField); override;
308 function GetDetailDataSet: TDataSet; override;
309 procedure CheckBrowseMode; override;
310 public
311 constructor Create(ADataSet: TIBCustomDataSet);
312 destructor Destroy; override;
313 property DelayTimerValue: integer {in Milliseconds}
314 read FDelayTimerValue write SetDelayTimerValue;
315 end;
316
317 TIBGeneratorApplyOnEvent = (gaeOnNewRecord,gaeOnPostRecord);
318
319 { TIBGenerator }
320
321 TIBGenerator = class(TPersistent)
322 private
323 FOwner: TIBCustomDataSet;
324 FApplyOnEvent: TIBGeneratorApplyOnEvent;
325 FFieldName: string;
326 FGeneratorName: string;
327 FIncrement: integer;
328 FQuery: TIBSQL;
329 function GetDatabase: TIBDatabase;
330 function GetTransaction: TIBTransaction;
331 procedure SetDatabase(AValue: TIBDatabase);
332 procedure SetGeneratorName(AValue: string);
333 procedure SetIncrement(const AValue: integer);
334 procedure SetTransaction(AValue: TIBTransaction);
335 procedure SetQuerySQL;
336 protected
337 function GetNextValue: integer;
338 public
339 constructor Create(Owner: TIBCustomDataSet);
340 destructor Destroy; override;
341 procedure Apply;
342 property Owner: TIBCustomDataSet read FOwner;
343 property Database: TIBDatabase read GetDatabase write SetDatabase;
344 property Transaction: TIBTransaction read GetTransaction write SetTransaction;
345 published
346 property Generator: string read FGeneratorName write SetGeneratorName;
347 property Field: string read FFieldName write FFieldName;
348 property Increment: integer read FIncrement write SetIncrement default 1;
349 property ApplyOnEvent: TIBGeneratorApplyOnEvent read FApplyOnEvent write FApplyOnEvent;
350 end;
351
352 {TIBControlLink - Allows IB Aware controls to react to dataset state changes}
353
354 TIBControlLink = class
355 private
356 FTIBDataSet: TIBCustomDataSet;
357 procedure SetIBDataSet(AValue: TIBCustomDataSet);
358 protected
359 procedure UpdateSQL(Sender: TObject); virtual;
360 procedure UpdateParams(Sender: TObject); virtual;
361 public
362 destructor Destroy; override;
363 property IBDataSet: TIBCustomDataSet read FTIBDataSet write SetIBDataSet;
364 end;
365
366 TIBAutoCommit = (acDisabled, acCommitRetaining);
367
368 { TIBCustomDataSet }
369
370 TIBUpdateAction = (uaFail, uaAbort, uaSkip, uaRetry, uaApply, uaApplied);
371
372 TIBUpdateErrorEvent = procedure(DataSet: TDataSet; E: EDatabaseError;
373 UpdateKind: TUpdateKind; var TheUpdateAction: TIBUpdateAction)
374 of object;
375 TIBUpdateRecordEvent = procedure(DataSet: TDataSet; UpdateKind: TUpdateKind;
376 var UpdateAction: TIBUpdateAction) of object;
377
378 TIBUpdateRecordTypes = set of TCachedUpdateStatus;
379
380 TDataSetCloseAction = (dcDiscardChanges, dcSaveChanges);
381
382 TOnValidatePost = procedure (Sender: TObject; var CancelPost: boolean) of object;
383
384 TOnDeleteReturning = procedure (Sender: TObject; QryResults: IResults) of object;
385
386 TIBCustomDataSet = class(TDataset)
387 private
388 FAllowAutoActivateTransaction: Boolean;
389 FArrayFieldCount: integer;
390 FArrayCacheOffset: integer;
391 FAutoCommit: TIBAutoCommit;
392 FEnableStatistics: boolean;
393 FGenerateParamNames: Boolean;
394 FGeneratorField: TIBGenerator;
395 FNeedsRefresh: Boolean;
396 FForcedRefresh: Boolean;
397 FDidActivate: Boolean;
398 FBase: TIBBase;
399 FBlobCacheOffset: Integer;
400 FBlobStreamList: TList;
401 FArrayList: TList;
402 FBufferChunks: Integer;
403 FBufferCache,
404 FOldBufferCache: PChar;
405 FBufferChunkSize,
406 FCacheSize,
407 FOldCacheSize: Integer;
408 FFilterBuffer: PChar;
409 FBPos,
410 FOBPos,
411 FBEnd,
412 FOBEnd: DWord;
413 FCachedUpdates: Boolean;
414 FCalcFieldsOffset: Integer;
415 FCurrentRecord: Long;
416 FDeletedRecords: Long;
417 FModelBuffer,
418 FOldBuffer: PChar;
419 FOnDeleteReturning: TOnDeleteReturning;
420 FOnValidatePost: TOnValidatePost;
421 FOpen: Boolean;
422 FInternalPrepared: Boolean;
423 FQDelete,
424 FQInsert,
425 FQRefresh,
426 FQSelect,
427 FQModify: TIBSQL;
428 FDatabaseInfo: TIBDatabaseInfo;
429 FRecordBufferSize: Integer;
430 FRecordCount: Integer;
431 FRecordSize: Integer;
432 FDataSetCloseAction: TDataSetCloseAction;
433 FUniDirectional: Boolean;
434 FUpdateMode: TUpdateMode;
435 FUpdateObject: TIBDataSetUpdateObject;
436 FParamCheck: Boolean;
437 FUpdatesPending: Boolean;
438 FUpdateRecordTypes: TIBUpdateRecordTypes;
439 FMappedFieldPosition: array of Integer;
440 FDataLink: TIBDataLink;
441
442 FBeforeDatabaseDisconnect,
443 FAfterDatabaseDisconnect,
444 FDatabaseFree: TNotifyEvent;
445 FOnUpdateError: TIBUpdateErrorEvent;
446 FOnUpdateRecord: TIBUpdateRecordEvent;
447 FBeforeTransactionEnd,
448 FAfterTransactionEnd,
449 FTransactionFree: TNotifyEvent;
450 FAliasNameMap: array of string;
451 FAliasNameList: array of string;
452 FBaseSQLSelect: TStrings;
453 FParser: TSelectSQLParser;
454 FCloseAction: TTransactionAction;
455 FInTransactionEnd: boolean;
456 FIBLinks: TList;
457 FFieldColumns: PFieldColumns;
458 FBufferUpdatedOnQryReturn: boolean;
459 FSelectCount: integer;
460 FInsertCount: integer;
461 FUpdateCount: integer;
462 FDeleteCount: integer;
463 procedure ColumnDataToBuffer(QryResults: IResults; ColumnIndex,
464 FieldIndex: integer; Buffer: PChar);
465 procedure InitModelBuffer(Qry: TIBSQL; Buffer: PChar);
466 function GetSelectStmtIntf: IStatement;
467 procedure SetUpdateMode(const Value: TUpdateMode);
468 procedure SetUpdateObject(Value: TIBDataSetUpdateObject);
469
470 function AdjustCurrentRecord(Buffer: Pointer; GetMode: TGetMode): TGetResult;
471 procedure AdjustRecordOnInsert(Buffer: Pointer);
472 function CanEdit: Boolean;
473 function CanInsert: Boolean;
474 function CanDelete: Boolean;
475 function CanRefresh: Boolean;
476 procedure CheckEditState;
477 procedure ClearBlobCache;
478 procedure ClearArrayCache;
479 procedure ClearIBLinks;
480 procedure CopyRecordBuffer(Source, Dest: Pointer);
481 procedure DoBeforeDatabaseDisconnect(Sender: TObject);
482 procedure DoAfterDatabaseDisconnect(Sender: TObject);
483 procedure DoDatabaseFree(Sender: TObject);
484 procedure DoBeforeTransactionEnd(Sender: TObject; Action: TTransactionAction);
485 procedure DoAfterTransactionEnd(Sender: TObject);
486 procedure DoTransactionFree(Sender: TObject);
487 procedure DoDeleteReturning(QryResults: IResults);
488 procedure FetchCurrentRecordToBuffer(Qry: TIBSQL; RecordNumber: Integer;
489 Buffer: PChar);
490 function GetDatabase: TIBDatabase;
491 function GetDeleteSQL: TStrings;
492 function GetInsertSQL: TStrings;
493 function GetSQLParams: ISQLParams;
494 function GetRefreshSQL: TStrings;
495 function GetSelectSQL: TStrings;
496 function GetStatementType: TIBSQLStatementTypes;
497 function GetModifySQL: TStrings;
498 function GetTransaction: TIBTransaction;
499 function GetParser: TSelectSQLParser;
500 procedure InternalDeleteRecord(Qry: TIBSQL; Buff: Pointer); virtual;
501 function InternalLocate(const KeyFields: string; const KeyValues: Variant;
502 Options: TLocateOptions): Boolean; virtual;
503 procedure InternalPostRecord(Qry: TIBSQL; Buff: Pointer); virtual;
504 procedure InternalRevertRecord(RecordNumber: Integer); virtual;
505 function IsVisible(Buffer: PChar): Boolean;
506 procedure RegisterIBLink(Sender: TIBControlLink);
507 procedure UnRegisterIBLink(Sender: TIBControlLink);
508 procedure SaveOldBuffer(Buffer: PChar);
509 procedure SetBufferChunks(Value: Integer);
510 procedure SetDatabase(Value: TIBDatabase);
511 procedure SetDeleteSQL(Value: TStrings);
512 procedure SetInsertSQL(Value: TStrings);
513 procedure SetInternalSQLParams(Params: ISQLParams; Buffer: Pointer);
514 procedure SetRefreshSQL(Value: TStrings);
515 procedure SetSelectSQL(Value: TStrings);
516 procedure SetModifySQL(Value: TStrings);
517 procedure SetTransaction(Value: TIBTransaction);
518 procedure SetUpdateRecordTypes(Value: TIBUpdateRecordTypes);
519 procedure SetUniDirectional(Value: Boolean);
520 procedure UpdateRecordFromQuery(QryResults: IResults; Buffer: PChar);
521 procedure RefreshParams;
522 function AdjustPosition(FCache: PChar; Offset: DWORD;
523 Origin: Integer): DWORD;
524 procedure ReadCache(FCache: PChar; Offset: DWORD; Origin: Integer;
525 Buffer: PChar);
526 procedure ReadRecordCache(RecordNumber: Integer; Buffer: PChar;
527 ReadOldBuffer: Boolean);
528 procedure WriteCache(FCache: PChar; Offset: DWORD; Origin: Integer;
529 Buffer: PChar);
530 procedure WriteRecordCache(RecordNumber: Integer; Buffer: PChar);
531 function InternalGetRecord(Buffer: PChar; GetMode: TGetMode;
532 DoCheck: Boolean): TGetResult; virtual;
533
534 protected
535 function GetMasterDetailDelay: integer; virtual;
536 procedure SetMasterDetailDelay(AValue: integer); virtual;
537 procedure ActivateConnection;
538 function ActivateTransaction: Boolean;
539 procedure DeactivateTransaction;
540 procedure CheckDatasetClosed;
541 procedure CheckDatasetOpen;
542 function CreateParser: TSelectSQLParser; virtual;
543 procedure FieldDefsFromQuery(SourceQuery: TIBSQL);
544 function GetActiveBuf: PChar;
545 procedure InternalBatchInput(InputObject: TIBBatchInput); virtual;
546 procedure InternalBatchOutput(OutputObject: TIBBatchOutput); virtual;
547 procedure InternalPrepare; virtual;
548 procedure InternalUnPrepare; virtual;
549 procedure InternalExecQuery; virtual;
550 procedure InternalRefreshRow; virtual;
551 procedure InternalSetParamsFromCursor; virtual;
552 procedure CheckNotUniDirectional;
553 procedure SQLChanging(Sender: TObject); virtual;
554 procedure SQLChanged(Sender: TObject); virtual;
555
556 { IProviderSupport }
557 procedure PSEndTransaction(Commit: Boolean); override;
558 function PSExecuteStatement(const ASQL: string; AParams: TParams;
559 ResultSet: Pointer = nil): Integer; override;
560 function PsGetTableName: string; override;
561 function PSGetQuoteChar: string; override;
562 function PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError; override;
563 function PSInTransaction: Boolean; override;
564 function PSIsSQLBased: Boolean; override;
565 function PSIsSQLSupported: Boolean; override;
566 procedure PSStartTransaction; override;
567 procedure PSReset; override;
568 function PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean; override;
569
570 { TDataSet support }
571 procedure InternalInsert; override;
572 procedure InitRecord(Buffer: PChar); override;
573 procedure Disconnect; virtual;
574 function ConstraintsStored: Boolean;
575 procedure ClearCalcFields(Buffer: PChar); override;
576 function AllocRecordBuffer: PChar; override;
577 procedure DoBeforeDelete; override;
578 procedure DoAfterDelete; override;
579 procedure DoBeforeEdit; override;
580 procedure DoAfterEdit; override;
581 procedure DoBeforeInsert; override;
582 procedure DoAfterInsert; override;
583 procedure DoBeforeClose; override;
584 procedure DoBeforeOpen; override;
585 procedure DoBeforePost; override;
586 procedure DoAfterPost; override;
587 procedure FreeRecordBuffer(var Buffer: PChar); override;
588 procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
589 function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
590 function GetCanModify: Boolean; override;
591 function GetDataSource: TDataSource; override;
592 function GetDBAliasName(FieldNo: integer): string;
593 function GetFieldDefFromAlias(aliasName: string): TFieldDef;
594 function GetFieldClass(FieldType: TFieldType): TFieldClass; override;
595 function GetRecNo: Integer; override;
596 function GetRecord(Buffer: PChar; GetMode: TGetMode;
597 DoCheck: Boolean): TGetResult; override;
598 function GetRecordCount: Integer; override;
599 function GetRecordSize: Word; override;
600 procedure InternalAutoCommit;
601 procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
602 procedure InternalCancel; override;
603 procedure InternalClose; override;
604 procedure InternalDelete; override;
605 procedure InternalFirst; override;
606 function InternalGetFieldData(Field: TField; Buffer: Pointer): Boolean; virtual;
607 procedure InternalGotoBookmark(Bookmark: Pointer); override;
608 procedure InternalHandleException; override;
609 procedure InternalInitFieldDefs; override;
610 procedure InternalInitRecord(Buffer: PChar); override;
611 procedure InternalLast; override;
612 procedure InternalOpen; override;
613 procedure InternalPost; override;
614 procedure InternalRefresh; override;
615 procedure InternalSetFieldData(Field: TField; Buffer: Pointer); virtual;
616 procedure InternalSetToRecord(Buffer: PChar); override;
617 function IsCursorOpen: Boolean; override;
618 procedure Loaded; override;
619 procedure ReQuery;
620 procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
621 procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
622 procedure SetCachedUpdates(Value: Boolean);
623 procedure SetDataSource(Value: TDataSource);
624 procedure SetGenerateParamNames(AValue: Boolean); virtual;
625 procedure SetFieldData(Field : TField; Buffer : Pointer); override;
626 procedure SetFieldData(Field : TField; Buffer : Pointer;
627 NativeFormat : Boolean); overload; override;
628 procedure SetRecNo(Value: Integer); override;
629
630 protected
631 {Likely to be made public by descendant classes}
632 property AutoCommit: TIBAutoCommit read FAutoCommit write FAutoCommit default acDisabled;
633 property SQLParams: ISQLParams read GetSQLParams;
634 property Params: ISQLParams read GetSQLParams;
635 property InternalPrepared: Boolean read FInternalPrepared;
636 property QDelete: TIBSQL read FQDelete;
637 property QInsert: TIBSQL read FQInsert;
638 property QRefresh: TIBSQL read FQRefresh;
639 property QSelect: TIBSQL read FQSelect;
640 property QModify: TIBSQL read FQModify;
641 property StatementType: TIBSQLStatementTypes read GetStatementType;
642 property SelectStmtHandle: IStatement read GetSelectStmtIntf;
643
644 {Likely to be made published by descendant classes}
645 property BufferChunks: Integer read FBufferChunks write SetBufferChunks;
646 property CachedUpdates: Boolean read FCachedUpdates write SetCachedUpdates;
647 property UniDirectional: Boolean read FUniDirectional write SetUniDirectional default False;
648 property GeneratorField: TIBGenerator read FGeneratorField write FGeneratorField;
649 property DeleteSQL: TStrings read GetDeleteSQL write SetDeleteSQL;
650 property InsertSQL: TStrings read GetInsertSQL write SetInsertSQL;
651 property RefreshSQL: TStrings read GetRefreshSQL write SetRefreshSQL;
652 property SelectSQL: TStrings read GetSelectSQL write SetSelectSQL;
653 property ModifySQL: TStrings read GetModifySQL write SetModifySQL;
654 property UpdateMode: TUpdateMode read FUpdateMode write SetUpdateMode default upWhereAll;
655 property ParamCheck: Boolean read FParamCheck write FParamCheck default True;
656 property Parser: TSelectSQLParser read GetParser;
657 property BaseSQLSelect: TStrings read FBaseSQLSelect;
658
659 property BeforeDatabaseDisconnect: TNotifyEvent read FBeforeDatabaseDisconnect
660 write FBeforeDatabaseDisconnect;
661 property AfterDatabaseDisconnect: TNotifyEvent read FAfterDatabaseDisconnect
662 write FAfterDatabaseDisconnect;
663 property DatabaseFree: TNotifyEvent read FDatabaseFree
664 write FDatabaseFree;
665 property BeforeTransactionEnd: TNotifyEvent read FBeforeTransactionEnd
666 write FBeforeTransactionEnd;
667 property AfterTransactionEnd: TNotifyEvent read FAfterTransactionEnd
668 write FAfterTransactionEnd;
669 property TransactionFree: TNotifyEvent read FTransactionFree
670 write FTransactionFree;
671 property OnValidatePost: TOnValidatePost read FOnValidatePost write FOnValidatePost;
672
673 public
674 constructor Create(AOwner: TComponent); override;
675 destructor Destroy; override;
676 procedure ApplyUpdates;
677 function CachedUpdateStatus: TCachedUpdateStatus;
678 procedure CancelUpdates;
679 function GetFieldPosition(AliasName: string): integer;
680 procedure FetchAll;
681 function LocateNext(const KeyFields: string; const KeyValues: Variant;
682 Options: TLocateOptions): Boolean;
683 procedure RecordModified(Value: Boolean);
684 procedure RevertRecord;
685 procedure Undelete;
686 procedure ResetParser; virtual;
687 function HasParser: boolean;
688
689 { TDataSet support methods }
690 function BookmarkValid(Bookmark: TBookmark): Boolean; override;
691 function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override;
692 function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
693 function GetArray(Field: TIBArrayField): IArray;
694 procedure SetArrayIntf(AnArray: IArray; Field: TIBArrayField);
695 function GetCurrentRecord(Buffer: PChar): Boolean; override;
696 function GetFieldData(Field : TField; Buffer : Pointer) : Boolean; overload; override;
697 function GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean; overload; (*override;*)
698 function GetFieldData(Field : TField; Buffer : Pointer;
699 NativeFormat : Boolean) : Boolean; overload; override;
700 property GenerateParamNames: Boolean read FGenerateParamNames write SetGenerateParamNames;
701 function Locate(const KeyFields: string; const KeyValues: Variant;
702 Options: TLocateOptions): Boolean; override;
703 function Lookup(const KeyFields: string; const KeyValues: Variant;
704 const ResultFields: string): Variant; override;
705 function UpdateStatus: TUpdateStatus; override;
706 function IsSequenced: Boolean; override;
707 procedure Post; override;
708 function ParamByName(ParamName: String): ISQLParam;
709 property ArrayFieldCount: integer read FArrayFieldCount;
710 property DatabaseInfo: TIBDatabaseInfo read FDatabaseInfo;
711 property UpdateObject: TIBDataSetUpdateObject read FUpdateObject write SetUpdateObject;
712 property UpdatesPending: Boolean read FUpdatesPending;
713 property UpdateRecordTypes: TIBUpdateRecordTypes read FUpdateRecordTypes
714 write SetUpdateRecordTypes;
715 property MasterDetailDelay: integer read GetMasterDetailDelay write SetMasterDetailDelay;
716 property DataSetCloseAction: TDataSetCloseAction
717 read FDataSetCloseAction write FDataSetCloseAction;
718
719 public
720 {Performance Statistics}
721 function GetRowsAffected(var SelectCount, InsertCount, UpdateCount, DeleteCount: integer): boolean;
722 function GetPerfStatistics(var stats: TPerfCounters): boolean;
723 property EnableStatistics: boolean read FEnableStatistics write FEnableStatistics;
724
725 published
726 property AllowAutoActivateTransaction: Boolean read FAllowAutoActivateTransaction
727 write FAllowAutoActivateTransaction;
728 property Database: TIBDatabase read GetDatabase write SetDatabase;
729 property Transaction: TIBTransaction read GetTransaction
730 write SetTransaction;
731 property ForcedRefresh: Boolean read FForcedRefresh
732 write FForcedRefresh default False;
733 property AutoCalcFields;
734
735 property AfterCancel;
736 property AfterClose;
737 property AfterDelete;
738 property AfterEdit;
739 property AfterInsert;
740 property AfterOpen;
741 property AfterPost;
742 property AfterRefresh;
743 property AfterScroll;
744 property BeforeCancel;
745 property BeforeClose;
746 property BeforeDelete;
747 property BeforeEdit;
748 property BeforeInsert;
749 property BeforeOpen;
750 property BeforePost;
751 property BeforeRefresh;
752 property BeforeScroll;
753 property OnCalcFields;
754 property OnDeleteError;
755 property OnEditError;
756 property OnNewRecord;
757 property OnPostError;
758 property OnUpdateError: TIBUpdateErrorEvent read FOnUpdateError
759 write FOnUpdateError;
760 property OnUpdateRecord: TIBUpdateRecordEvent read FOnUpdateRecord
761 write FOnUpdateRecord;
762 property OnDeleteReturning: TOnDeleteReturning read FOnDeleteReturning
763 write FOnDeleteReturning;
764 end;
765
766 TIBParserDataSet = class(TIBCustomDataSet)
767 public
768 property Parser;
769 end;
770
771 TIBDataSet = class(TIBParserDataSet)
772 private
773 function GetPrepared: Boolean;
774
775 protected
776 procedure SetFiltered(Value: Boolean); override;
777 procedure InternalOpen; override;
778
779 public
780 procedure Prepare;
781 procedure UnPrepare;
782 procedure BatchInput(InputObject: TIBBatchInput);
783 procedure BatchOutput(OutputObject: TIBBatchOutput);
784 procedure ExecSQL;
785
786 public
787 property Params;
788 property Prepared : Boolean read GetPrepared;
789 property QDelete;
790 property QInsert;
791 property QRefresh;
792 property QSelect;
793 property QModify;
794 property StatementType;
795 property SelectStmtHandle;
796 property BaseSQLSelect;
797
798 published
799 { TIBCustomDataSet }
800 property AutoCommit;
801 property BufferChunks;
802 property CachedUpdates;
803 property EnableStatistics;
804 property DeleteSQL;
805 property InsertSQL;
806 property RefreshSQL;
807 property SelectSQL;
808 property ModifySQL;
809 property GeneratorField;
810 property GenerateParamNames;
811 property MasterDetailDelay;
812 property ParamCheck;
813 property UniDirectional;
814 property Filtered;
815 property DataSetCloseAction;
816
817 property BeforeDatabaseDisconnect;
818 property AfterDatabaseDisconnect;
819 property DatabaseFree;
820 property BeforeTransactionEnd;
821 property AfterTransactionEnd;
822 property TransactionFree;
823
824 { TIBDataSet }
825 property Active;
826 property AutoCalcFields;
827 property DataSource read GetDataSource write SetDataSource;
828
829 property AfterCancel;
830 property AfterClose;
831 property AfterDelete;
832 property AfterEdit;
833 property AfterInsert;
834 property AfterOpen;
835 property AfterPost;
836 property AfterScroll;
837 property BeforeCancel;
838 property BeforeClose;
839 property BeforeDelete;
840 property BeforeEdit;
841 property BeforeInsert;
842 property BeforeOpen;
843 property BeforePost;
844 property BeforeScroll;
845 property OnCalcFields;
846 property OnDeleteError;
847 property OnEditError;
848 property OnFilterRecord;
849 property OnNewRecord;
850 property OnPostError;
851 property OnValidatePost;
852 property OnDeleteReturning;
853 end;
854
855 { TIBDSBlobStream }
856 TIBDSBlobStream = class(TStream)
857 private
858 FHasWritten: boolean;
859 protected
860 FField: TField;
861 FBlobStream: TIBBlobStream;
862 function GetSize: Int64; override;
863 public
864 constructor Create(AField: TField; ABlobStream: TIBBlobStream;
865 Mode: TBlobStreamMode);
866 destructor Destroy; override;
867 function Read(var Buffer; Count: Longint): Longint; override;
868 function Seek(Offset: Longint; Origin: Word): Longint; override;
869 procedure SetSize(NewSize: Longint); override;
870 function Write(const Buffer; Count: Longint): Longint; override;
871 end;
872
873 {Extended Field Def for character set info}
874
875 { TIBFieldDef }
876
877 TIBFieldDef = class(TFieldDef)
878 private
879 FArrayBounds: TArrayBounds;
880 FArrayDimensions: integer;
881 FCharacterSetName: RawByteString;
882 FCharacterSetSize: integer;
883 FCodePage: TSystemCodePage;
884 FIdentityColumn: boolean;
885 FRelationName: string;
886 FDataSize: integer;
887 published
888 property CharacterSetName: RawByteString read FCharacterSetName write FCharacterSetName;
889 property CharacterSetSize: integer read FCharacterSetSize write FCharacterSetSize;
890 property CodePage: TSystemCodePage read FCodePage write FCodePage;
891 property DataSize: integer read FDataSize write FDataSize;
892 property RelationName: string read FRelationName write FRelationName;
893 property ArrayDimensions: integer read FArrayDimensions write FArrayDimensions;
894 property ArrayBounds: TArrayBounds read FArrayBounds write FArrayBounds;
895 property IdentityColumn: boolean read FIdentityColumn write FIdentityColumn default false;
896 end;
897
898 const
899 DefaultFieldClasses: array[TFieldType] of TFieldClass = (
900 nil, { ftUnknown }
901 TIBStringField, { ftString }
902 TIBSmallintField, { ftSmallint }
903 TIBIntegerField, { ftInteger }
904 TWordField, { ftWord }
905 TBooleanField, { ftBoolean }
906 TFloatField, { ftFloat }
907 TCurrencyField, { ftCurrency }
908 TIBBCDField, { ftBCD }
909 TDateField, { ftDate }
910 TTimeField, { ftTime }
911 TDateTimeField, { ftDateTime }
912 TBytesField, { ftBytes }
913 TVarBytesField, { ftVarBytes }
914 TAutoIncField, { ftAutoInc }
915 TBlobField, { ftBlob }
916 TIBMemoField, { ftMemo }
917 TGraphicField, { ftGraphic }
918 TBlobField, { ftFmtMemo }
919 TBlobField, { ftParadoxOle }
920 TBlobField, { ftDBaseOle }
921 TBlobField, { ftTypedBinary }
922 nil, { ftCursor }
923 TStringField, { ftFixedChar }
924 nil, { ftWideString }
925 TIBLargeIntField, { ftLargeInt }
926 nil, { ftADT }
927 TIBArrayField, { ftArray }
928 nil, { ftReference }
929 nil, { ftDataSet }
930 TBlobField, { ftOraBlob }
931 TMemoField, { ftOraClob }
932 TVariantField, { ftVariant }
933 nil, { ftInterface }
934 nil, { ftIDispatch }
935 TGuidField, { ftGuid }
936 TDateTimeField, {ftTimestamp}
937 TIBBCDField, {ftFMTBcd}
938 nil, {ftFixedWideChar}
939 nil); {ftWideMemo}
940 (*
941 TADTField, { ftADT }
942 TArrayField, { ftArray }
943 TReferenceField, { ftReference }
944 TDataSetField, { ftDataSet }
945 TBlobField, { ftOraBlob }
946 TMemoField, { ftOraClob }
947 TVariantField, { ftVariant }
948 TInterfaceField, { ftInterface }
949 TIDispatchField, { ftIDispatch }
950 TGuidField); { ftGuid } *)
951 (*var
952 CreateProviderProc: function(DataSet: TIBCustomDataSet): IProvider = nil;*)
953
954 implementation
955
956 uses Variants, FmtBCD, LazUTF8, FBMessages, IBQuery;
957
958 const FILE_BEGIN = 0;
959 FILE_CURRENT = 1;
960 FILE_END = 2;
961
962 type
963
964 TFieldNode = class(TObject)
965 protected
966 FieldName : String;
967 COMPUTED_BLR : Boolean;
968 DEFAULT_VALUE : boolean;
969 IDENTITY_COLUMN : boolean;
970 NextField : TFieldNode;
971 end;
972
973 TRelationNode = class(TObject)
974 protected
975 RelationName : String;
976 FieldNodes : TFieldNode;
977 NextRelation : TRelationNode;
978 end;
979
980
981 { Copied from LCLProc in order to avoid LCL dependency
982
983 Ensures the covenient look of multiline string
984 when displaying it in the single line
985 * Replaces CR and LF with spaces
986 * Removes duplicate spaces
987 }
988 function TextToSingleLine(const AText: string): string;
989 var
990 str: string;
991 i, wstart, wlen: Integer;
992 begin
993 str := Trim(AText);
994 wstart := 0;
995 wlen := 0;
996 i := 1;
997 while i < Length(str) - 1 do
998 begin
999 if (str[i] in [' ', #13, #10]) then
1000 begin
1001 if (wstart = 0) then
1002 begin
1003 wstart := i;
1004 wlen := 1;
1005 end else
1006 Inc(wlen);
1007 end else
1008 begin
1009 if wstart > 0 then
1010 begin
1011 str[wstart] := ' ';
1012 Delete(str, wstart+1, wlen-1);
1013 Dec(i, wlen-1);
1014 wstart := 0;
1015 end;
1016 end;
1017 Inc(i);
1018 end;
1019 Result := str;
1020 end;
1021
1022 { TIBLargeIntField }
1023
1024 procedure TIBLargeIntField.Bind(Binding: Boolean);
1025 begin
1026 inherited Bind(Binding);
1027 if Binding and (FieldDef <> nil) then
1028 FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1029 end;
1030
1031 { TIBIntegerField }
1032
1033 procedure TIBIntegerField.Bind(Binding: Boolean);
1034 begin
1035 inherited Bind(Binding);
1036 if Binding and (FieldDef <> nil) then
1037 FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1038 end;
1039
1040 { TIBSmallintField }
1041
1042 procedure TIBSmallintField.Bind(Binding: Boolean);
1043 begin
1044 inherited Bind(Binding);
1045 if Binding and (FieldDef <> nil) then
1046 FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1047 end;
1048
1049 { TIBArray }
1050
1051 procedure TIBArray.EventHandler(Sender: IArray; Reason: TArrayEventReason);
1052 begin
1053 case Reason of
1054 arChanging:
1055 if FRecNo <> FField.Dataset.RecNo then
1056 IBError(ibxeNotCurrentArray,[nil]);
1057
1058 arChanged:
1059 FField.DataChanged;
1060 end;
1061 end;
1062
1063 constructor TIBArray.Create(aField: TIBArrayField; anArray: IArray);
1064 begin
1065 inherited Create;
1066 FField := aField;
1067 FArray := anArray;
1068 FRecNo := FField.Dataset.RecNo;
1069 FArray.AddEventHandler(EventHandler);
1070 end;
1071
1072 destructor TIBArray.Destroy;
1073 begin
1074 FArray.RemoveEventHandler(EventHandler);
1075 inherited Destroy;
1076 end;
1077
1078 { TIBArrayField }
1079
1080 function TIBArrayField.GetArrayIntf: IArray;
1081 begin
1082 Result := TIBCustomDataSet(DataSet).GetArray(self);
1083 end;
1084
1085 function TIBArrayField.GetArrayID: TISC_QUAD;
1086 begin
1087 GetData(@Result);
1088 end;
1089
1090 procedure TIBArrayField.SetArrayIntf(AValue: IArray);
1091 begin
1092 TIBCustomDataSet(DataSet).SetArrayIntf(AValue,self);
1093 DataChanged;
1094 end;
1095
1096 class procedure TIBArrayField.CheckTypeSize(AValue: Longint);
1097 begin
1098 //Ignore
1099 end;
1100
1101 function TIBArrayField.GetAsString: string;
1102 begin
1103 Result := '(Array)';
1104 end;
1105
1106 function TIBArrayField.GetDataSize: Integer;
1107 begin
1108 Result := sizeof(TISC_QUAD);
1109 end;
1110
1111 procedure TIBArrayField.Bind(Binding: Boolean);
1112 begin
1113 inherited Bind(Binding);
1114 if Binding then
1115 begin
1116 FCacheOffset := TIBCustomDataSet(DataSet).ArrayFieldCount;
1117 Inc(TIBCustomDataSet(DataSet).FArrayFieldCount);
1118 if FieldDef <> nil then
1119 begin
1120 FRelationName := TIBFieldDef(FieldDef).FRelationName;
1121 FArrayDimensions := TIBFieldDef(FieldDef).ArrayDimensions;
1122 FArrayBounds := TIBFieldDef(FieldDef).ArrayBounds;
1123 end;
1124 end;
1125 end;
1126
1127 constructor TIBArrayField.Create(AOwner: TComponent);
1128 begin
1129 inherited Create(AOwner);
1130 SetDataType(ftArray);
1131 end;
1132
1133 function TIBArrayField.CreateArray: IArray;
1134 begin
1135 with DataSet as TIBCustomDataSet do
1136 Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,FRelationName,FieldName);
1137 end;
1138
1139 { TIBMemoField }
1140
1141 function TIBMemoField.GetTruncatedText: string;
1142 begin
1143 Result := GetAsString;
1144
1145 if Result <> '' then
1146 begin
1147 case CharacterSetSize of
1148 1:
1149 if DisplayWidth = 0 then
1150 Result := TextToSingleLine(Result)
1151 else
1152 if Length(Result) > DisplayWidth then {Show truncation with elipses}
1153 Result := TextToSingleLine(system.copy(Result,1,DisplayWidth-3)) + '...';
1154
1155 {2: case 2 ignored. This should be handled by TIBWideMemo}
1156
1157 3, {Assume UNICODE_FSS is really UTF8}
1158 4: {Include GB18030 - assuming UTF8 routines work for this codeset}
1159 if DisplayWidth = 0 then
1160 Result := ValidUTF8String(TextToSingleLine(Result))
1161 else
1162 if UTF8Length(Result) > DisplayWidth then {Show truncation with elipses}
1163 Result := ValidUTF8String(TextToSingleLine(UTF8Copy(Result,1,DisplayWidth-3))) + '...';
1164 end;
1165 end
1166 end;
1167
1168 procedure TIBMemoField.Bind(Binding: Boolean);
1169 var IBFieldDef: TIBFieldDef;
1170 begin
1171 inherited Bind(Binding);
1172 if Binding and (FieldDef <> nil) then
1173 begin
1174 IBFieldDef := FieldDef as TIBFieldDef;
1175 CharacterSetSize := IBFieldDef.CharacterSetSize;
1176 CharacterSetName := IBFieldDef.CharacterSetName;
1177 CodePage := IBFieldDef.CodePage;
1178 end;
1179 end;
1180
1181 function TIBMemoField.GetAsString: string;
1182 var s: RawByteString;
1183 begin
1184 s := inherited GetAsString;
1185 SetCodePage(s,CodePage,false);
1186 if (CodePage <> CP_NONE) and (CodePage <> CP_UTF8) then
1187 SetCodePage(s,CP_UTF8,true); {LCL only accepts UTF8}
1188 Result := s;
1189 end;
1190
1191 function TIBMemoField.GetDefaultWidth: Longint;
1192 begin
1193 if DisplayTextAsClassName then
1194 Result := inherited
1195 else
1196 Result := 128;
1197 end;
1198
1199 procedure TIBMemoField.GetText(var AText: string; ADisplayText: Boolean);
1200 begin
1201 if ADisplayText then
1202 begin
1203 if not DisplayTextAsClassName and (CharacterSetName <> '') then
1204 AText := GetTruncatedText
1205 else
1206 inherited GetText(AText, ADisplayText);
1207 end
1208 else
1209 AText := GetAsString;
1210 end;
1211
1212 procedure TIBMemoField.SetAsString(const AValue: string);
1213 var s: RawByteString;
1214 begin
1215 s := AValue;
1216 if StringCodePage(s) <> CodePage then
1217 SetCodePage(s,CodePage,CodePage<>CP_NONE);
1218 inherited SetAsString(s);
1219 end;
1220
1221 constructor TIBMemoField.Create(AOwner: TComponent);
1222 begin
1223 inherited Create(AOwner);
1224 BlobType := ftMemo;
1225 FCodePage := CP_NONE;
1226 end;
1227
1228 { TIBControlLink }
1229
1230 destructor TIBControlLink.Destroy;
1231 begin
1232 IBDataSet := nil;
1233 inherited Destroy;
1234 end;
1235
1236 procedure TIBControlLink.UpdateParams(Sender: TObject);
1237 begin
1238
1239 end;
1240
1241 procedure TIBControlLink.UpdateSQL(Sender: TObject);
1242 begin
1243
1244 end;
1245
1246 procedure TIBControlLink.SetIBDataSet(AValue: TIBCustomDataSet);
1247 begin
1248 if FTIBDataSet = AValue then Exit;
1249 if IBDataSet <> nil then
1250 IBDataSet.UnRegisterIBLink(self);
1251 FTIBDataSet := AValue;
1252 if IBDataSet <> nil then
1253 IBDataSet.RegisterIBLink(self);
1254 end;
1255
1256
1257 { TIBStringField}
1258
1259 procedure TIBStringField.Bind(Binding: Boolean);
1260 var IBFieldDef: TIBFieldDef;
1261 begin
1262 inherited Bind(Binding);
1263 if Binding and (FieldDef <> nil) then
1264 begin
1265 IBFieldDef := FieldDef as TIBFieldDef;
1266 CharacterSetSize := IBFieldDef.CharacterSetSize;
1267 CharacterSetName := IBFieldDef.CharacterSetName;
1268 FDataSize := IBFieldDef.DataSize + 1;
1269 if AutoFieldSize then
1270 Size := IBFieldDef.Size;
1271 CodePage := IBFieldDef.CodePage;
1272 end;
1273 end;
1274
1275 function TIBStringField.GetDataSize: Integer;
1276 begin
1277 Result := FDataSize;
1278 end;
1279
1280 constructor TIBStringField.Create(aOwner: TComponent);
1281 begin
1282 inherited Create(aOwner);
1283 FCharacterSetSize := 1;
1284 FCodePage := CP_NONE;
1285 FAutoFieldSize := true;
1286 end;
1287
1288 class procedure TIBStringField.CheckTypeSize(Value: Integer);
1289 begin
1290 { don't check string size. all sizes valid }
1291 end;
1292
1293 function TIBStringField.GetAsString: string;
1294 begin
1295 if not GetValue(Result) then Result := '';
1296 end;
1297
1298 function TIBStringField.GetAsVariant: Variant;
1299 var
1300 S: string;
1301 begin
1302 if GetValue(S) then Result := S else Result := Null;
1303 end;
1304
1305 function TIBStringField.GetValue(var Value: string): Boolean;
1306 var
1307 Buffer: PChar;
1308 s: RawByteString;
1309 begin
1310 Buffer := nil;
1311 IBAlloc(Buffer, 0, DataSize);
1312 try
1313 Result := GetData(Buffer);
1314 if Result then
1315 begin
1316 s := strpas(Buffer);
1317 SetCodePage(s,CodePage,false);
1318 if (CodePage <> CP_NONE) and (CodePage <> CP_UTF8) then
1319 SetCodePage(s,CP_UTF8,true); {LCL only accepts UTF8}
1320 Value := s;
1321 // writeln(FieldName,': ', StringCodePage(Value),', ',Value);
1322 if Transliterate and (Value <> '') then
1323 DataSet.Translate(PChar(Value), PChar(Value), False);
1324 end
1325 finally
1326 FreeMem(Buffer);
1327 end;
1328 end;
1329
1330 procedure TIBStringField.SetAsString(const Value: string);
1331 var
1332 Buffer: PChar;
1333 s: RawByteString;
1334 begin
1335 Buffer := nil;
1336 IBAlloc(Buffer, 0, DataSize);
1337 try
1338 s := Value;
1339 if StringCodePage(s) <> CodePage then
1340 SetCodePage(s,CodePage,CodePage<>CP_NONE);
1341 StrLCopy(Buffer, PChar(s), DataSize-1);
1342 if Transliterate then
1343 DataSet.Translate(Buffer, Buffer, True);
1344 SetData(Buffer);
1345 finally
1346 FreeMem(Buffer);
1347 end;
1348 end;
1349
1350
1351 { TIBBCDField }
1352
1353 constructor TIBBCDField.Create(AOwner: TComponent);
1354 begin
1355 inherited Create(AOwner);
1356 SetDataType(ftBCD);
1357 Size := 8;
1358 end;
1359
1360 procedure TIBBCDField.Bind(Binding: Boolean);
1361 begin
1362 inherited Bind(Binding);
1363 if Binding and (FieldDef <> nil) then
1364 FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1365 end;
1366
1367 class procedure TIBBCDField.CheckTypeSize(Value: Integer);
1368 begin
1369 { No need to check as the base type is currency, not BCD }
1370 end;
1371
1372 function TIBBCDField.GetAsCurrency: Currency;
1373 begin
1374 if not GetValue(Result) then
1375 Result := 0;
1376 end;
1377
1378 function TIBBCDField.GetAsString: string;
1379 var
1380 C: System.Currency;
1381 begin
1382 if GetValue(C) then
1383 Result := CurrToStr(C)
1384 else
1385 Result := '';
1386 end;
1387
1388 function TIBBCDField.GetAsVariant: Variant;
1389 var
1390 C: System.Currency;
1391 begin
1392 if GetValue(C) then
1393 Result := C
1394 else
1395 Result := Null;
1396 end;
1397
1398 function TIBBCDField.GetDataSize: Integer;
1399 begin
1400 {$IFDEF TBCDFIELD_IS_BCD}
1401 Result := 8;
1402 {$ELSE}
1403 Result := inherited GetDataSize
1404 {$ENDIF}
1405 end;
1406
1407 { TIBDataLink }
1408
1409 constructor TIBDataLink.Create(ADataSet: TIBCustomDataSet);
1410 begin
1411 inherited Create;
1412 FDataSet := ADataSet;
1413 FTimer := TFPTimer.Create(nil);
1414 {$IF FPC_FULLVERSION >= 30002}
1415 FTimer.Enabled := true;
1416 {$IFEND}
1417 FTimer.Interval := 0;
1418 FTimer.OnTimer := HandleRefreshTimer;
1419 FDelayTimerValue := 0;
1420 end;
1421
1422 destructor TIBDataLink.Destroy;
1423 begin
1424 FDataSet.FDataLink := nil;
1425 if assigned(FTimer) then FTimer.Free;
1426 inherited Destroy;
1427 end;
1428
1429 procedure TIBDataLink.HandleRefreshTimer(Sender: TObject);
1430 begin
1431 FTimer.StopTimer;
1432 if FDataSet.Active then
1433 FDataSet.RefreshParams;
1434 end;
1435
1436 procedure TIBDataLink.SetDelayTimerValue(AValue: integer);
1437 begin
1438 if FDelayTimerValue = AValue then Exit;
1439 FDelayTimerValue := AValue;
1440 FTimer.Interval := FDelayTimerValue;
1441 end;
1442
1443 procedure TIBDataLink.ActiveChanged;
1444 begin
1445 if FDataSet.Active then
1446 FDataSet.RefreshParams;
1447 end;
1448
1449
1450 function TIBDataLink.GetDetailDataSet: TDataSet;
1451 begin
1452 Result := FDataSet;
1453 end;
1454
1455 procedure TIBDataLink.RecordChanged(Field: TField);
1456 begin
1457 if (Field = nil) and FDataSet.Active then
1458 begin
1459 {$IF FPC_FULLVERSION >= 30002}
1460 if FDelayTimerValue > 0 then
1461 FTimer.StartTimer
1462 else
1463 {$IFEND}
1464 FDataSet.RefreshParams;
1465 end;
1466 end;
1467
1468 procedure TIBDataLink.CheckBrowseMode;
1469 begin
1470 if FDataSet.Active then
1471 FDataSet.CheckBrowseMode;
1472 end;
1473
1474 { TIBCustomDataSet }
1475
1476 constructor TIBCustomDataSet.Create(AOwner: TComponent);
1477 begin
1478 inherited Create(AOwner);
1479 FBase := TIBBase.Create(Self);
1480 FDatabaseInfo := TIBDatabaseInfo.Create(self);
1481 FIBLinks := TList.Create;
1482 FCurrentRecord := -1;
1483 FDeletedRecords := 0;
1484 FUniDirectional := False;
1485 FBufferChunks := BufferCacheSize;
1486 FBlobStreamList := TList.Create;
1487 FArrayList := TList.Create;
1488 FGeneratorField := TIBGenerator.Create(self);
1489 FDataLink := TIBDataLink.Create(Self);
1490 FQDelete := TIBSQL.Create(Self);
1491 FQDelete.OnSQLChanging := SQLChanging;
1492 FQDelete.GoToFirstRecordOnExecute := False;
1493 FQInsert := TIBSQL.Create(Self);
1494 FQInsert.OnSQLChanging := SQLChanging;
1495 FQInsert.GoToFirstRecordOnExecute := False;
1496 FQRefresh := TIBSQL.Create(Self);
1497 FQRefresh.OnSQLChanging := SQLChanging;
1498 FQRefresh.GoToFirstRecordOnExecute := False;
1499 FQSelect := TIBSQL.Create(Self);
1500 FQSelect.OnSQLChanging := SQLChanging;
1501 FQSelect.OnSQLChanged := SQLChanged;
1502 FQSelect.GoToFirstRecordOnExecute := False;
1503 FQModify := TIBSQL.Create(Self);
1504 FQModify.OnSQLChanging := SQLChanging;
1505 FQModify.GoToFirstRecordOnExecute := False;
1506 FUpdateRecordTypes := [cusUnmodified, cusModified, cusInserted];
1507 FParamCheck := True;
1508 FGenerateParamNames := False;
1509 FForcedRefresh := False;
1510 FAutoCommit:= acDisabled;
1511 FDataSetCloseAction := dcDiscardChanges;
1512 {Bookmark Size is Integer for IBX}
1513 BookmarkSize := SizeOf(Integer);
1514 FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
1515 FBase.AfterDatabaseDisconnect := DoAfterDatabaseDisconnect;
1516 FBase.OnDatabaseFree := DoDatabaseFree;
1517 FBase.BeforeTransactionEnd := DoBeforeTransactionEnd;
1518 FBase.AfterTransactionEnd := DoAfterTransactionEnd;
1519 FBase.OnTransactionFree := DoTransactionFree;
1520 if AOwner is TIBDatabase then
1521 Database := TIBDatabase(AOwner)
1522 else
1523 if AOwner is TIBTransaction then
1524 Transaction := TIBTransaction(AOwner);
1525 FBaseSQLSelect := TStringList.Create;
1526 end;
1527
1528 destructor TIBCustomDataSet.Destroy;
1529 begin
1530 if Active then Active := false;
1531 if assigned(FGeneratorField) then FGeneratorField.Free;
1532 FDataLink.Free;
1533 FBase.Free;
1534 ClearBlobCache;
1535 ClearIBLinks;
1536 FIBLinks.Free;
1537 FBlobStreamList.Free;
1538 FArrayList.Free;
1539 FreeMem(FBufferCache);
1540 FBufferCache := nil;
1541 FreeMem(FOldBufferCache);
1542 FOldBufferCache := nil;
1543 FCacheSize := 0;
1544 FOldCacheSize := 0;
1545 FMappedFieldPosition := nil;
1546 if assigned(FBaseSQLSelect) then FBaseSQLSelect.Free;
1547 if assigned(FParser) then FParser.Free;
1548 inherited Destroy;
1549 end;
1550
1551 function TIBCustomDataSet.AdjustCurrentRecord(Buffer: Pointer; GetMode: TGetMode):
1552 TGetResult;
1553 begin
1554 while not IsVisible(Buffer) do
1555 begin
1556 if GetMode = gmPrior then
1557 begin
1558 Dec(FCurrentRecord);
1559 if FCurrentRecord = -1 then
1560 begin
1561 result := grBOF;
1562 exit;
1563 end;
1564 ReadRecordCache(FCurrentRecord, Buffer, False);
1565 end
1566 else begin
1567 Inc(FCurrentRecord);
1568 if (FCurrentRecord = FRecordCount) then
1569 begin
1570 if (not FQSelect.EOF) and FQSelect.Next then
1571 begin
1572 FetchCurrentRecordToBuffer(FQSelect, FCurrentRecord, Buffer);
1573 Inc(FRecordCount);
1574 end
1575 else begin
1576 result := grEOF;
1577 exit;
1578 end;
1579 end
1580 else
1581 ReadRecordCache(FCurrentRecord, Buffer, False);
1582 end;
1583 end;
1584 result := grOK;
1585 end;
1586
1587 procedure TIBCustomDataSet.ApplyUpdates;
1588 var
1589 CurBookmark: TBookmark;
1590 Buffer: PRecordData;
1591 CurUpdateTypes: TIBUpdateRecordTypes;
1592 UpdateAction: TIBUpdateAction;
1593 UpdateKind: TUpdateKind;
1594 bRecordsSkipped: Boolean;
1595
1596 procedure GetUpdateKind;
1597 begin
1598 case Buffer^.rdCachedUpdateStatus of
1599 cusModified:
1600 UpdateKind := ukModify;
1601 cusInserted:
1602 UpdateKind := ukInsert;
1603 else
1604 UpdateKind := ukDelete;
1605 end;
1606 end;
1607
1608 procedure ResetBufferUpdateStatus;
1609 begin
1610 case Buffer^.rdCachedUpdateStatus of
1611 cusModified:
1612 begin
1613 PRecordData(Buffer)^.rdUpdateStatus := usUnmodified;
1614 PRecordData(Buffer)^.rdCachedUpdateStatus := cusUnmodified;
1615 end;
1616 cusInserted:
1617 begin
1618 PRecordData(Buffer)^.rdUpdateStatus := usUnmodified;
1619 PRecordData(Buffer)^.rdCachedUpdateStatus := cusUnmodified;
1620 end;
1621 cusDeleted:
1622 begin
1623 PRecordData(Buffer)^.rdUpdateStatus := usDeleted;
1624 PRecordData(Buffer)^.rdCachedUpdateStatus := cusUnmodified;
1625 end;
1626 end;
1627 WriteRecordCache(PRecordData(Buffer)^.rdRecordNumber, Pointer(Buffer));
1628 end;
1629
1630 procedure UpdateUsingOnUpdateRecord;
1631 begin
1632 UpdateAction := uaFail;
1633 try
1634 FOnUpdateRecord(Self, UpdateKind, UpdateAction);
1635 except
1636 on E: Exception do
1637 begin
1638 if (E is EDatabaseError) and Assigned(FOnUpdateError) then
1639 FOnUpdateError(Self, EIBError(E), UpdateKind, UpdateAction);
1640 if UpdateAction = uaFail then
1641 raise;
1642 end;
1643 end;
1644 end;
1645
1646 procedure UpdateUsingUpdateObject;
1647 begin
1648 try
1649 FUpdateObject.Apply(UpdateKind,PChar(Buffer));
1650 ResetBufferUpdateStatus;
1651 except
1652 on E: Exception do
1653 if (E is EDatabaseError) and Assigned(FOnUpdateError) then
1654 FOnUpdateError(Self, EIBError(E), UpdateKind, UpdateAction);
1655 end;
1656 end;
1657
1658 procedure UpdateUsingInternalquery;
1659 begin
1660 try
1661 case Buffer^.rdCachedUpdateStatus of
1662 cusModified:
1663 InternalPostRecord(FQModify, Buffer);
1664 cusInserted:
1665 InternalPostRecord(FQInsert, Buffer);
1666 cusDeleted:
1667 InternalDeleteRecord(FQDelete, Buffer);
1668 end;
1669 except
1670 on E: EIBError do begin
1671 UpdateAction := uaFail;
1672 if Assigned(FOnUpdateError) then
1673 FOnUpdateError(Self, E, UpdateKind, UpdateAction);
1674 case UpdateAction of
1675 uaFail: raise;
1676 uaAbort: SysUtils.Abort;
1677 uaSkip: bRecordsSkipped := True;
1678 end;
1679 end;
1680 end;
1681 end;
1682
1683 begin
1684 if State in [dsEdit, dsInsert] then
1685 Post;
1686 FBase.CheckDatabase;
1687 FBase.CheckTransaction;
1688 DisableControls;
1689 CurBookmark := Bookmark;
1690 CurUpdateTypes := FUpdateRecordTypes;
1691 FUpdateRecordTypes := [cusModified, cusInserted, cusDeleted];
1692 try
1693 First;
1694 bRecordsSkipped := False;
1695 while not EOF do
1696 begin
1697 Buffer := PRecordData(GetActiveBuf);
1698 GetUpdateKind;
1699 UpdateAction := uaApply;
1700 if Assigned(FUpdateObject) or Assigned(FOnUpdateRecord) then
1701 begin
1702 if (Assigned(FOnUpdateRecord)) then
1703 UpdateUsingOnUpdateRecord
1704 else
1705 if Assigned(FUpdateObject) then
1706 UpdateUsingUpdateObject;
1707 case UpdateAction of
1708 uaFail:
1709 IBError(ibxeUserAbort, [nil]);
1710 uaAbort:
1711 SysUtils.Abort;
1712 uaApplied:
1713 ResetBufferUpdateStatus;
1714 uaSkip:
1715 bRecordsSkipped := True;
1716 uaRetry:
1717 Continue;
1718 end;
1719 end;
1720 if (not Assigned(FUpdateObject)) and (UpdateAction = UaApply) then
1721 begin
1722 UpdateUsingInternalquery;
1723 UpdateAction := uaApplied;
1724 end;
1725 Next;
1726 end;
1727 FUpdatesPending := bRecordsSkipped;
1728 finally
1729 FUpdateRecordTypes := CurUpdateTypes;
1730 Bookmark := CurBookmark;
1731 EnableControls;
1732 end;
1733 end;
1734
1735 procedure TIBCustomDataSet.InternalBatchInput(InputObject: TIBBatchInput);
1736 begin
1737 FQSelect.BatchInput(InputObject);
1738 end;
1739
1740 procedure TIBCustomDataSet.InternalBatchOutput(OutputObject: TIBBatchOutput);
1741 var
1742 Qry: TIBSQL;
1743 begin
1744 Qry := TIBSQL.Create(Self);
1745 try
1746 Qry.Database := FBase.Database;
1747 Qry.Transaction := FBase.Transaction;
1748 Qry.SQL.Assign(FQSelect.SQL);
1749 Qry.BatchOutput(OutputObject);
1750 finally
1751 Qry.Free;
1752 end;
1753 end;
1754
1755 procedure TIBCustomDataSet.CancelUpdates;
1756 var
1757 CurUpdateTypes: TIBUpdateRecordTypes;
1758 begin
1759 if State in [dsEdit, dsInsert] then
1760 Post;
1761 if FCachedUpdates and FUpdatesPending then
1762 begin
1763 DisableControls;
1764 CurUpdateTypes := UpdateRecordTypes;
1765 UpdateRecordTypes := [cusModified, cusInserted, cusDeleted];
1766 try
1767 First;
1768 while not EOF do
1769 begin
1770 if UpdateStatus = usInserted then
1771 RevertRecord
1772 else
1773 begin
1774 RevertRecord;
1775 Next;
1776 end;
1777 end;
1778 finally
1779 UpdateRecordTypes := CurUpdateTypes;
1780 First;
1781 FUpdatesPending := False;
1782 EnableControls;
1783 end;
1784 end;
1785 end;
1786
1787 function TIBCustomDataSet.GetFieldPosition(AliasName: string): integer;
1788 var i: integer;
1789 Prepared: boolean;
1790 begin
1791 Result := 0;
1792 Prepared := FInternalPrepared;
1793 if not Prepared then
1794 InternalPrepare;
1795 try
1796 for i := 0 to Length(FAliasNameList) - 1 do
1797 if FAliasNameList[i] = AliasName then
1798 begin
1799 Result := i + 1;
1800 Exit
1801 end;
1802 finally
1803 if not Prepared then
1804 InternalUnPrepare;
1805 end;
1806 end;
1807
1808 procedure TIBCustomDataSet.ActivateConnection;
1809 begin
1810 if not Assigned(Database) then
1811 IBError(ibxeDatabaseNotAssigned, [nil]);
1812 if not Assigned(Transaction) then
1813 IBError(ibxeTransactionNotAssigned, [nil]);
1814 if not Database.Connected then Database.Open;
1815 end;
1816
1817 function TIBCustomDataSet.ActivateTransaction: Boolean;
1818 begin
1819 Result := False;
1820 if AllowAutoActivateTransaction or (csDesigning in ComponentState) then
1821 begin
1822 if not Assigned(Transaction) then
1823 IBError(ibxeTransactionNotAssigned, [nil]);
1824 if not Transaction.Active then
1825 begin
1826 Result := True;
1827 Transaction.StartTransaction;
1828 FDidActivate := True;
1829 end;
1830 end;
1831 end;
1832
1833 procedure TIBCustomDataSet.DeactivateTransaction;
1834 var
1835 i: Integer;
1836 begin
1837 if not Assigned(Transaction) then
1838 IBError(ibxeTransactionNotAssigned, [nil]);
1839 with Transaction do
1840 begin
1841 for i := 0 to SQLObjectCount - 1 do
1842 begin
1843 if (SQLObjects[i] <> nil) and ((SQLObjects[i]).owner is TDataSet) then
1844 begin
1845 if TDataSet(SQLObjects[i].owner).Active then
1846 begin
1847 FDidActivate := False;
1848 exit;
1849 end;
1850 end;
1851 end;
1852 end;
1853 FInternalPrepared := False;
1854 if Transaction.InTransaction then
1855 Transaction.Commit;
1856 FDidActivate := False;
1857 end;
1858
1859 procedure TIBCustomDataSet.CheckDatasetClosed;
1860 begin
1861 if FOpen then
1862 IBError(ibxeDatasetOpen, [nil]);
1863 end;
1864
1865 procedure TIBCustomDataSet.CheckDatasetOpen;
1866 begin
1867 if not FOpen then
1868 IBError(ibxeDatasetClosed, [nil]);
1869 end;
1870
1871 function TIBCustomDataSet.CreateParser: TSelectSQLParser;
1872 begin
1873 Result := TSelectSQLParser.Create(self,FBaseSQLSelect);
1874 Result.OnSQLChanging := SQLChanging
1875 end;
1876
1877 procedure TIBCustomDataSet.CheckNotUniDirectional;
1878 begin
1879 if UniDirectional then
1880 IBError(ibxeDataSetUniDirectional, [nil]);
1881 end;
1882
1883 procedure TIBCustomDataSet.AdjustRecordOnInsert(Buffer: Pointer);
1884 begin
1885 with PRecordData(Buffer)^ do
1886 if (State = dsInsert) and (not Modified) then
1887 begin
1888 rdRecordNumber := FRecordCount;
1889 FCurrentRecord := FRecordCount;
1890 end;
1891 end;
1892
1893 function TIBCustomDataSet.CanEdit: Boolean;
1894 var
1895 Buff: PRecordData;
1896 begin
1897 Buff := PRecordData(GetActiveBuf);
1898 result := (FQModify.SQL.Text <> '') or
1899 (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukModify).Text <> '')) or
1900 ((Buff <> nil) and (Buff^.rdCachedUpdateStatus = cusInserted) and
1901 (FCachedUpdates));
1902 end;
1903
1904 function TIBCustomDataSet.CanInsert: Boolean;
1905 begin
1906 result := (FQInsert.SQL.Text <> '') or
1907 (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukInsert).Text <> ''));
1908 end;
1909
1910 function TIBCustomDataSet.CanDelete: Boolean;
1911 begin
1912 if (FQDelete.SQL.Text <> '') or
1913 (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukDelete).Text <> '')) then
1914 result := True
1915 else
1916 result := False;
1917 end;
1918
1919 function TIBCustomDataSet.CanRefresh: Boolean;
1920 begin
1921 result := (FQRefresh.SQL.Text <> '') or
1922 (Assigned(FUpdateObject) and (FUpdateObject.RefreshSQL.Text <> ''));
1923 end;
1924
1925 procedure TIBCustomDataSet.CheckEditState;
1926 begin
1927 case State of
1928 { Check all the wsEditMode types }
1929 dsEdit, dsInsert, dsSetKey, dsCalcFields, dsFilter,
1930 dsNewValue, dsInternalCalc :
1931 begin
1932 if (State in [dsEdit]) and (not CanEdit) then
1933 IBError(ibxeCannotUpdate, [nil]);
1934 if (State in [dsInsert]) and (not CanInsert) then
1935 IBError(ibxeCannotInsert, [nil]);
1936 end;
1937 else
1938 IBError(ibxeNotEditing, [])
1939 end;
1940 end;
1941
1942 procedure TIBCustomDataSet.ClearBlobCache;
1943 var
1944 i: Integer;
1945 begin
1946 for i := 0 to FBlobStreamList.Count - 1 do
1947 begin
1948 TIBBlobStream(FBlobStreamList[i]).Free;
1949 FBlobStreamList[i] := nil;
1950 end;
1951 FBlobStreamList.Pack;
1952 end;
1953
1954 procedure TIBCustomDataSet.ClearArrayCache;
1955 var
1956 i: Integer;
1957 begin
1958 for i := 0 to FArrayList.Count - 1 do
1959 begin
1960 TIBArray(FArrayList[i]).Free;
1961 FArrayList[i] := nil;
1962 end;
1963 FArrayList.Pack;
1964 end;
1965
1966 procedure TIBCustomDataSet.CopyRecordBuffer(Source, Dest: Pointer);
1967 begin
1968 Move(Source^, Dest^, FRecordBufferSize);
1969 end;
1970
1971 procedure TIBCustomDataSet.DoBeforeDatabaseDisconnect(Sender: TObject);
1972 begin
1973 if Active then
1974 Active := False;
1975 InternalUnPrepare;
1976 if Assigned(FBeforeDatabaseDisconnect) then
1977 FBeforeDatabaseDisconnect(Sender);
1978 end;
1979
1980 procedure TIBCustomDataSet.DoAfterDatabaseDisconnect(Sender: TObject);
1981 begin
1982 if Assigned(FAfterDatabaseDisconnect) then
1983 FAfterDatabaseDisconnect(Sender);
1984 end;
1985
1986 procedure TIBCustomDataSet.DoDatabaseFree(Sender: TObject);
1987 begin
1988 if Assigned(FDatabaseFree) then
1989 FDatabaseFree(Sender);
1990 end;
1991
1992 procedure TIBCustomDataSet.DoBeforeTransactionEnd(Sender: TObject;
1993 Action: TTransactionAction);
1994 begin
1995 FCloseAction := Action;
1996 FInTransactionEnd := true;
1997 try
1998 if Active then
1999 Active := False;
2000 finally
2001 FInTransactionEnd := false;
2002 end;
2003 if FQSelect <> nil then
2004 FQSelect.FreeHandle;
2005 if FQDelete <> nil then
2006 FQDelete.FreeHandle;
2007 if FQInsert <> nil then
2008 FQInsert.FreeHandle;
2009 if FQModify <> nil then
2010 FQModify.FreeHandle;
2011 if FQRefresh <> nil then
2012 FQRefresh.FreeHandle;
2013 InternalUnPrepare;
2014 if Assigned(FBeforeTransactionEnd) then
2015 FBeforeTransactionEnd(Sender);
2016 end;
2017
2018 procedure TIBCustomDataSet.DoAfterTransactionEnd(Sender: TObject);
2019 begin
2020 if Assigned(FAfterTransactionEnd) then
2021 FAfterTransactionEnd(Sender);
2022 end;
2023
2024 procedure TIBCustomDataSet.DoTransactionFree(Sender: TObject);
2025 begin
2026 if Assigned(FTransactionFree) then
2027 FTransactionFree(Sender);
2028 end;
2029
2030 procedure TIBCustomDataSet.DoDeleteReturning(QryResults: IResults);
2031 begin
2032 if assigned(FOnDeleteReturning) then
2033 OnDeleteReturning(self,QryResults);
2034 end;
2035
2036 procedure TIBCustomDataSet.InitModelBuffer(Qry: TIBSQL; Buffer: PChar);
2037 var i, j: Integer;
2038 FieldsLoaded: integer;
2039 p: PRecordData;
2040 colMetadata: IColumnMetaData;
2041 begin
2042 p := PRecordData(Buffer);
2043 { Get record information }
2044 p^.rdBookmarkFlag := bfCurrent;
2045 p^.rdFieldCount := Qry.FieldCount;
2046 p^.rdRecordNumber := -1;
2047 p^.rdUpdateStatus := usUnmodified;
2048 p^.rdCachedUpdateStatus := cusUnmodified;
2049 p^.rdSavedOffset := $FFFFFFFF;
2050
2051 { Load up the fields }
2052 FieldsLoaded := FQSelect.MetaData.Count;
2053 j := 1;
2054 for i := 0 to Qry.MetaData.Count - 1 do
2055 begin
2056 if (Qry = FQSelect) then
2057 j := i + 1
2058 else
2059 begin
2060 if FieldsLoaded = 0 then
2061 break;
2062 j := FQSelect.FieldIndex[Qry[i].Name] + 1;
2063 if j < 1 then
2064 continue
2065 else
2066 Dec(FieldsLoaded);
2067 end;
2068 if j > 0 then
2069 begin
2070 colMetadata := Qry.MetaData[i];
2071 with p^.rdFields[j], FFieldColumns^[j] do
2072 begin
2073 fdDataType := colMetadata.GetSQLType;
2074 if fdDataType = SQL_BLOB then
2075 fdDataScale := 0
2076 else
2077 fdDataScale := colMetadata.getScale;
2078 fdNullable := colMetadata.getIsNullable;
2079 fdIsNull := true;
2080 fdDataSize := colMetadata.GetSize;
2081 fdDataLength := 0;
2082 fdCodePage := CP_NONE;
2083
2084 case fdDataType of
2085 SQL_TIMESTAMP,
2086 SQL_TYPE_DATE,
2087 SQL_TYPE_TIME:
2088 fdDataSize := SizeOf(TDateTime);
2089 SQL_SHORT, SQL_LONG:
2090 begin
2091 if (fdDataScale = 0) then
2092 fdDataSize := SizeOf(Integer)
2093 else
2094 if (fdDataScale >= (-4)) then
2095 fdDataSize := SizeOf(Currency)
2096 else
2097 fdDataSize := SizeOf(Double);
2098 end;
2099 SQL_INT64:
2100 begin
2101 if (fdDataScale = 0) then
2102 fdDataSize := SizeOf(Int64)
2103 else
2104 if (fdDataScale >= (-4)) then
2105 fdDataSize := SizeOf(Currency)
2106 else
2107 fdDataSize := SizeOf(Double);
2108 end;
2109 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2110 fdDataSize := SizeOf(Double);
2111 SQL_BOOLEAN:
2112 fdDataSize := SizeOf(wordBool);
2113 SQL_VARYING,
2114 SQL_TEXT,
2115 SQL_BLOB:
2116 fdCodePage := Qry.Metadata[i].getCodePage;
2117 end;
2118 fdDataOfs := FRecordSize;
2119 Inc(FRecordSize, fdDataSize);
2120 end;
2121 end;
2122 end;
2123 end;
2124
2125 {Update Buffer Fields from Query Results}
2126
2127 procedure TIBCustomDataSet.UpdateRecordFromQuery(QryResults: IResults;
2128 Buffer: PChar);
2129 var i, j: integer;
2130 begin
2131 for i := 0 to QryResults.Count - 1 do
2132 begin
2133 j := GetFieldPosition(QryResults[i].GetAliasName);
2134 if j > 0 then
2135 begin
2136 ColumnDataToBuffer(QryResults,i,j,Buffer);
2137 FBufferUpdatedOnQryReturn := true;
2138 end;
2139 end;
2140 end;
2141
2142
2143 {Move column data returned from query to row buffer}
2144
2145 procedure TIBCustomDataSet.ColumnDataToBuffer(QryResults: IResults;
2146 ColumnIndex, FieldIndex: integer; Buffer: PChar);
2147 var
2148 LocalData: PByte;
2149 LocalDate: TDateTime;
2150 LocalDouble: Double;
2151 LocalInt: Integer;
2152 LocalBool: wordBool;
2153 LocalInt64: Int64;
2154 LocalCurrency: Currency;
2155 ColData: ISQLData;
2156 begin
2157 LocalData := nil;
2158 with PRecordData(Buffer)^.rdFields[FieldIndex], FFieldColumns^[FieldIndex] do
2159 begin
2160 QryResults.GetData(ColumnIndex,fdIsNull,fdDataLength,LocalData);
2161 if not fdIsNull then
2162 begin
2163 ColData := QryResults[ColumnIndex];
2164 case fdDataType of {Get Formatted data for column types that need formatting}
2165 SQL_TYPE_DATE,
2166 SQL_TYPE_TIME,
2167 SQL_TIMESTAMP:
2168 begin
2169 {This is an IBX native format and not the TDataset approach. See also GetFieldData}
2170 LocalDate := ColData.AsDateTime;
2171 LocalData := PByte(@LocalDate);
2172 end;
2173 SQL_SHORT, SQL_LONG:
2174 begin
2175 if (fdDataScale = 0) then
2176 begin
2177 LocalInt := ColData.AsLong;
2178 LocalData := PByte(@LocalInt);
2179 end
2180 else
2181 if (fdDataScale >= (-4)) then
2182 begin
2183 LocalCurrency := ColData.AsCurrency;
2184 LocalData := PByte(@LocalCurrency);
2185 end
2186 else
2187 begin
2188 LocalDouble := ColData.AsDouble;
2189 LocalData := PByte(@LocalDouble);
2190 end;
2191 end;
2192 SQL_INT64:
2193 begin
2194 if (fdDataScale = 0) then
2195 begin
2196 LocalInt64 := ColData.AsInt64;
2197 LocalData := PByte(@LocalInt64);
2198 end
2199 else
2200 if (fdDataScale >= (-4)) then
2201 begin
2202 LocalCurrency := ColData.AsCurrency;
2203 LocalData := PByte(@LocalCurrency);
2204 end
2205 else
2206 begin
2207 LocalDouble := ColData.AsDouble;
2208 LocalData := PByte(@LocalDouble);
2209 end
2210 end;
2211 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2212 begin
2213 LocalDouble := ColData.AsDouble;
2214 LocalData := PByte(@LocalDouble);
2215 end;
2216 SQL_BOOLEAN:
2217 begin
2218 LocalBool := ColData.AsBoolean;
2219 LocalData := PByte(@LocalBool);
2220 end;
2221 end;
2222
2223 if fdDataType = SQL_VARYING then
2224 Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2225 else
2226 Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2227 end
2228 else {Null column}
2229 if fdDataType = SQL_VARYING then
2230 FillChar(Buffer[fdDataOfs],fdDataLength,0)
2231 else
2232 FillChar(Buffer[fdDataOfs],fdDataSize,0);
2233 end;
2234 end;
2235
2236 function TIBCustomDataSet.GetMasterDetailDelay: integer;
2237 begin
2238 Result := FDataLink.DelayTimerValue;
2239 end;
2240
2241 { Read the record from FQSelect.Current into the record buffer
2242 Then write the buffer to in memory cache }
2243 procedure TIBCustomDataSet.FetchCurrentRecordToBuffer(Qry: TIBSQL;
2244 RecordNumber: Integer; Buffer: PChar);
2245 var
2246 pbd: PBlobDataArray;
2247 pda: PArrayDataArray;
2248 i, j: Integer;
2249 FieldsLoaded: Integer;
2250 p: PRecordData;
2251 begin
2252 if RecordNumber = -1 then
2253 begin
2254 InitModelBuffer(Qry,Buffer);
2255 Exit;
2256 end;
2257 p := PRecordData(Buffer);
2258 { Make sure blob cache is empty }
2259 pbd := PBlobDataArray(Buffer + FBlobCacheOffset);
2260 pda := PArrayDataArray(Buffer + FArrayCacheOffset);
2261 for i := 0 to BlobFieldCount - 1 do
2262 pbd^[i] := nil;
2263 for i := 0 to ArrayFieldCount - 1 do
2264 pda^[i] := nil;
2265
2266 { Get record information }
2267 p^.rdBookmarkFlag := bfCurrent;
2268 p^.rdFieldCount := Qry.FieldCount;
2269 p^.rdRecordNumber := RecordNumber;
2270 p^.rdUpdateStatus := usUnmodified;
2271 p^.rdCachedUpdateStatus := cusUnmodified;
2272 p^.rdSavedOffset := $FFFFFFFF;
2273
2274 { Load up the fields }
2275 FieldsLoaded := FQSelect.MetaData.Count;
2276 j := 1;
2277 for i := 0 to Qry.FieldCount - 1 do
2278 begin
2279 if (Qry = FQSelect) then
2280 j := i + 1
2281 else
2282 begin
2283 if FieldsLoaded = 0 then
2284 break;
2285 j := FQSelect.FieldIndex[Qry[i].Name] + 1;
2286 if j < 1 then
2287 continue
2288 else
2289 Dec(FieldsLoaded);
2290 end;
2291 with FQSelect.MetaData[j - 1] do
2292 if GetAliasname = 'IBX_INTERNAL_DBKEY' then {do not localize}
2293 begin
2294 if (GetSize <= 8) then
2295 p^.rdDBKey := PIBDBKEY(Qry[i].AsPointer)^;
2296 continue;
2297 end;
2298 if j > 0 then
2299 ColumnDataToBuffer(Qry.Current,i,j,Buffer);
2300 end;
2301 WriteRecordCache(RecordNumber, Buffer);
2302 end;
2303
2304 function TIBCustomDataSet.GetActiveBuf: PChar;
2305 begin
2306 case State of
2307 dsBrowse:
2308 if IsEmpty then
2309 result := nil
2310 else
2311 result := ActiveBuffer;
2312 dsEdit, dsInsert:
2313 result := ActiveBuffer;
2314 dsCalcFields:
2315 result := CalcBuffer;
2316 dsFilter:
2317 result := FFilterBuffer;
2318 dsNewValue:
2319 result := ActiveBuffer;
2320 dsOldValue:
2321 if (PRecordData(ActiveBuffer)^.rdRecordNumber =
2322 PRecordData(FOldBuffer)^.rdRecordNumber) then
2323 result := FOldBuffer
2324 else
2325 result := ActiveBuffer;
2326 else if not FOpen then
2327 result := nil
2328 else
2329 result := ActiveBuffer;
2330 end;
2331 end;
2332
2333 function TIBCustomDataSet.CachedUpdateStatus: TCachedUpdateStatus;
2334 begin
2335 if Active then
2336 result := PRecordData(GetActiveBuf)^.rdCachedUpdateStatus
2337 else
2338 result := cusUnmodified;
2339 end;
2340
2341 function TIBCustomDataSet.GetDatabase: TIBDatabase;
2342 begin
2343 result := FBase.Database;
2344 end;
2345
2346 function TIBCustomDataSet.GetDeleteSQL: TStrings;
2347 begin
2348 result := FQDelete.SQL;
2349 end;
2350
2351 function TIBCustomDataSet.GetInsertSQL: TStrings;
2352 begin
2353 result := FQInsert.SQL;
2354 end;
2355
2356 function TIBCustomDataSet.GetSQLParams: ISQLParams;
2357 begin
2358 if not FInternalPrepared then
2359 InternalPrepare;
2360 result := FQSelect.Params;
2361 end;
2362
2363 function TIBCustomDataSet.GetRefreshSQL: TStrings;
2364 begin
2365 result := FQRefresh.SQL;
2366 end;
2367
2368 function TIBCustomDataSet.GetSelectSQL: TStrings;
2369 begin
2370 result := FQSelect.SQL;
2371 end;
2372
2373 function TIBCustomDataSet.GetStatementType: TIBSQLStatementTypes;
2374 begin
2375 result := FQSelect.SQLStatementType;
2376 end;
2377
2378 function TIBCustomDataSet.GetModifySQL: TStrings;
2379 begin
2380 result := FQModify.SQL;
2381 end;
2382
2383 function TIBCustomDataSet.GetTransaction: TIBTransaction;
2384 begin
2385 result := FBase.Transaction;
2386 end;
2387
2388 procedure TIBCustomDataSet.InternalDeleteRecord(Qry: TIBSQL; Buff: Pointer);
2389 begin
2390 if (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukDelete).Text <> '')) then
2391 FUpdateObject.Apply(ukDelete,Buff)
2392 else
2393 begin
2394 SetInternalSQLParams(FQDelete.Params, Buff);
2395 FQDelete.ExecQuery;
2396 if (FQDelete.FieldCount > 0) then
2397 DoDeleteReturning(FQDelete.Current);
2398 end;
2399 with PRecordData(Buff)^ do
2400 begin
2401 rdUpdateStatus := usDeleted;
2402 rdCachedUpdateStatus := cusUnmodified;
2403 end;
2404 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2405 end;
2406
2407 function TIBCustomDataSet.InternalLocate(const KeyFields: string;
2408 const KeyValues: Variant; Options: TLocateOptions): Boolean;
2409 var
2410 keyFieldList: TList;
2411 CurBookmark: TBookmark;
2412 fieldValue: Variant;
2413 lookupValues: array of variant;
2414 i, fieldCount: Integer;
2415 fieldValueAsString: string;
2416 lookupValueAsString: string;
2417 begin
2418 keyFieldList := TList.Create;
2419 try
2420 GetFieldList(keyFieldList, KeyFields);
2421 fieldCount := keyFieldList.Count;
2422 CurBookmark := Bookmark;
2423 result := false;
2424 SetLength(lookupValues, fieldCount);
2425 if not EOF then
2426 begin
2427 for i := 0 to fieldCount - 1 do {expand key values into lookupValues array}
2428 begin
2429 if VarIsArray(KeyValues) then
2430 lookupValues[i] := KeyValues[i]
2431 else
2432 if i > 0 then
2433 lookupValues[i] := NULL
2434 else
2435 lookupValues[0] := KeyValues;
2436
2437 {convert to upper case is case insensitive search}
2438 if (TField(keyFieldList[i]).DataType = ftString) and
2439 not VarIsNull(lookupValues[i]) and (loCaseInsensitive in Options) then
2440 lookupValues[i] := UpperCase(lookupValues[i]);
2441 end;
2442 end;
2443 while not result and not EOF do {search for a matching record}
2444 begin
2445 i := 0;
2446 result := true;
2447 while result and (i < fieldCount) do
2448 {see if all of the key fields matches}
2449 begin
2450 fieldValue := TField(keyFieldList[i]).Value;
2451 result := not (VarIsNull(fieldValue) xor VarIsNull(lookupValues[i]));
2452 if result and not VarIsNull(fieldValue) then
2453 begin
2454 try
2455 if TField(keyFieldList[i]).DataType = ftString then
2456 begin
2457 {strings need special handling because of the locate options that
2458 apply to them}
2459 fieldValueAsString := TField(keyFieldList[i]).AsString;
2460 lookupValueAsString := lookupValues[i];
2461 if (loCaseInsensitive in Options) then
2462 fieldValueAsString := UpperCase(fieldValueAsString);
2463
2464 if (loPartialKey in Options) then
2465 result := result and (Pos(lookupValueAsString, fieldValueAsString) = 1)
2466 else
2467 result := result and (fieldValueAsString = lookupValueAsString);
2468 end
2469 else
2470 result := result and (lookupValues[i] =
2471 VarAsType(fieldValue, VarType(lookupValues[i])));
2472 except on EVariantError do
2473 result := False;
2474 end;
2475 end;
2476 Inc(i);
2477 end;
2478 if not result then
2479 Next;
2480 end;
2481 if not result then
2482 Bookmark := CurBookmark
2483 else
2484 CursorPosChanged;
2485 finally
2486 keyFieldList.Free;
2487 SetLength(lookupValues,0)
2488 end;
2489 end;
2490
2491 procedure TIBCustomDataSet.InternalPostRecord(Qry: TIBSQL; Buff: Pointer);
2492 var
2493 i, j, k, arr: Integer;
2494 pbd: PBlobDataArray;
2495 pda: PArrayDataArray;
2496 begin
2497 pbd := PBlobDataArray(PChar(Buff) + FBlobCacheOffset);
2498 pda := PArrayDataArray(PChar(Buff) + FArrayCacheOffset);
2499 j := 0; arr := 0;
2500 for i := 0 to FieldCount - 1 do
2501 if Fields[i].IsBlob then
2502 begin
2503 k := FMappedFieldPosition[Fields[i].FieldNo -1];
2504 if pbd^[j] <> nil then
2505 begin
2506 pbd^[j].Finalize;
2507 PISC_QUAD(
2508 PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ :=
2509 pbd^[j].BlobID;
2510 PRecordData(Buff)^.rdFields[k].fdIsNull := pbd^[j].Size = 0;
2511 end
2512 else
2513 begin
2514 PRecordData(Buff)^.rdFields[k].fdIsNull := true;
2515 with PISC_QUAD(PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ do
2516 begin
2517 gds_quad_high := 0;
2518 gds_quad_low := 0;
2519 end;
2520 end;
2521 Inc(j);
2522 end
2523 else
2524 if Fields[i] is TIBArrayField then
2525 begin
2526 if pda^[arr] <> nil then
2527 begin
2528 k := FMappedFieldPosition[Fields[i].FieldNo -1];
2529 PISC_QUAD(
2530 PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ := pda^[arr].ArrayIntf.GetArrayID;
2531 PRecordData(Buff)^.rdFields[k].fdIsNull := pda^[arr].ArrayIntf.IsEmpty;
2532 end;
2533 Inc(arr);
2534 end;
2535 FBufferUpdatedOnQryReturn := false;
2536 if Assigned(FUpdateObject) then
2537 begin
2538 if (Qry = FQDelete) then
2539 FUpdateObject.Apply(ukDelete,Buff)
2540 else if (Qry = FQInsert) then
2541 FUpdateObject.Apply(ukInsert,Buff)
2542 else
2543 FUpdateObject.Apply(ukModify,Buff);
2544 FUpdateObject.GetRowsAffected(FSelectCount, FInsertCount, FUpdateCount, FDeleteCount);
2545 end
2546 else begin
2547 SetInternalSQLParams(Qry.Params, Buff);
2548 Qry.ExecQuery;
2549 Qry.Statement.GetRowsAffected(FSelectCount, FInsertCount, FUpdateCount, FDeleteCount);
2550 if Qry.FieldCount > 0 then {Has RETURNING Clause}
2551 UpdateRecordFromQuery(Qry.Current,Buff);
2552 end;
2553 PRecordData(Buff)^.rdUpdateStatus := usUnmodified;
2554 PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
2555 SetModified(False);
2556 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2557 if (FForcedRefresh or (FNeedsRefresh and not FBufferUpdatedOnQryReturn)) and CanRefresh then
2558 InternalRefreshRow;
2559 end;
2560
2561 procedure TIBCustomDataSet.InternalRefreshRow;
2562 var
2563 Buff: PChar;
2564 ofs: DWORD;
2565 Qry: TIBSQL;
2566 begin
2567 FBase.SetCursor;
2568 try
2569 Buff := GetActiveBuf;
2570 if CanRefresh then
2571 begin
2572 if Buff <> nil then
2573 begin
2574 if (Assigned(FUpdateObject) and (FUpdateObject.RefreshSQL.Text <> '')) then
2575 begin
2576 Qry := TIBSQL.Create(self);
2577 Qry.Database := Database;
2578 Qry.Transaction := Transaction;
2579 Qry.GoToFirstRecordOnExecute := False;
2580 Qry.SQL.Text := FUpdateObject.RefreshSQL.Text;
2581 end
2582 else
2583 Qry := FQRefresh;
2584 SetInternalSQLParams(Qry.Params, Buff);
2585 Qry.ExecQuery;
2586 try
2587 if (Qry.SQLStatementType = SQLExecProcedure) or Qry.Next then
2588 begin
2589 ofs := PRecordData(Buff)^.rdSavedOffset;
2590 FetchCurrentRecordToBuffer(Qry,
2591 PRecordData(Buff)^.rdRecordNumber,
2592 Buff);
2593 if FCachedUpdates and (ofs <> $FFFFFFFF) then
2594 begin
2595 PRecordData(Buff)^.rdSavedOffset := ofs;
2596 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2597 SaveOldBuffer(Buff);
2598 end;
2599 end;
2600 finally
2601 Qry.Close;
2602 end;
2603 if Qry <> FQRefresh then
2604 Qry.Free;
2605 end
2606 end
2607 else
2608 IBError(ibxeCannotRefresh, [nil]);
2609 finally
2610 FBase.RestoreCursor;
2611 end;
2612 end;
2613
2614 procedure TIBCustomDataSet.InternalRevertRecord(RecordNumber: Integer);
2615 var
2616 NewBuffer, OldBuffer: PRecordData;
2617
2618 begin
2619 NewBuffer := nil;
2620 OldBuffer := nil;
2621 NewBuffer := PRecordData(AllocRecordBuffer);
2622 OldBuffer := PRecordData(AllocRecordBuffer);
2623 try
2624 ReadRecordCache(RecordNumber, PChar(NewBuffer), False);
2625 ReadRecordCache(RecordNumber, PChar(OldBuffer), True);
2626 case NewBuffer^.rdCachedUpdateStatus of
2627 cusInserted:
2628 begin
2629 NewBuffer^.rdCachedUpdateStatus := cusUninserted;
2630 Inc(FDeletedRecords);
2631 end;
2632 cusModified,
2633 cusDeleted:
2634 begin
2635 if (NewBuffer^.rdCachedUpdateStatus = cusDeleted) then
2636 Dec(FDeletedRecords);
2637 CopyRecordBuffer(OldBuffer, NewBuffer);
2638 end;
2639 end;
2640
2641 if State in dsEditModes then
2642 Cancel;
2643
2644 WriteRecordCache(RecordNumber, PChar(NewBuffer));
2645
2646 if (NewBuffer^.rdCachedUpdateStatus = cusUninserted ) then
2647 ReSync([]);
2648 finally
2649 FreeRecordBuffer(PChar(NewBuffer));
2650 FreeRecordBuffer(PChar(OldBuffer));
2651 end;
2652 end;
2653
2654 { A visible record is one that is not truly deleted,
2655 and it is also listed in the FUpdateRecordTypes set }
2656
2657 function TIBCustomDataSet.IsVisible(Buffer: PChar): Boolean;
2658 begin
2659 result := True;
2660 if not (State = dsOldValue) then
2661 result :=
2662 (PRecordData(Buffer)^.rdCachedUpdateStatus in FUpdateRecordTypes) and
2663 (not ((PRecordData(Buffer)^.rdCachedUpdateStatus = cusUnmodified) and
2664 (PRecordData(Buffer)^.rdUpdateStatus = usDeleted)));
2665 end;
2666
2667
2668 function TIBCustomDataSet.LocateNext(const KeyFields: string;
2669 const KeyValues: Variant; Options: TLocateOptions): Boolean;
2670 begin
2671 DisableControls;
2672 try
2673 result := InternalLocate(KeyFields, KeyValues, Options);
2674 finally
2675 EnableControls;
2676 end;
2677 end;
2678
2679 procedure TIBCustomDataSet.InternalPrepare;
2680 begin
2681 if FInternalPrepared then
2682 Exit;
2683 FBase.SetCursor;
2684 try
2685 ActivateConnection;
2686 ActivateTransaction;
2687 FBase.CheckDatabase;
2688 FBase.CheckTransaction;
2689 if HasParser and (FParser.SQLText <> FQSelect.SQL.Text) then
2690 begin
2691 FQSelect.OnSQLChanged := nil; {Do not react to change}
2692 try
2693 FQSelect.SQL.Text := FParser.SQLText;
2694 finally
2695 FQSelect.OnSQLChanged := SQLChanged;
2696 end;
2697 end;
2698 // writeln( FQSelect.SQL.Text);
2699 if FQSelect.SQL.Text <> '' then
2700 begin
2701 if not FQSelect.Prepared then
2702 begin
2703 FQSelect.GenerateParamNames := FGenerateParamNames;
2704 FQSelect.ParamCheck := ParamCheck;
2705 FQSelect.Prepare;
2706 end;
2707 FQDelete.GenerateParamNames := FGenerateParamNames;
2708 if (Trim(FQDelete.SQL.Text) <> '') and (not FQDelete.Prepared) then
2709 FQDelete.Prepare;
2710 FQInsert.GenerateParamNames := FGenerateParamNames;
2711 if (Trim(FQInsert.SQL.Text) <> '') and (not FQInsert.Prepared) then
2712 FQInsert.Prepare;
2713 FQRefresh.GenerateParamNames := FGenerateParamNames;
2714 if (Trim(FQRefresh.SQL.Text) <> '') and (not FQRefresh.Prepared) then
2715 FQRefresh.Prepare;
2716 FQModify.GenerateParamNames := FGenerateParamNames;
2717 if (Trim(FQModify.SQL.Text) <> '') and (not FQModify.Prepared) then
2718 FQModify.Prepare;
2719 FInternalPrepared := True;
2720 InternalInitFieldDefs;
2721 end else
2722 IBError(ibxeEmptyQuery, [nil]);
2723 finally
2724 FBase.RestoreCursor;
2725 end;
2726 end;
2727
2728 procedure TIBCustomDataSet.RecordModified(Value: Boolean);
2729 begin
2730 SetModified(Value);
2731 end;
2732
2733 procedure TIBCustomDataSet.RevertRecord;
2734 var
2735 Buff: PRecordData;
2736 begin
2737 if FCachedUpdates and FUpdatesPending then
2738 begin
2739 Buff := PRecordData(GetActiveBuf);
2740 InternalRevertRecord(Buff^.rdRecordNumber);
2741 ReadRecordCache(Buff^.rdRecordNumber, PChar(Buff), False);
2742 DataEvent(deRecordChange, 0);
2743 end;
2744 end;
2745
2746 procedure TIBCustomDataSet.SaveOldBuffer(Buffer: PChar);
2747 var
2748 OldBuffer: Pointer;
2749 procedure CopyOldBuffer;
2750 begin
2751 CopyRecordBuffer(Buffer, OldBuffer);
2752 if BlobFieldCount > 0 then
2753 FillChar(PChar(OldBuffer)[FBlobCacheOffset],
2754 BlobFieldCount * SizeOf(TIBBlobStream) + ArrayFieldCount * SizeOf(IArray),
2755 0);
2756 end;
2757
2758 begin
2759 if (Buffer <> nil) and (PRecordData(Buffer)^.rdRecordNumber >= 0) then
2760 begin
2761 OldBuffer := AllocRecordBuffer;
2762 try
2763 if (PRecordData(Buffer)^.rdSavedOffset = $FFFFFFFF) then
2764 begin
2765 PRecordData(Buffer)^.rdSavedOffset := AdjustPosition(FOldBufferCache, 0,
2766 FILE_END);
2767 CopyOldBuffer;
2768 WriteCache(FOldBufferCache, 0, FILE_CURRENT, OldBuffer);
2769 WriteCache(FBufferCache, PRecordData(Buffer)^.rdRecordNumber * FRecordBufferSize,
2770 FILE_BEGIN, Buffer);
2771 end
2772 else begin
2773 CopyOldBuffer;
2774 WriteCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
2775 OldBuffer);
2776 end;
2777 finally
2778 FreeRecordBuffer(PChar(OldBuffer));
2779 end;
2780 end;
2781 end;
2782
2783 procedure TIBCustomDataSet.SetBufferChunks(Value: Integer);
2784 begin
2785 if (Value <= 0) then
2786 FBufferChunks := BufferCacheSize
2787 else
2788 FBufferChunks := Value;
2789 end;
2790
2791 procedure TIBCustomDataSet.SetDatabase(Value: TIBDatabase);
2792 begin
2793 if (csLoading in ComponentState) or (FBase.Database <> Value) then
2794 begin
2795 CheckDatasetClosed;
2796 InternalUnPrepare;
2797 FBase.Database := Value;
2798 FQDelete.Database := Value;
2799 FQInsert.Database := Value;
2800 FQRefresh.Database := Value;
2801 FQSelect.Database := Value;
2802 FQModify.Database := Value;
2803 FDatabaseInfo.Database := Value;
2804 FGeneratorField.Database := Value;
2805 end;
2806 end;
2807
2808 procedure TIBCustomDataSet.SetDeleteSQL(Value: TStrings);
2809 begin
2810 if FQDelete.SQL.Text <> Value.Text then
2811 begin
2812 Disconnect;
2813 FQDelete.SQL.Assign(Value);
2814 end;
2815 end;
2816
2817 procedure TIBCustomDataSet.SetInsertSQL(Value: TStrings);
2818 begin
2819 if FQInsert.SQL.Text <> Value.Text then
2820 begin
2821 Disconnect;
2822 FQInsert.SQL.Assign(Value);
2823 end;
2824 end;
2825
2826 procedure TIBCustomDataSet.SetInternalSQLParams(Params: ISQLParams; Buffer: Pointer);
2827 var
2828 i, j: Integer;
2829 cr, data: PChar;
2830 fn: string;
2831 st: RawByteString;
2832 OldBuffer: Pointer;
2833 Param: ISQLParam;
2834 begin
2835 if (Buffer = nil) then
2836 IBError(ibxeBufferNotSet, [nil]);
2837 if (not FInternalPrepared) then
2838 InternalPrepare;
2839 OldBuffer := nil;
2840 try
2841 for i := 0 to Params.GetCount - 1 do
2842 begin
2843 Param := Params[i];
2844 fn := Param.Name;
2845 if (Pos('OLD_', fn) = 1) then {mbcs ok}
2846 begin
2847 fn := Copy(fn, 5, Length(fn));
2848 if not Assigned(OldBuffer) then
2849 begin
2850 OldBuffer := AllocRecordBuffer;
2851 ReadRecordCache(PRecordData(Buffer)^.rdRecordNumber, OldBuffer, True);
2852 end;
2853 cr := OldBuffer;
2854 end
2855 else if (Pos('NEW_', fn) = 1) then {mbcs ok}
2856 begin
2857 fn := Copy(fn, 5, Length(fn));
2858 cr := Buffer;
2859 end
2860 else
2861 cr := Buffer;
2862 j := FQSelect.FieldIndex[fn] + 1;
2863 if (j > 0) then
2864 with PRecordData(cr)^,rdFields[j], FFieldColumns^[j] do
2865 begin
2866 if Param.name = 'IBX_INTERNAL_DBKEY' then {do not localize}
2867 begin
2868 PIBDBKey(Param.AsPointer)^ := rdDBKey;
2869 continue;
2870 end;
2871 if fdIsNull then
2872 Param.IsNull := True
2873 else begin
2874 Param.IsNull := False;
2875 data := cr + fdDataOfs;
2876 case fdDataType of
2877 SQL_TEXT, SQL_VARYING:
2878 begin
2879 SetString(st, data, fdDataLength);
2880 SetCodePage(st,fdCodePage,false);
2881 Param.AsString := st;
2882 end;
2883 SQL_FLOAT, SQL_DOUBLE, SQL_D_FLOAT:
2884 Param.AsDouble := PDouble(data)^;
2885 SQL_SHORT, SQL_LONG:
2886 begin
2887 if fdDataScale = 0 then
2888 Param.AsLong := PLong(data)^
2889 else
2890 if fdDataScale >= (-4) then
2891 Param.AsCurrency := PCurrency(data)^
2892 else
2893 Param.AsDouble := PDouble(data)^;
2894 end;
2895 SQL_INT64:
2896 begin
2897 if fdDataScale = 0 then
2898 Param.AsInt64 := PInt64(data)^
2899 else
2900 if fdDataScale >= (-4) then
2901 Param.AsCurrency := PCurrency(data)^
2902 else
2903 Param.AsDouble := PDouble(data)^;
2904 end;
2905 SQL_BLOB, SQL_ARRAY, SQL_QUAD:
2906 Param.AsQuad := PISC_QUAD(data)^;
2907 SQL_TYPE_DATE,
2908 SQL_TYPE_TIME,
2909 SQL_TIMESTAMP:
2910 {This is an IBX native format and not the TDataset approach. See also SetFieldData}
2911 Param.AsDateTime := PDateTime(data)^;
2912 SQL_BOOLEAN:
2913 Param.AsBoolean := PWordBool(data)^;
2914 end;
2915 end;
2916 end;
2917 end;
2918 finally
2919 if (OldBuffer <> nil) then
2920 FreeRecordBuffer(PChar(OldBuffer));
2921 end;
2922 end;
2923
2924 procedure TIBCustomDataSet.SetRefreshSQL(Value: TStrings);
2925 begin
2926 if FQRefresh.SQL.Text <> Value.Text then
2927 begin
2928 Disconnect;
2929 FQRefresh.SQL.Assign(Value);
2930 end;
2931 end;
2932
2933 procedure TIBCustomDataSet.SetSelectSQL(Value: TStrings);
2934 begin
2935 if FQSelect.SQL.Text <> Value.Text then
2936 begin
2937 Disconnect;
2938 FQSelect.SQL.Assign(Value);
2939 end;
2940 end;
2941
2942 procedure TIBCustomDataSet.SetModifySQL(Value: TStrings);
2943 begin
2944 if FQModify.SQL.Text <> Value.Text then
2945 begin
2946 Disconnect;
2947 FQModify.SQL.Assign(Value);
2948 end;
2949 end;
2950
2951 procedure TIBCustomDataSet.SetTransaction(Value: TIBTransaction);
2952 begin
2953 if (FBase.Transaction <> Value) then
2954 begin
2955 CheckDatasetClosed;
2956 FBase.Transaction := Value;
2957 FQDelete.Transaction := Value;
2958 FQInsert.Transaction := Value;
2959 FQRefresh.Transaction := Value;
2960 FQSelect.Transaction := Value;
2961 FQModify.Transaction := Value;
2962 FGeneratorField.Transaction := Value;
2963 end;
2964 end;
2965
2966 procedure TIBCustomDataSet.SetUniDirectional(Value: Boolean);
2967 begin
2968 CheckDatasetClosed;
2969 FUniDirectional := Value;
2970 end;
2971
2972 procedure TIBCustomDataSet.SetUpdateRecordTypes(Value: TIBUpdateRecordTypes);
2973 begin
2974 FUpdateRecordTypes := Value;
2975 if Active then
2976 First;
2977 end;
2978
2979 procedure TIBCustomDataSet.RefreshParams;
2980 var
2981 DataSet: TDataSet;
2982 begin
2983 DisableControls;
2984 try
2985 if FDataLink.DataSource <> nil then
2986 begin
2987 DataSet := FDataLink.DataSource.DataSet;
2988 if DataSet <> nil then
2989 if DataSet.Active and (DataSet.State <> dsSetKey) then
2990 begin
2991 Close;
2992 Open;
2993 end;
2994 end;
2995 finally
2996 EnableControls;
2997 end;
2998 end;
2999
3000 procedure TIBCustomDataSet.RegisterIBLink(Sender: TIBControlLink);
3001 begin
3002 if FIBLinks.IndexOf(Sender) = -1 then
3003 begin
3004 FIBLinks.Add(Sender);
3005 if Active then
3006 begin
3007 Active := false;
3008 Active := true;
3009 end;
3010 end;
3011 end;
3012
3013
3014 procedure TIBCustomDataSet.SQLChanging(Sender: TObject);
3015 begin
3016 Active := false;
3017 { if FOpen then
3018 InternalClose;}
3019 if FInternalPrepared then
3020 InternalUnPrepare;
3021 FieldDefs.Clear;
3022 FieldDefs.Updated := false;
3023 end;
3024
3025 procedure TIBCustomDataSet.SQLChanged(Sender: TObject);
3026 begin
3027 FBaseSQLSelect.assign(FQSelect.SQL);
3028 end;
3029
3030 { I can "undelete" uninserted records (make them "inserted" again).
3031 I can "undelete" cached deleted (the deletion hasn't yet occurred) }
3032 procedure TIBCustomDataSet.Undelete;
3033 var
3034 Buff: PRecordData;
3035 begin
3036 CheckActive;
3037 Buff := PRecordData(GetActiveBuf);
3038 with Buff^ do
3039 begin
3040 if rdCachedUpdateStatus = cusUninserted then
3041 begin
3042 rdCachedUpdateStatus := cusInserted;
3043 Dec(FDeletedRecords);
3044 end
3045 else if (rdUpdateStatus = usDeleted) and
3046 (rdCachedUpdateStatus = cusDeleted) then
3047 begin
3048 rdCachedUpdateStatus := cusUnmodified;
3049 rdUpdateStatus := usUnmodified;
3050 Dec(FDeletedRecords);
3051 end;
3052 WriteRecordCache(rdRecordNumber, PChar(Buff));
3053 end;
3054 end;
3055
3056 procedure TIBCustomDataSet.UnRegisterIBLink(Sender: TIBControlLink);
3057 begin
3058 FIBLinks.Remove(Sender);
3059 end;
3060
3061 function TIBCustomDataSet.UpdateStatus: TUpdateStatus;
3062 begin
3063 if Active then
3064 if GetActiveBuf <> nil then
3065 result := PRecordData(GetActiveBuf)^.rdUpdateStatus
3066 else
3067 result := usUnmodified
3068 else
3069 result := usUnmodified;
3070 end;
3071
3072 function TIBCustomDataSet.IsSequenced: Boolean;
3073 begin
3074 Result := Assigned( FQSelect ) and FQSelect.EOF;
3075 end;
3076
3077 function TIBCustomDataSet.ParamByName(ParamName: String): ISQLParam;
3078 begin
3079 ActivateConnection;
3080 ActivateTransaction;
3081 if not FInternalPrepared then
3082 InternalPrepare;
3083 Result := Params.ByName(ParamName);
3084 end;
3085
3086 function TIBCustomDataSet.GetRowsAffected(var SelectCount, InsertCount,
3087 UpdateCount, DeleteCount: integer): boolean;
3088 begin
3089 Result := Active;
3090 SelectCount := FSelectCount;
3091 InsertCount := FInsertCount;
3092 UpdateCount := FUpdateCount;
3093 DeleteCount := FDeleteCount;
3094 end;
3095
3096 function TIBCustomDataSet.GetPerfStatistics(var stats: TPerfCounters): boolean;
3097 begin
3098 Result := EnableStatistics and (FQSelect.Statement <> nil) and
3099 FQSelect.Statement.GetPerfStatistics(stats);
3100 end;
3101
3102 {Beware: the parameter FCache is used as an identifier to determine which
3103 cache is being operated on and is not referenced in the computation.
3104 The result is an adjusted offset into the identified cache, either the
3105 Buffer Cache or the old Buffer Cache.}
3106
3107 function TIBCustomDataSet.AdjustPosition(FCache: PChar; Offset: DWORD;
3108 Origin: Integer): DWORD;
3109 var
3110 OldCacheSize: Integer;
3111 begin
3112 if (FCache = FBufferCache) then
3113 begin
3114 case Origin of
3115 FILE_BEGIN: FBPos := Offset;
3116 FILE_CURRENT: FBPos := FBPos + Offset;
3117 FILE_END: FBPos := DWORD(FBEnd) + Offset;
3118 end;
3119 OldCacheSize := FCacheSize;
3120 while (FBPos >= DWORD(FCacheSize)) do
3121 Inc(FCacheSize, FBufferChunkSize);
3122 if FCacheSize > OldCacheSize then
3123 IBAlloc(FBufferCache, FCacheSize, FCacheSize);
3124 result := FBPos;
3125 end
3126 else begin
3127 case Origin of
3128 FILE_BEGIN: FOBPos := Offset;
3129 FILE_CURRENT: FOBPos := FOBPos + Offset;
3130 FILE_END: FOBPos := DWORD(FOBEnd) + Offset;
3131 end;
3132 OldCacheSize := FOldCacheSize;
3133 while (FBPos >= DWORD(FOldCacheSize)) do
3134 Inc(FOldCacheSize, FBufferChunkSize);
3135 if FOldCacheSize > OldCacheSize then
3136 IBAlloc(FOldBufferCache, FOldCacheSize, FOldCacheSize);
3137 result := FOBPos;
3138 end;
3139 end;
3140
3141 procedure TIBCustomDataSet.ReadCache(FCache: PChar; Offset: DWORD; Origin: Integer;
3142 Buffer: PChar);
3143 var
3144 pCache: PChar;
3145 AdjustedOffset: DWORD;
3146 bOld: Boolean;
3147 begin
3148 bOld := (FCache = FOldBufferCache);
3149 AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
3150 if not bOld then
3151 pCache := FBufferCache + AdjustedOffset
3152 else
3153 pCache := FOldBufferCache + AdjustedOffset;
3154 Move(pCache^, Buffer^, DWORD(FRecordBufferSize));
3155 AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
3156 end;
3157
3158 procedure TIBCustomDataSet.ReadRecordCache(RecordNumber: Integer; Buffer: PChar;
3159 ReadOldBuffer: Boolean);
3160 begin
3161 if FUniDirectional then
3162 RecordNumber := RecordNumber mod UniCache;
3163 if (ReadOldBuffer) then
3164 begin
3165 ReadRecordCache(RecordNumber, Buffer, False);
3166 if FCachedUpdates and
3167 (PRecordData(Buffer)^.rdSavedOffset <> $FFFFFFFF) then
3168 ReadCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
3169 Buffer)
3170 else
3171 if ReadOldBuffer and
3172 (PRecordData(FOldBuffer)^.rdRecordNumber = RecordNumber) then
3173 CopyRecordBuffer( FOldBuffer, Buffer )
3174 end
3175 else
3176 ReadCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
3177 end;
3178
3179 procedure TIBCustomDataSet.WriteCache(FCache: PChar; Offset: DWORD; Origin: Integer;
3180 Buffer: PChar);
3181 var
3182 pCache: PChar;
3183 AdjustedOffset: DWORD;
3184 bOld: Boolean;
3185 dwEnd: DWORD;
3186 begin
3187 bOld := (FCache = FOldBufferCache);
3188 AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
3189 if not bOld then
3190 pCache := FBufferCache + AdjustedOffset
3191 else
3192 pCache := FOldBufferCache + AdjustedOffset;
3193 Move(Buffer^, pCache^, FRecordBufferSize);
3194 dwEnd := AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
3195 if not bOld then
3196 begin
3197 if (dwEnd > FBEnd) then
3198 FBEnd := dwEnd;
3199 end
3200 else begin
3201 if (dwEnd > FOBEnd) then
3202 FOBEnd := dwEnd;
3203 end;
3204 end;
3205
3206 procedure TIBCustomDataSet.WriteRecordCache(RecordNumber: Integer; Buffer: PChar);
3207 begin
3208 if RecordNumber >= 0 then
3209 begin
3210 if FUniDirectional then
3211 RecordNumber := RecordNumber mod UniCache;
3212 WriteCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
3213 end;
3214 end;
3215
3216 function TIBCustomDataSet.AllocRecordBuffer: PChar;
3217 begin
3218 result := nil;
3219 IBAlloc(result, FRecordBufferSize, FRecordBufferSize);
3220 Move(FModelBuffer^, result^, FRecordBufferSize);
3221 end;
3222
3223 function TIBCustomDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
3224 var
3225 pb: PBlobDataArray;
3226 fs: TIBBlobStream;
3227 Buff: PChar;
3228 bTr, bDB: Boolean;
3229 begin
3230 if (Field = nil) or (Field.DataSet <> self) then
3231 IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3232 Buff := GetActiveBuf;
3233 if Buff = nil then
3234 begin
3235 fs := TIBBlobStream.Create;
3236 fs.Mode := bmReadWrite;
3237 fs.Database := Database;
3238 fs.Transaction := Transaction;
3239 fs.SetField(Field);
3240 FBlobStreamList.Add(Pointer(fs));
3241 result := TIBDSBlobStream.Create(Field, fs, Mode);
3242 exit;
3243 end;
3244 pb := PBlobDataArray(Buff + FBlobCacheOffset);
3245 if pb^[Field.Offset] = nil then
3246 begin
3247 AdjustRecordOnInsert(Buff);
3248 pb^[Field.Offset] := TIBBlobStream.Create;
3249 fs := pb^[Field.Offset];
3250 FBlobStreamList.Add(Pointer(fs));
3251 fs.Mode := bmReadWrite;
3252 fs.Database := Database;
3253 fs.Transaction := Transaction;
3254 fs.SetField(Field);
3255 fs.BlobID :=
3256 PISC_QUAD(@Buff[FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]].fdDataOfs])^;
3257 if (CachedUpdates) then
3258 begin
3259 bTr := not Transaction.InTransaction;
3260 bDB := not Database.Connected;
3261 if bDB then
3262 Database.Open;
3263 if bTr then
3264 Transaction.StartTransaction;
3265 fs.Seek(0, soFromBeginning);
3266 if bTr then
3267 Transaction.Commit;
3268 if bDB then
3269 Database.Close;
3270 end;
3271 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3272 end else
3273 fs := pb^[Field.Offset];
3274 result := TIBDSBlobStream.Create(Field, fs, Mode);
3275 end;
3276
3277 function TIBCustomDataSet.GetArray(Field: TIBArrayField): IArray;
3278 var Buff: PChar;
3279 pda: PArrayDataArray;
3280 bTr, bDB: Boolean;
3281 begin
3282 if (Field = nil) or (Field.DataSet <> self) then
3283 IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3284 Buff := GetActiveBuf;
3285 if Buff = nil then
3286 Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3287 Field.FRelationName,Field.FieldName)
3288 else
3289 begin
3290 pda := PArrayDataArray(Buff + FArrayCacheOffset);
3291 if pda^[Field.FCacheOffset] = nil then
3292 begin
3293 AdjustRecordOnInsert(Buff);
3294 if Field.IsNull then
3295 Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3296 Field.FRelationName,Field.FieldName)
3297 else
3298 Result := Database.Attachment.OpenArray(Transaction.TransactionIntf,
3299 Field.FRelationName,Field.FieldName,Field.ArrayID);
3300 pda^[Field.FCacheOffset] := TIBArray.Create(Field,Result);
3301 FArrayList.Add(pda^[Field.FCacheOffset]);
3302 if (CachedUpdates) then
3303 begin
3304 bTr := not Transaction.InTransaction;
3305 bDB := not Database.Connected;
3306 if bDB then
3307 Database.Open;
3308 if bTr then
3309 Transaction.StartTransaction;
3310 pda^[Field.FCacheOffset].ArrayIntf.PreLoad;
3311 if bTr then
3312 Transaction.Commit;
3313 if bDB then
3314 Database.Close;
3315 end;
3316 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3317 end
3318 else
3319 Result := pda^[Field.FCacheOffset].ArrayIntf;
3320 end;
3321 end;
3322
3323 procedure TIBCustomDataSet.SetArrayIntf(AnArray: IArray; Field: TIBArrayField);
3324 var Buff: PChar;
3325 pda: PArrayDataArray;
3326 begin
3327 if (Field = nil) or (Field.DataSet <> self) then
3328 IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3329 Buff := GetActiveBuf;
3330 if Buff <> nil then
3331 begin
3332 AdjustRecordOnInsert(Buff);
3333 pda := PArrayDataArray(Buff + FArrayCacheOffset);
3334 pda^[Field.FCacheOffset].FArray := AnArray;
3335 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3336 end;
3337 end;
3338
3339 function TIBCustomDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
3340 const
3341 CMPLess = -1;
3342 CMPEql = 0;
3343 CMPGtr = 1;
3344 RetCodes: array[Boolean, Boolean] of ShortInt = ((2, CMPLess),
3345 (CMPGtr, CMPEql));
3346 begin
3347 result := RetCodes[Bookmark1 = nil, Bookmark2 = nil];
3348
3349 if Result = 2 then
3350 begin
3351 if PInteger(Bookmark1)^ < PInteger(Bookmark2)^ then
3352 Result := CMPLess
3353 else
3354 if PInteger(Bookmark1)^ > PInteger(Bookmark2)^ then
3355 Result := CMPGtr
3356 else
3357 Result := CMPEql;
3358 end;
3359 end;
3360
3361 procedure TIBCustomDataSet.DoBeforeDelete;
3362 var
3363 Buff: PRecordData;
3364 begin
3365 if not CanDelete then
3366 IBError(ibxeCannotDelete, [nil]);
3367 Buff := PRecordData(GetActiveBuf);
3368 if FCachedUpdates and
3369 (Buff^.rdCachedUpdateStatus in [cusUnmodified]) then
3370 SaveOldBuffer(PChar(Buff));
3371 inherited DoBeforeDelete;
3372 end;
3373
3374 procedure TIBCustomDataSet.DoAfterDelete;
3375 begin
3376 inherited DoAfterDelete;
3377 FBase.DoAfterDelete(self);
3378 InternalAutoCommit;
3379 end;
3380
3381 procedure TIBCustomDataSet.DoBeforeEdit;
3382 var
3383 Buff: PRecordData;
3384 begin
3385 Buff := PRecordData(GetActiveBuf);
3386 if not(CanEdit or (FQModify.SQL.Count <> 0) or
3387 (FCachedUpdates and Assigned(FOnUpdateRecord))) then
3388 IBError(ibxeCannotUpdate, [nil]);
3389 if FCachedUpdates and (Buff^.rdCachedUpdateStatus in [cusUnmodified, cusInserted]) then
3390 SaveOldBuffer(PChar(Buff));
3391 CopyRecordBuffer(GetActiveBuf, FOldBuffer);
3392 inherited DoBeforeEdit;
3393 end;
3394
3395 procedure TIBCustomDataSet.DoAfterEdit;
3396 begin
3397 inherited DoAfterEdit;
3398 FBase.DoAfterEdit(self);
3399 end;
3400
3401 procedure TIBCustomDataSet.DoBeforeInsert;
3402 begin
3403 if not CanInsert then
3404 IBError(ibxeCannotInsert, [nil]);
3405 inherited DoBeforeInsert;
3406 end;
3407
3408 procedure TIBCustomDataSet.DoAfterInsert;
3409 begin
3410 if GeneratorField.ApplyOnEvent = gaeOnNewRecord then
3411 GeneratorField.Apply;
3412 inherited DoAfterInsert;
3413 FBase.DoAfterInsert(self);
3414 end;
3415
3416 procedure TIBCustomDataSet.DoBeforeClose;
3417 begin
3418 inherited DoBeforeClose;
3419 if FInTransactionEnd and (FCloseAction = TARollback) then
3420 Exit;
3421 if State in [dsInsert,dsEdit] then
3422 begin
3423 if DataSetCloseAction = dcSaveChanges then
3424 Post;
3425 {Note this can fail with an exception e.g. due to
3426 database validation error. In which case the dataset remains open }
3427 end;
3428 if FCachedUpdates and FUpdatesPending and (DataSetCloseAction = dcSaveChanges) then
3429 ApplyUpdates;
3430 end;
3431
3432 procedure TIBCustomDataSet.DoBeforeOpen;
3433 var i: integer;
3434 begin
3435 if assigned(FParser) then
3436 FParser.Reset;
3437 for i := 0 to FIBLinks.Count - 1 do
3438 TIBControlLink(FIBLinks[i]).UpdateSQL(self);
3439 inherited DoBeforeOpen;
3440 for i := 0 to FIBLinks.Count - 1 do
3441 TIBControlLink(FIBLinks[i]).UpdateParams(self);
3442 end;
3443
3444 procedure TIBCustomDataSet.DoBeforePost;
3445 begin
3446 inherited DoBeforePost;
3447 if (State = dsInsert) and
3448 (GeneratorField.ApplyOnEvent = gaeOnPostRecord) then
3449 GeneratorField.Apply
3450 end;
3451
3452 procedure TIBCustomDataSet.DoAfterPost;
3453 begin
3454 inherited DoAfterPost;
3455 FBase.DoAfterPost(self);
3456 InternalAutoCommit;
3457 end;
3458
3459 procedure TIBCustomDataSet.FetchAll;
3460 var
3461 CurBookmark: TBookmark;
3462 begin
3463 FBase.SetCursor;
3464 try
3465 if FQSelect.EOF or not FQSelect.Open then
3466 exit;
3467 DisableControls;
3468 try
3469 CurBookmark := Bookmark;
3470 Last;
3471 Bookmark := CurBookmark;
3472 finally
3473 EnableControls;
3474 end;
3475 finally
3476 FBase.RestoreCursor;
3477 end;
3478 end;
3479
3480 procedure TIBCustomDataSet.FreeRecordBuffer(var Buffer: PChar);
3481 begin
3482 FreeMem(Buffer);
3483 Buffer := nil;
3484 end;
3485
3486 procedure TIBCustomDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
3487 begin
3488 Move(PRecordData(Buffer)^.rdRecordNumber, Data^, BookmarkSize);
3489 end;
3490
3491 function TIBCustomDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
3492 begin
3493 result := PRecordData(Buffer)^.rdBookmarkFlag;
3494 end;
3495
3496 function TIBCustomDataSet.GetCanModify: Boolean;
3497 begin
3498 result := (FQInsert.SQL.Text <> '') or
3499 (FQModify.SQL.Text <> '') or
3500 (FQDelete.SQL.Text <> '') or
3501 (Assigned(FUpdateObject));
3502 end;
3503
3504 function TIBCustomDataSet.GetCurrentRecord(Buffer: PChar): Boolean;
3505 begin
3506 if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then
3507 begin
3508 UpdateCursorPos;
3509 ReadRecordCache(PRecordData(ActiveBuffer)^.rdRecordNumber, Buffer, False);
3510 result := True;
3511 end
3512 else
3513 result := False;
3514 end;
3515
3516 function TIBCustomDataSet.GetDataSource: TDataSource;
3517 begin
3518 if FDataLink = nil then
3519 result := nil
3520 else
3521 result := FDataLink.DataSource;
3522 end;
3523
3524 function TIBCustomDataSet.GetDBAliasName(FieldNo: integer): string;
3525 begin
3526 Result := FAliasNameMap[FieldNo-1]
3527 end;
3528
3529 function TIBCustomDataSet.GetFieldDefFromAlias(aliasName: string): TFieldDef;
3530 var
3531 i: integer;
3532 begin
3533 Result := nil;
3534 for i := 0 to Length(FAliasNameMap) - 1 do
3535 if FAliasNameMap[i] = aliasName then
3536 begin
3537 Result := FieldDefs[i];
3538 Exit
3539 end;
3540 end;
3541
3542 function TIBCustomDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
3543 begin
3544 Result := DefaultFieldClasses[FieldType];
3545 end;
3546
3547 function TIBCustomDataSet.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean;
3548 begin
3549 result := GetFieldData(FieldByNumber(FieldNo), buffer);
3550 end;
3551
3552 function TIBCustomDataSet.InternalGetFieldData(Field: TField; Buffer: Pointer): Boolean;
3553 var
3554 Buff, Data: PChar;
3555 CurrentRecord: PRecordData;
3556 begin
3557 result := False;
3558 Buff := GetActiveBuf;
3559 if (Buff = nil) or
3560 (not IsVisible(Buff)) or not assigned(Field.DataSet) then
3561 exit;
3562 { The intention here is to stuff the buffer with the data for the
3563 referenced field for the current record }
3564 CurrentRecord := PRecordData(Buff);
3565 if (Field.FieldNo < 0) then
3566 begin
3567 Inc(Buff, FRecordSize + Field.Offset);
3568 result := Boolean(Buff[0]);
3569 if result and (Buffer <> nil) then
3570 Move(Buff[1], Buffer^, Field.DataSize);
3571 end
3572 else
3573 if (FMappedFieldPosition[Field.FieldNo - 1] > 0) and
3574 (FMappedFieldPosition[Field.FieldNo - 1] <= CurrentRecord^.rdFieldCount) then
3575 with CurrentRecord^.rdFields[FMappedFieldPosition[Field.FieldNo - 1]],
3576 FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]] do
3577 begin
3578 result := not fdIsNull;
3579 if result and (Buffer <> nil) then
3580 begin
3581 Data := Buff + fdDataOfs;
3582 if (fdDataType = SQL_VARYING) or (fdDataType = SQL_TEXT) then
3583 begin
3584 if fdDataLength < Field.DataSize then
3585 begin
3586 Move(Data^, Buffer^, fdDataLength);
3587 PChar(Buffer)[fdDataLength] := #0;
3588 end
3589 else
3590 IBError(ibxeFieldSizeError,[Field.FieldName])
3591 end
3592 else
3593 Move(Data^, Buffer^, Field.DataSize);
3594 end;
3595 end;
3596 end;
3597
3598 { GetRecNo and SetRecNo both operate off of 1-based indexes as
3599 opposed to 0-based indexes.
3600 This is because we want LastRecordNumber/RecordCount = 1 }
3601
3602 function TIBCustomDataSet.GetRecNo: Integer;
3603 begin
3604 if GetActiveBuf = nil then
3605 result := 0
3606 else
3607 result := PRecordData(GetActiveBuf)^.rdRecordNumber + 1;
3608 end;
3609
3610 function TIBCustomDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
3611 DoCheck: Boolean): TGetResult;
3612 var
3613 Accept: Boolean;
3614 SaveState: TDataSetState;
3615 begin
3616 Result := grOK;
3617 if Filtered and Assigned(OnFilterRecord) then
3618 begin
3619 Accept := False;
3620 SaveState := SetTempState(dsFilter);
3621 while not Accept do
3622 begin
3623 Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3624 if Result <> grOK then
3625 break;
3626 FFilterBuffer := Buffer;
3627 try
3628 Accept := True;
3629 OnFilterRecord(Self, Accept);
3630 if not Accept and (GetMode = gmCurrent) then
3631 GetMode := gmPrior;
3632 except
3633 // FBase.HandleException(Self);
3634 end;
3635 end;
3636 RestoreState(SaveState);
3637 end
3638 else
3639 Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3640 end;
3641
3642 function TIBCustomDataSet.InternalGetRecord(Buffer: PChar; GetMode: TGetMode;
3643 DoCheck: Boolean): TGetResult;
3644 begin
3645 result := grError;
3646 case GetMode of
3647 gmCurrent: begin
3648 if (FCurrentRecord >= 0) then begin
3649 if FCurrentRecord < FRecordCount then
3650 ReadRecordCache(FCurrentRecord, Buffer, False)
3651 else begin
3652 while (not FQSelect.EOF) and FQSelect.Next and
3653 (FCurrentRecord >= FRecordCount) do begin
3654 FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
3655 Inc(FRecordCount);
3656 end;
3657 FCurrentRecord := FRecordCount - 1;
3658 if (FCurrentRecord >= 0) then
3659 ReadRecordCache(FCurrentRecord, Buffer, False);
3660 end;
3661 result := grOk;
3662 end else
3663 result := grBOF;
3664 end;
3665 gmNext: begin
3666 result := grOk;
3667 if FCurrentRecord = FRecordCount then
3668 result := grEOF
3669 else if FCurrentRecord = FRecordCount - 1 then begin
3670 if (not FQSelect.EOF) then begin
3671 FQSelect.Next;
3672 Inc(FCurrentRecord);
3673 end;
3674 if (FQSelect.EOF) then begin
3675 result := grEOF;
3676 end else begin
3677 Inc(FRecordCount);
3678 FetchCurrentRecordToBuffer(FQSelect, FCurrentRecord, Buffer);
3679 end;
3680 end else if (FCurrentRecord < FRecordCount) then begin
3681 Inc(FCurrentRecord);
3682 ReadRecordCache(FCurrentRecord, Buffer, False);
3683 end;
3684 end;
3685 else { gmPrior }
3686 begin
3687 if (FCurrentRecord = 0) then begin
3688 Dec(FCurrentRecord);
3689 result := grBOF;
3690 end else if (FCurrentRecord > 0) and
3691 (FCurrentRecord <= FRecordCount) then begin
3692 Dec(FCurrentRecord);
3693 ReadRecordCache(FCurrentRecord, Buffer, False);
3694 result := grOk;
3695 end else if (FCurrentRecord = -1) then
3696 result := grBOF;
3697 end;
3698 end;
3699 if result = grOk then
3700 result := AdjustCurrentRecord(Buffer, GetMode);
3701 if result = grOk then with PRecordData(Buffer)^ do begin
3702 rdBookmarkFlag := bfCurrent;
3703 GetCalcFields(Buffer);
3704 end else if (result = grEOF) then begin
3705 CopyRecordBuffer(FModelBuffer, Buffer);
3706 PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3707 end else if (result = grBOF) then begin
3708 CopyRecordBuffer(FModelBuffer, Buffer);
3709 PRecordData(Buffer)^.rdBookmarkFlag := bfBOF;
3710 end else if (result = grError) then begin
3711 CopyRecordBuffer(FModelBuffer, Buffer);
3712 PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3713 end;;
3714 end;
3715
3716 function TIBCustomDataSet.GetRecordCount: Integer;
3717 begin
3718 result := FRecordCount - FDeletedRecords;
3719 end;
3720
3721 function TIBCustomDataSet.GetRecordSize: Word;
3722 begin
3723 result := FRecordBufferSize;
3724 end;
3725
3726 procedure TIBCustomDataSet.InternalAutoCommit;
3727 begin
3728 with Transaction do
3729 if InTransaction and (FAutoCommit = acCommitRetaining) then
3730 begin
3731 if CachedUpdates then ApplyUpdates;
3732 CommitRetaining;
3733 end;
3734 end;
3735
3736 procedure TIBCustomDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
3737 begin
3738 CheckEditState;
3739 begin
3740 { When adding records, we *always* append.
3741 Insertion is just too costly }
3742 AdjustRecordOnInsert(Buffer);
3743 with PRecordData(Buffer)^ do
3744 begin
3745 rdUpdateStatus := usInserted;
3746 rdCachedUpdateStatus := cusInserted;
3747 end;
3748 if not CachedUpdates then
3749 InternalPostRecord(FQInsert, Buffer)
3750 else begin
3751 WriteRecordCache(FCurrentRecord, Buffer);
3752 FUpdatesPending := True;
3753 end;
3754 Inc(FRecordCount);
3755 InternalSetToRecord(Buffer);
3756 end
3757 end;
3758
3759 procedure TIBCustomDataSet.InternalCancel;
3760 var
3761 Buff: PChar;
3762 CurRec: Integer;
3763 pda: PArrayDataArray;
3764 i: integer;
3765 begin
3766 inherited InternalCancel;
3767 Buff := GetActiveBuf;
3768 if Buff <> nil then
3769 begin
3770 pda := PArrayDataArray(Buff + FArrayCacheOffset);
3771 for i := 0 to ArrayFieldCount - 1 do
3772 pda^[i].ArrayIntf.CancelChanges;
3773 CurRec := FCurrentRecord;
3774 AdjustRecordOnInsert(Buff);
3775 if (State = dsEdit) then begin
3776 CopyRecordBuffer(FOldBuffer, Buff);
3777 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3778 end else begin
3779 CopyRecordBuffer(FModelBuffer, Buff);
3780 PRecordData(Buff)^.rdUpdateStatus := usDeleted;
3781 PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
3782 PRecordData(Buff)^.rdBookmarkFlag := bfEOF;
3783 FCurrentRecord := CurRec;
3784 end;
3785 end;
3786 end;
3787
3788
3789 procedure TIBCustomDataSet.InternalClose;
3790 begin
3791 if FDidActivate then
3792 DeactivateTransaction;
3793 FQSelect.Close;
3794 ClearBlobCache;
3795 ClearArrayCache;
3796 FreeRecordBuffer(FModelBuffer);
3797 FreeRecordBuffer(FOldBuffer);
3798 FCurrentRecord := -1;
3799 FOpen := False;
3800 FRecordCount := 0;
3801 FDeletedRecords := 0;
3802 FRecordSize := 0;
3803 FBPos := 0;
3804 FOBPos := 0;
3805 FCacheSize := 0;
3806 FOldCacheSize := 0;
3807 FBEnd := 0;
3808 FOBEnd := 0;
3809 FreeMem(FBufferCache);
3810 FBufferCache := nil;
3811 FreeMem(FFieldColumns);
3812 FFieldColumns := nil;
3813 FreeMem(FOldBufferCache);
3814 FOldBufferCache := nil;
3815 BindFields(False);
3816 ResetParser;
3817 if DefaultFields then DestroyFields;
3818 end;
3819
3820 procedure TIBCustomDataSet.InternalDelete;
3821 var
3822 Buff: PChar;
3823 begin
3824 FBase.SetCursor;
3825 try
3826 Buff := GetActiveBuf;
3827 if CanDelete then
3828 begin
3829 if not CachedUpdates then
3830 InternalDeleteRecord(FQDelete, Buff)
3831 else
3832 begin
3833 with PRecordData(Buff)^ do
3834 begin
3835 if rdCachedUpdateStatus = cusInserted then
3836 rdCachedUpdateStatus := cusUninserted
3837 else begin
3838 rdUpdateStatus := usDeleted;
3839 rdCachedUpdateStatus := cusDeleted;
3840 end;
3841 end;
3842 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3843 end;
3844 Inc(FDeletedRecords);
3845 FUpdatesPending := True;
3846 end else
3847 IBError(ibxeCannotDelete, [nil]);
3848 finally
3849 FBase.RestoreCursor;
3850 end;
3851 end;
3852
3853 procedure TIBCustomDataSet.InternalFirst;
3854 begin
3855 FCurrentRecord := -1;
3856 end;
3857
3858 procedure TIBCustomDataSet.InternalGotoBookmark(Bookmark: Pointer);
3859 begin
3860 FCurrentRecord := PInteger(Bookmark)^;
3861 end;
3862
3863 procedure TIBCustomDataSet.InternalHandleException;
3864 begin
3865 FBase.HandleException(Self)
3866 end;
3867
3868 procedure TIBCustomDataSet.InternalInitFieldDefs;
3869 begin
3870 if not InternalPrepared then
3871 begin
3872 InternalPrepare;
3873 exit;
3874 end;
3875 FieldDefsFromQuery(FQSelect);
3876 end;
3877
3878 procedure TIBCustomDataSet.FieldDefsFromQuery(SourceQuery: TIBSQL);
3879 const
3880 DefaultSQL = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3881 'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME ' + {do not localize}
3882 'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3883 'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3884 'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3885 'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3886 ' (not F.RDB$DEFAULT_VALUE is NULL)) '; {do not localize}
3887
3888 DefaultSQLODS12 = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3889 'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME, R.RDB$IDENTITY_TYPE ' + {do not localize}
3890 'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3891 'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3892 'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3893 'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3894 ' (not F.RDB$DEFAULT_VALUE is NULL) or ' + {do not localize}
3895 ' ( not R.RDB$IDENTITY_TYPE is NULL))' ; {do not localize}
3896
3897 var
3898 FieldType: TFieldType;
3899 FieldSize: Word;
3900 FieldDataSize: integer;
3901 CharSetSize: integer;
3902 CharSetName: RawByteString;
3903 FieldCodePage: TSystemCodePage;
3904 FieldNullable : Boolean;
3905 i, FieldPosition, FieldPrecision: Integer;
3906 FieldAliasName, DBAliasName: string;
3907 aRelationName, FieldName: string;
3908 Query : TIBSQL;
3909 FieldIndex: Integer;
3910 FRelationNodes : TRelationNode;
3911 aArrayDimensions: integer;
3912 aArrayBounds: TArrayBounds;
3913 ArrayMetaData: IArrayMetaData;
3914
3915 function Add_Node(Relation, Field : String) : TRelationNode;
3916 var
3917 FField : TFieldNode;
3918 begin
3919 if FRelationNodes.RelationName = '' then
3920 Result := FRelationNodes
3921 else
3922 begin
3923 Result := TRelationNode.Create;
3924 Result.NextRelation := FRelationNodes;
3925 end;
3926 Result.RelationName := Relation;
3927 FRelationNodes := Result;
3928 Query.Params[0].AsString := Relation;
3929 Query.ExecQuery;
3930 while not Query.Eof do
3931 begin
3932 FField := TFieldNode.Create;
3933 FField.FieldName := Query.Fields[2].AsString;
3934 FField.DEFAULT_VALUE := not Query.Fields[1].IsNull;
3935 FField.COMPUTED_BLR := not Query.Fields[0].IsNull;
3936 FField.IDENTITY_COLUMN := (Query.FieldCount > 3) and not Query.Fields[3].IsNull;
3937 FField.NextField := Result.FieldNodes;
3938 Result.FieldNodes := FField;
3939 Query.Next;
3940 end;
3941 Query.Close;
3942 end;
3943
3944 function Has_COMPUTED_BLR(Relation, Field : String) : Boolean;
3945 var
3946 FRelation : TRelationNode;
3947 FField : TFieldNode;
3948 begin
3949 FRelation := FRelationNodes;
3950 while Assigned(FRelation) and
3951 (FRelation.RelationName <> Relation) do
3952 FRelation := FRelation.NextRelation;
3953 if not Assigned(FRelation) then
3954 FRelation := Add_Node(Relation, Field);
3955 Result := false;
3956 FField := FRelation.FieldNodes;
3957 while Assigned(FField) do
3958 if FField.FieldName = Field then
3959 begin
3960 Result := Ffield.COMPUTED_BLR;
3961 Exit;
3962 end
3963 else
3964 FField := Ffield.NextField;
3965 end;
3966
3967 function Has_DEFAULT_VALUE(Relation, Field : String) : Boolean;
3968 var
3969 FRelation : TRelationNode;
3970 FField : TFieldNode;
3971 begin
3972 FRelation := FRelationNodes;
3973 while Assigned(FRelation) and
3974 (FRelation.RelationName <> Relation) do
3975 FRelation := FRelation.NextRelation;
3976 if not Assigned(FRelation) then
3977 FRelation := Add_Node(Relation, Field);
3978 Result := false;
3979 FField := FRelation.FieldNodes;
3980 while Assigned(FField) do
3981 if FField.FieldName = Field then
3982 begin
3983 Result := Ffield.DEFAULT_VALUE;
3984 Exit;
3985 end
3986 else
3987 FField := Ffield.NextField;
3988 end;
3989
3990 function Is_IDENTITY_COLUMN(Relation, Field : String) : Boolean;
3991 var
3992 FRelation : TRelationNode;
3993 FField : TFieldNode;
3994 begin
3995 FRelation := FRelationNodes;
3996 while Assigned(FRelation) and
3997 (FRelation.RelationName <> Relation) do
3998 FRelation := FRelation.NextRelation;
3999 if not Assigned(FRelation) then
4000 FRelation := Add_Node(Relation, Field);
4001 Result := false;
4002 FField := FRelation.FieldNodes;
4003 while Assigned(FField) do
4004 if FField.FieldName = Field then
4005 begin
4006 Result := Ffield.IDENTITY_COLUMN;
4007 Exit;
4008 end
4009 else
4010 FField := Ffield.NextField;
4011 end;
4012
4013 Procedure FreeNodes;
4014 var
4015 FRelation : TRelationNode;
4016 FField : TFieldNode;
4017 begin
4018 while Assigned(FRelationNodes) do
4019 begin
4020 While Assigned(FRelationNodes.FieldNodes) do
4021 begin
4022 FField := FRelationNodes.FieldNodes.NextField;
4023 FRelationNodes.FieldNodes.Free;
4024 FRelationNodes.FieldNodes := FField;
4025 end;
4026 FRelation := FRelationNodes.NextRelation;
4027 FRelationNodes.Free;
4028 FRelationNodes := FRelation;
4029 end;
4030 end;
4031
4032 begin
4033 FRelationNodes := TRelationNode.Create;
4034 FNeedsRefresh := False;
4035 if not Database.InternalTransaction.InTransaction then
4036 Database.InternalTransaction.StartTransaction;
4037 Query := TIBSQL.Create(self);
4038 try
4039 Query.Database := DataBase;
4040 Query.Transaction := Database.InternalTransaction;
4041 FieldDefs.BeginUpdate;
4042 FieldDefs.Clear;
4043 FieldIndex := 0;
4044 if (Length(FMappedFieldPosition) < SourceQuery.MetaData.Count) then
4045 SetLength(FMappedFieldPosition, SourceQuery.MetaData.Count);
4046 if FDatabaseInfo.ODSMajorVersion >= 12 then
4047 Query.SQL.Text := DefaultSQLODS12
4048 else
4049 Query.SQL.Text := DefaultSQL;
4050 Query.Prepare;
4051 SetLength(FAliasNameMap, SourceQuery.MetaData.Count);
4052 SetLength(FAliasNameList, SourceQuery.MetaData.Count);
4053 for i := 0 to SourceQuery.MetaData.GetCount - 1 do
4054 with SourceQuery.MetaData[i] do
4055 begin
4056 { Get the field name }
4057 FieldAliasName := GetName;
4058 DBAliasName := GetAliasname;
4059 aRelationName := getRelationName;
4060 FieldName := getSQLName;
4061 FAliasNameList[i] := DBAliasName;
4062 FieldSize := 0;
4063 FieldDataSize := GetSize;
4064 FieldPrecision := 0;
4065 FieldNullable := IsNullable;
4066 CharSetSize := 0;
4067 CharSetName := '';
4068 FieldCodePage := CP_NONE;
4069 aArrayDimensions := 0;
4070 SetLength(aArrayBounds,0);
4071 case SQLType of
4072 { All VARCHAR's must be converted to strings before recording
4073 their values }
4074 SQL_VARYING, SQL_TEXT:
4075 begin
4076 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
4077 CharSetSize := 1;
4078 CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
4079 Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
4080 FieldSize := FieldDataSize div CharSetSize;
4081 FieldType := ftString;
4082 end;
4083 { All Doubles/Floats should be cast to doubles }
4084 SQL_DOUBLE, SQL_FLOAT:
4085 FieldType := ftFloat;
4086 SQL_SHORT:
4087 begin
4088 if (getScale = 0) then
4089 FieldType := ftSmallInt
4090 else begin
4091 FieldType := ftBCD;
4092 FieldPrecision := 4;
4093 FieldSize := -getScale;
4094 end;
4095 end;
4096 SQL_LONG:
4097 begin
4098 if (getScale = 0) then
4099 FieldType := ftInteger
4100 else if (getScale >= (-4)) then
4101 begin
4102 FieldType := ftBCD;
4103 FieldPrecision := 9;
4104 FieldSize := -getScale;
4105 end
4106 else
4107 if Database.SQLDialect = 1 then
4108 FieldType := ftFloat
4109 else
4110 if (FieldCount > i) and (Fields[i] is TFloatField) then
4111 FieldType := ftFloat
4112 else
4113 begin
4114 FieldType := ftFMTBCD;
4115 FieldPrecision := 9;
4116 FieldSize := -getScale;
4117 end;
4118 end;
4119
4120 SQL_INT64:
4121 begin
4122 if (getScale = 0) then
4123 FieldType := ftLargeInt
4124 else if (getScale >= (-4)) then
4125 begin
4126 FieldType := ftBCD;
4127 FieldPrecision := 18;
4128 FieldSize := -getScale;
4129 end
4130 else
4131 FieldType := ftFloat;
4132 end;
4133 SQL_TIMESTAMP: FieldType := ftDateTime;
4134 SQL_TYPE_TIME: FieldType := ftTime;
4135 SQL_TYPE_DATE: FieldType := ftDate;
4136 SQL_BLOB:
4137 begin
4138 FieldSize := sizeof (TISC_QUAD);
4139 if (getSubtype = 1) then
4140 begin
4141 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
4142 CharSetSize := 1;
4143 CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
4144 Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
4145 FieldType := ftMemo;
4146 end
4147 else
4148 FieldType := ftBlob;
4149 end;
4150 SQL_ARRAY:
4151 begin
4152 FieldSize := sizeof (TISC_QUAD);
4153 FieldType := ftArray;
4154 ArrayMetaData := GetArrayMetaData;
4155 if ArrayMetaData <> nil then
4156 begin
4157 aArrayDimensions := ArrayMetaData.GetDimensions;
4158 aArrayBounds := ArrayMetaData.GetBounds;
4159 end;
4160 end;
4161 SQL_BOOLEAN:
4162 FieldType:= ftBoolean;
4163 else
4164 FieldType := ftUnknown;
4165 end;
4166 FieldPosition := i + 1;
4167 if (FieldType <> ftUnknown) and (FieldAliasName <> 'IBX_INTERNAL_DBKEY') then {do not localize}
4168 begin
4169 FMappedFieldPosition[FieldIndex] := FieldPosition;
4170 Inc(FieldIndex);
4171 with TIBFieldDef.Create(FieldDefs,'',FieldType,0,False,FieldDefs.Count+1) do
4172 begin
4173 Name := FieldAliasName;
4174 FAliasNameMap[FieldNo-1] := DBAliasName;
4175 Size := FieldSize;
4176 DataSize := FieldDataSize;
4177 Precision := FieldPrecision;
4178 Required := not FieldNullable;
4179 RelationName := aRelationName;
4180 InternalCalcField := False;
4181 CharacterSetSize := CharSetSize;
4182 CharacterSetName := CharSetName;
4183 CodePage := FieldCodePage;
4184 ArrayDimensions := aArrayDimensions;
4185 ArrayBounds := aArrayBounds;
4186 if (FieldName <> '') and (RelationName <> '') then
4187 begin
4188 IdentityColumn := Is_IDENTITY_COLUMN(RelationName, FieldName);
4189 if Has_COMPUTED_BLR(RelationName, FieldName) then
4190 begin
4191 Attributes := [faReadOnly];
4192 InternalCalcField := True;
4193 FNeedsRefresh := True;
4194 end
4195 else
4196 begin
4197 if Has_DEFAULT_VALUE(RelationName, FieldName) then
4198 begin
4199 if not FieldNullable then
4200 Attributes := [faRequired];
4201 end
4202 else
4203 FNeedsRefresh := True;
4204 end;
4205 end;
4206 end;
4207 end;
4208 end;
4209 finally
4210 Query.free;
4211 FreeNodes;
4212 Database.InternalTransaction.Commit;
4213 FieldDefs.EndUpdate;
4214 FieldDefs.Updated := true;
4215 end;
4216 end;
4217
4218 procedure TIBCustomDataSet.InternalInitRecord(Buffer: PChar);
4219 begin
4220 CopyRecordBuffer(FModelBuffer, Buffer);
4221 end;
4222
4223 procedure TIBCustomDataSet.InternalLast;
4224 var
4225 Buffer: PChar;
4226 begin
4227 if (FQSelect.EOF) then
4228 FCurrentRecord := FRecordCount
4229 else begin
4230 Buffer := AllocRecordBuffer;
4231 try
4232 while FQSelect.Next do
4233 begin
4234 FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
4235 Inc(FRecordCount);
4236 end;
4237 FCurrentRecord := FRecordCount;
4238 finally
4239 FreeRecordBuffer(Buffer);
4240 end;
4241 end;
4242 end;
4243
4244 procedure TIBCustomDataSet.InternalSetParamsFromCursor;
4245 var
4246 i: Integer;
4247 cur_param: ISQLParam;
4248 cur_field: TField;
4249 s: TStream;
4250 begin
4251 if FQSelect.SQL.Text = '' then
4252 IBError(ibxeEmptyQuery, [nil]);
4253 if not FInternalPrepared then
4254 InternalPrepare;
4255 if (SQLParams.GetCount > 0) and (DataSource <> nil) and (DataSource.DataSet <> nil) then
4256 begin
4257 for i := 0 to SQLParams.GetCount - 1 do
4258 begin
4259 cur_field := DataSource.DataSet.FindField(SQLParams[i].Name);
4260 if (cur_field <> nil) then
4261 begin
4262 cur_param := SQLParams[i];
4263 if (cur_field.IsNull) then
4264 cur_param.IsNull := True
4265 else
4266 case cur_field.DataType of
4267 ftString:
4268 cur_param.AsString := cur_field.AsString;
4269 ftBoolean:
4270 cur_param.AsBoolean := cur_field.AsBoolean;
4271 ftSmallint, ftWord:
4272 cur_param.AsShort := cur_field.AsInteger;
4273 ftInteger:
4274 cur_param.AsLong := cur_field.AsInteger;
4275 ftLargeInt:
4276 cur_param.AsInt64 := cur_field.AsLargeInt;
4277 ftFloat, ftCurrency:
4278 cur_param.AsDouble := cur_field.AsFloat;
4279 ftBCD:
4280 cur_param.AsCurrency := cur_field.AsCurrency;
4281 ftDate:
4282 cur_param.AsDate := cur_field.AsDateTime;
4283 ftTime:
4284 cur_param.AsTime := cur_field.AsDateTime;
4285 ftDateTime:
4286 cur_param.AsDateTime := cur_field.AsDateTime;
4287 ftBlob, ftMemo:
4288 begin
4289 s := nil;
4290 try
4291 s := DataSource.DataSet.
4292 CreateBlobStream(cur_field, bmRead);
4293 cur_param.AsBlob := TIBDSBlobStream(s).FBlobStream.Blob;
4294 finally
4295 s.free;
4296 end;
4297 end;
4298 ftArray:
4299 cur_param.AsArray := TIBArrayField(cur_field).ArrayIntf;
4300 else
4301 IBError(ibxeNotSupported, [nil]);
4302 end;
4303 end;
4304 end;
4305 end;
4306 end;
4307
4308 procedure TIBCustomDataSet.ReQuery;
4309 begin
4310 FQSelect.Close;
4311 ClearBlobCache;
4312 FCurrentRecord := -1;
4313 FRecordCount := 0;
4314 FDeletedRecords := 0;
4315 FBPos := 0;
4316 FOBPos := 0;
4317 FBEnd := 0;
4318 FOBEnd := 0;
4319 FQSelect.Close;
4320 FQSelect.ExecQuery;
4321 FOpen := FQSelect.Open;
4322 First;
4323 end;
4324
4325 procedure TIBCustomDataSet.InternalOpen;
4326
4327 function RecordDataLength(n: Integer): Long;
4328 begin
4329 result := SizeOf(TRecordData) + ((n - 1) * SizeOf(TFieldData));
4330 end;
4331
4332 begin
4333 FBase.SetCursor;
4334 try
4335 ActivateConnection;
4336 ActivateTransaction;
4337 if FQSelect.SQL.Text = '' then
4338 IBError(ibxeEmptyQuery, [nil]);
4339 if not FInternalPrepared then
4340 InternalPrepare;
4341 if FQSelect.Statement <> nil then
4342 FQSelect.Statement.EnableStatistics(FEnableStatistics);
4343 if FQSelect.SQLStatementType = SQLSelect then
4344 begin
4345 if DefaultFields then
4346 CreateFields;
4347 FArrayFieldCount := 0;
4348 BindFields(True);
4349 FCurrentRecord := -1;
4350 FQSelect.ExecQuery;
4351 FOpen := FQSelect.Open;
4352
4353 { Initialize offsets, buffer sizes, etc...
4354 1. Initially FRecordSize is just the "RecordDataLength".
4355 2. Allocate a "model" buffer and do a dummy fetch
4356 3. After the dummy fetch, FRecordSize will be appropriately
4357 adjusted to reflect the additional "weight" of the field
4358 data.
4359 4. Set up the FCalcFieldsOffset, FBlobCacheOffset, FArrayCacheOffset and FRecordBufferSize.
4360 5. Now, with the BufferSize available, allocate memory for chunks of records
4361 6. Re-allocate the model buffer, accounting for the new
4362 FRecordBufferSize.
4363 7. Finally, calls to AllocRecordBuffer will work!.
4364 }
4365 {Step 1}
4366 FRecordSize := RecordDataLength(FQSelect.FieldCount);
4367 {Step 2, 3}
4368 GetMem(FFieldColumns,sizeof(TFieldColumns) * (FQSelect.FieldCount));
4369 IBAlloc(FModelBuffer, 0, FRecordSize);
4370 InitModelBuffer(FQSelect, FModelBuffer);
4371 {Step 4}
4372 FCalcFieldsOffset := FRecordSize;
4373 FBlobCacheOffset := FCalcFieldsOffset + CalcFieldsSize;
4374 FArrayCacheOffset := (FBlobCacheOffset + (BlobFieldCount * SizeOf(TIBBlobStream)));
4375 FRecordBufferSize := FArrayCacheOffset + (ArrayFieldCount * sizeof(IArray));
4376 {Step 5}
4377 if UniDirectional then
4378 FBufferChunkSize := FRecordBufferSize * UniCache
4379 else
4380 FBufferChunkSize := FRecordBufferSize * BufferChunks;
4381 IBAlloc(FBufferCache, FBufferChunkSize, FBufferChunkSize);
4382 if FCachedUpdates or (csReading in ComponentState) then
4383 IBAlloc(FOldBufferCache, FBufferChunkSize, FBufferChunkSize);
4384 FBPos := 0;
4385 FOBPos := 0;
4386 FBEnd := 0;
4387 FOBEnd := 0;
4388 FCacheSize := FBufferChunkSize;
4389 FOldCacheSize := FBufferChunkSize;
4390 {Step 6}
4391 IBAlloc(FModelBuffer, RecordDataLength(FQSelect.FieldCount),
4392 FRecordBufferSize);
4393 {Step 7}
4394 FOldBuffer := AllocRecordBuffer;
4395 end
4396 else
4397 FQSelect.ExecQuery;
4398 finally
4399 FBase.RestoreCursor;
4400 end;
4401 end;
4402
4403 procedure TIBCustomDataSet.InternalPost;
4404 var
4405 Qry: TIBSQL;
4406 Buff: PChar;
4407 bInserting: Boolean;
4408 begin
4409 FBase.SetCursor;
4410 try
4411 Buff := GetActiveBuf;
4412 CheckEditState;
4413 AdjustRecordOnInsert(Buff);
4414 if (State = dsInsert) then
4415 begin
4416 bInserting := True;
4417 Qry := FQInsert;
4418 PRecordData(Buff)^.rdUpdateStatus := usInserted;
4419 PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4420 WriteRecordCache(FRecordCount, Buff);
4421 FCurrentRecord := FRecordCount;
4422 end
4423 else begin
4424 bInserting := False;
4425 Qry := FQModify;
4426 if PRecordData(Buff)^.rdCachedUpdateStatus = cusUnmodified then
4427 begin
4428 PRecordData(Buff)^.rdUpdateStatus := usModified;
4429 PRecordData(Buff)^.rdCachedUpdateStatus := cusModified;
4430 end
4431 else if PRecordData(Buff)^.
4432 rdCachedUpdateStatus = cusUninserted then
4433 begin
4434 PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4435 Dec(FDeletedRecords);
4436 end;
4437 end;
4438 if (not CachedUpdates) then
4439 InternalPostRecord(Qry, Buff)
4440 else begin
4441 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4442 FUpdatesPending := True;
4443 end;
4444 if bInserting then
4445 Inc(FRecordCount);
4446 finally
4447 FBase.RestoreCursor;
4448 end;
4449 end;
4450
4451 procedure TIBCustomDataSet.InternalRefresh;
4452 begin
4453 inherited InternalRefresh;
4454 InternalRefreshRow;
4455 end;
4456
4457 procedure TIBCustomDataSet.InternalSetToRecord(Buffer: PChar);
4458 begin
4459 InternalGotoBookmark(@(PRecordData(Buffer)^.rdRecordNumber));
4460 end;
4461
4462 function TIBCustomDataSet.IsCursorOpen: Boolean;
4463 begin
4464 result := FOpen;
4465 end;
4466
4467 procedure TIBCustomDataSet.Loaded;
4468 begin
4469 if assigned(FQSelect) then
4470 FBaseSQLSelect.assign(FQSelect.SQL);
4471 inherited Loaded;
4472 end;
4473
4474 procedure TIBCustomDataSet.Post;
4475 var CancelPost: boolean;
4476 begin
4477 CancelPost := false;
4478 if assigned(FOnValidatePost) then
4479 OnValidatePost(self,CancelPost);
4480 if CancelPost then
4481 Cancel
4482 else
4483 inherited Post;
4484 end;
4485
4486 function TIBCustomDataSet.Locate(const KeyFields: string; const KeyValues: Variant;
4487 Options: TLocateOptions): Boolean;
4488 var
4489 CurBookmark: TBookmark;
4490 begin
4491 DisableControls;
4492 try
4493 CurBookmark := Bookmark;
4494 First;
4495 result := InternalLocate(KeyFields, KeyValues, Options);
4496 if not result then
4497 Bookmark := CurBookmark;
4498 finally
4499 EnableControls;
4500 end;
4501 end;
4502
4503 function TIBCustomDataSet.Lookup(const KeyFields: string; const KeyValues: Variant;
4504 const ResultFields: string): Variant;
4505 var
4506 fl: TList;
4507 CurBookmark: TBookmark;
4508 begin
4509 DisableControls;
4510 fl := TList.Create;
4511 CurBookmark := Bookmark;
4512 try
4513 First;
4514 if InternalLocate(KeyFields, KeyValues, []) then
4515 begin
4516 if (ResultFields <> '') then
4517 result := FieldValues[ResultFields]
4518 else
4519 result := NULL;
4520 end
4521 else
4522 result := Null;
4523 finally
4524 Bookmark := CurBookmark;
4525 fl.Free;
4526 EnableControls;
4527 end;
4528 end;
4529
4530 procedure TIBCustomDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
4531 begin
4532 PRecordData(Buffer)^.rdRecordNumber := PInteger(Data)^;
4533 end;
4534
4535 procedure TIBCustomDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
4536 begin
4537 PRecordData(Buffer)^.rdBookmarkFlag := Value;
4538 end;
4539
4540 procedure TIBCustomDataSet.SetCachedUpdates(Value: Boolean);
4541 begin
4542 if not Value and FCachedUpdates then
4543 CancelUpdates;
4544 if (not (csReading in ComponentState)) and Value then
4545 CheckDatasetClosed;
4546 FCachedUpdates := Value;
4547 end;
4548
4549 procedure TIBCustomDataSet.SetDataSource(Value: TDataSource);
4550 begin
4551 if IsLinkedTo(Value) then
4552 IBError(ibxeCircularReference, [nil]);
4553 if FDataLink <> nil then
4554 FDataLink.DataSource := Value;
4555 end;
4556
4557 procedure TIBCustomDataSet.InternalSetFieldData(Field: TField; Buffer: Pointer);
4558 var
4559 Buff, TmpBuff: PChar;
4560 MappedFieldPos: integer;
4561 begin
4562 Buff := GetActiveBuf;
4563 if Field.FieldNo < 0 then
4564 begin
4565 TmpBuff := Buff + FRecordSize + Field.Offset;
4566 Boolean(TmpBuff[0]) := LongBool(Buffer);
4567 if Boolean(TmpBuff[0]) then
4568 Move(Buffer^, TmpBuff[1], Field.DataSize);
4569 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4570 end
4571 else begin
4572 CheckEditState;
4573 with PRecordData(Buff)^ do
4574 begin
4575 { If inserting, Adjust record position }
4576 AdjustRecordOnInsert(Buff);
4577 MappedFieldPos := FMappedFieldPosition[Field.FieldNo - 1];
4578 if (MappedFieldPos > 0) and
4579 (MappedFieldPos <= rdFieldCount) then
4580 with rdFields[MappedFieldPos], FFieldColumns^[MappedFieldPos] do
4581 begin
4582 Field.Validate(Buffer);
4583 if (Buffer = nil) or
4584 (Field is TIBStringField) and (PChar(Buffer)[0] = #0) then
4585 fdIsNull := True
4586 else
4587 begin
4588 Move(Buffer^, Buff[fdDataOfs],fdDataSize);
4589 if (fdDataType = SQL_TEXT) or (fdDataType = SQL_VARYING) then
4590 fdDataLength := StrLen(PChar(Buffer));
4591 fdIsNull := False;
4592 if rdUpdateStatus = usUnmodified then
4593 begin
4594 if CachedUpdates then
4595 begin
4596 FUpdatesPending := True;
4597 if State = dsInsert then
4598 rdCachedUpdateStatus := cusInserted
4599 else if State = dsEdit then
4600 rdCachedUpdateStatus := cusModified;
4601 end;
4602
4603 if State = dsInsert then
4604 rdUpdateStatus := usInserted
4605 else
4606 rdUpdateStatus := usModified;
4607 end;
4608 WriteRecordCache(rdRecordNumber, Buff);
4609 SetModified(True);
4610 end;
4611 end;
4612 end;
4613 end;
4614 if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
4615 DataEvent(deFieldChange, PtrInt(Field));
4616 end;
4617
4618 procedure TIBCustomDataSet.SetRecNo(Value: Integer);
4619 begin
4620 CheckBrowseMode;
4621 if (Value < 1) then
4622 Value := 1
4623 else if Value > FRecordCount then
4624 begin
4625 InternalLast;
4626 Value := Min(FRecordCount, Value);
4627 end;
4628 if (Value <> RecNo) then
4629 begin
4630 DoBeforeScroll;
4631 FCurrentRecord := Value - 1;
4632 Resync([]);
4633 DoAfterScroll;
4634 end;
4635 end;
4636
4637 procedure TIBCustomDataSet.Disconnect;
4638 begin
4639 Close;
4640 InternalUnPrepare;
4641 end;
4642
4643 procedure TIBCustomDataSet.SetUpdateMode(const Value: TUpdateMode);
4644 begin
4645 if not CanModify then
4646 IBError(ibxeCannotUpdate, [nil])
4647 else
4648 FUpdateMode := Value;
4649 end;
4650
4651
4652 procedure TIBCustomDataSet.SetUpdateObject(Value: TIBDataSetUpdateObject);
4653 begin
4654 if Value <> FUpdateObject then
4655 begin
4656 if Assigned(FUpdateObject) and (FUpdateObject.DataSet = Self) then
4657 FUpdateObject.DataSet := nil;
4658 FUpdateObject := Value;
4659 if Assigned(FUpdateObject) then
4660 begin
4661 if Assigned(FUpdateObject.DataSet) and
4662 (FUpdateObject.DataSet <> Self) then
4663 FUpdateObject.DataSet.UpdateObject := nil;
4664 FUpdateObject.DataSet := Self;
4665 end;
4666 end;
4667 end;
4668
4669 function TIBCustomDataSet.ConstraintsStored: Boolean;
4670 begin
4671 Result := Constraints.Count > 0;
4672 end;
4673
4674 procedure TIBCustomDataSet.ClearCalcFields(Buffer: PChar);
4675 begin
4676 FillChar(Buffer[FRecordSize], CalcFieldsSize, 0);
4677 end;
4678
4679 procedure TIBCustomDataSet.ClearIBLinks;
4680 var i: integer;
4681 begin
4682 for i := FIBLinks.Count - 1 downto 0 do
4683 TIBControlLink(FIBLinks[i]).IBDataSet := nil;
4684 end;
4685
4686
4687 procedure TIBCustomDataSet.InternalUnPrepare;
4688 begin
4689 if FInternalPrepared then
4690 begin
4691 CheckDatasetClosed;
4692 if FDidActivate then
4693 DeactivateTransaction;
4694 FieldDefs.Clear;
4695 FieldDefs.Updated := false;
4696 FInternalPrepared := False;
4697 Setlength(FAliasNameList,0);
4698 end;
4699 end;
4700
4701 procedure TIBCustomDataSet.InternalExecQuery;
4702 var
4703 DidActivate: Boolean;
4704 begin
4705 DidActivate := False;
4706 FBase.SetCursor;
4707 try
4708 ActivateConnection;
4709 DidActivate := ActivateTransaction;
4710 if FQSelect.SQL.Text = '' then
4711 IBError(ibxeEmptyQuery, [nil]);
4712 if not FInternalPrepared then
4713 InternalPrepare;
4714 if FQSelect.SQLStatementType = SQLSelect then
4715 begin
4716 IBError(ibxeIsASelectStatement, [nil]);
4717 end
4718 else
4719 FQSelect.ExecQuery;
4720 finally
4721 if DidActivate then
4722 DeactivateTransaction;
4723 FBase.RestoreCursor;
4724 end;
4725 end;
4726
4727 function TIBCustomDataSet.GetSelectStmtIntf: IStatement;
4728 begin
4729 Result := FQSelect.Statement;
4730 end;
4731
4732 procedure TIBCustomDataSet.SetMasterDetailDelay(AValue: integer);
4733 begin
4734 FDataLink.DelayTimerValue := AValue;
4735 end;
4736
4737 function TIBCustomDataSet.GetParser: TSelectSQLParser;
4738 begin
4739 if not assigned(FParser) then
4740 FParser := CreateParser;
4741 Result := FParser
4742 end;
4743
4744 procedure TIBCustomDataSet.ResetParser;
4745 begin
4746 if assigned(FParser) then
4747 begin
4748 FParser.Free;
4749 FParser := nil;
4750 FQSelect.OnSQLChanged := nil; {Do not react to change}
4751 try
4752 FQSelect.SQL.Assign(FBaseSQLSelect);
4753 finally
4754 FQSelect.OnSQLChanged := SQLChanged;
4755 end;
4756 end;
4757 end;
4758
4759 function TIBCustomDataSet.HasParser: boolean;
4760 begin
4761 Result := not (csDesigning in ComponentState) and (FParser <> nil)
4762 end;
4763
4764 procedure TIBCustomDataSet.SetGenerateParamNames(AValue: Boolean);
4765 begin
4766 if FGenerateParamNames = AValue then Exit;
4767 FGenerateParamNames := AValue;
4768 Disconnect
4769 end;
4770
4771 procedure TIBCustomDataSet.InitRecord(Buffer: PChar);
4772 begin
4773 inherited InitRecord(Buffer);
4774 with PRecordData(Buffer)^ do
4775 begin
4776 rdUpdateStatus := TUpdateStatus(usInserted);
4777 rdBookMarkFlag := bfInserted;
4778 rdRecordNumber := -1;
4779 end;
4780 end;
4781
4782 procedure TIBCustomDataSet.InternalInsert;
4783 begin
4784 CursorPosChanged;
4785 end;
4786
4787 { TIBDataSet IProviderSupport }
4788
4789 procedure TIBCustomDataSet.PSEndTransaction(Commit: Boolean);
4790 begin
4791 if Commit then
4792 Transaction.Commit else
4793 Transaction.Rollback;
4794 end;
4795
4796 function TIBCustomDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams;
4797 ResultSet: Pointer = nil): Integer;
4798 var
4799 FQuery: TIBQuery;
4800 begin
4801 if Assigned(ResultSet) then
4802 begin
4803 TDataSet(ResultSet^) := TIBQuery.Create(nil);
4804 with TIBQuery(ResultSet^) do
4805 begin
4806 SQL.Text := ASQL;
4807 Params.Assign(AParams);
4808 Open;
4809 Result := RowsAffected;
4810 end;
4811 end
4812 else
4813 begin
4814 FQuery := TIBQuery.Create(nil);
4815 try
4816 FQuery.Database := Database;
4817 FQuery.Transaction := Transaction;
4818 FQuery.GenerateParamNames := True;
4819 FQuery.SQL.Text := ASQL;
4820 FQuery.Params.Assign(AParams);
4821 FQuery.ExecSQL;
4822 Result := FQuery.RowsAffected;
4823 finally
4824 FQuery.Free;
4825 end;
4826 end;
4827 end;
4828
4829 function TIBCustomDataSet.PSGetQuoteChar: string;
4830 begin
4831 if Database.SQLDialect = 3 then
4832 Result := '"' else
4833 Result := '';
4834 end;
4835
4836 function TIBCustomDataSet.PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError;
4837 var
4838 PrevErr: Integer;
4839 begin
4840 if Prev <> nil then
4841 PrevErr := Prev.ErrorCode else
4842 PrevErr := 0;
4843 if E is EIBError then
4844 with EIBError(E) do
4845 Result := EUpdateError.Create(E.Message, '', SQLCode, PrevErr, E) else
4846 Result := inherited PSGetUpdateException(E, Prev);
4847 end;
4848
4849 function TIBCustomDataSet.PSInTransaction: Boolean;
4850 begin
4851 Result := Transaction.InTransaction;
4852 end;
4853
4854 function TIBCustomDataSet.PSIsSQLBased: Boolean;
4855 begin
4856 Result := True;
4857 end;
4858
4859 function TIBCustomDataSet.PSIsSQLSupported: Boolean;
4860 begin
4861 Result := True;
4862 end;
4863
4864 procedure TIBCustomDataSet.PSReset;
4865 begin
4866 inherited PSReset;
4867 if Active then
4868 begin
4869 Close;
4870 Open;
4871 end;
4872 end;
4873
4874 function TIBCustomDataSet.PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean;
4875 var
4876 UpdateAction: TIBUpdateAction;
4877 SQL: string;
4878 Params: TParams;
4879
4880 procedure AssignParams(DataSet: TDataSet; Params: TParams);
4881 var
4882 I: Integer;
4883 Old: Boolean;
4884 Param: TParam;
4885 PName: string;
4886 Field: TField;
4887 Value: Variant;
4888 begin
4889 for I := 0 to Params.Count - 1 do
4890 begin
4891 Param := Params[I];
4892 PName := Param.Name;
4893 Old := CompareText(Copy(PName, 1, 4), 'OLD_') = 0; {do not localize}
4894 if Old then System.Delete(PName, 1, 4);
4895 Field := DataSet.FindField(PName);
4896 if not Assigned(Field) then Continue;
4897 if Old then Param.AssignFieldValue(Field, Field.OldValue) else
4898 begin
4899 Value := Field.NewValue;
4900 if VarIsEmpty(Value) then Value := Field.OldValue;
4901 Param.AssignFieldValue(Field, Value);
4902 end;
4903 end;
4904 end;
4905
4906 begin
4907 Result := False;
4908 if Assigned(OnUpdateRecord) then
4909 begin
4910 UpdateAction := uaFail;
4911 if Assigned(FOnUpdateRecord) then
4912 begin
4913 FOnUpdateRecord(Delta, UpdateKind, UpdateAction);
4914 Result := UpdateAction = uaApplied;
4915 end;
4916 end
4917 else if Assigned(FUpdateObject) then
4918 begin
4919 SQL := FUpdateObject.GetSQL(UpdateKind).Text;
4920 if SQL <> '' then
4921 begin
4922 Params := TParams.Create;
4923 try
4924 Params.ParseSQL(SQL, True);
4925 AssignParams(Delta, Params);
4926 if PSExecuteStatement(SQL, Params) = 0 then
4927 IBError(ibxeNoRecordsAffected, [nil]);
4928 Result := True;
4929 finally
4930 Params.Free;
4931 end;
4932 end;
4933 end;
4934 end;
4935
4936 procedure TIBCustomDataSet.PSStartTransaction;
4937 begin
4938 ActivateConnection;
4939 Transaction.StartTransaction;
4940 end;
4941
4942 function TIBCustomDataSet.PsGetTableName: string;
4943 begin
4944 // if not FInternalPrepared then
4945 // InternalPrepare;
4946 { It is possible for the FQSelectSQL to be unprepared
4947 with FInternalPreprepared being true (see DoBeforeTransactionEnd).
4948 So check the Prepared of the SelectSQL instead }
4949 if not FQSelect.Prepared then
4950 FQSelect.Prepare;
4951 Result := FQSelect.UniqueRelationName;
4952 end;
4953
4954 procedure TIBDataSet.BatchInput(InputObject: TIBBatchInput);
4955 begin
4956 InternalBatchInput(InputObject);
4957 end;
4958
4959 procedure TIBDataSet.BatchOutput(OutputObject: TIBBatchOutput);
4960 begin
4961 InternalBatchOutput(OutputObject);
4962 end;
4963
4964 procedure TIBDataSet.ExecSQL;
4965 begin
4966 InternalExecQuery;
4967 end;
4968
4969 procedure TIBDataSet.Prepare;
4970 begin
4971 InternalPrepare;
4972 end;
4973
4974 procedure TIBDataSet.UnPrepare;
4975 begin
4976 InternalUnPrepare;
4977 end;
4978
4979 function TIBDataSet.GetPrepared: Boolean;
4980 begin
4981 Result := InternalPrepared;
4982 end;
4983
4984 procedure TIBDataSet.InternalOpen;
4985 begin
4986 ActivateConnection;
4987 ActivateTransaction;
4988 InternalSetParamsFromCursor;
4989 Inherited InternalOpen;
4990 end;
4991
4992 procedure TIBDataSet.SetFiltered(Value: Boolean);
4993 begin
4994 if(Filtered <> Value) then
4995 begin
4996 inherited SetFiltered(value);
4997 if Active then
4998 begin
4999 Close;
5000 Open;
5001 end;
5002 end
5003 else
5004 inherited SetFiltered(value);
5005 end;
5006
5007 function TIBCustomDataSet.BookmarkValid(Bookmark: TBookmark): Boolean;
5008 begin
5009 Result := false;
5010 if not Assigned(Bookmark) then
5011 exit;
5012 Result := PInteger(Bookmark)^ < FRecordCount;
5013 end;
5014
5015 function TIBCustomDataSet.GetFieldData(Field: TField;
5016 Buffer: Pointer): Boolean;
5017 {$IFDEF TBCDFIELD_IS_BCD}
5018 var
5019 lTempCurr : System.Currency;
5020 begin
5021 if (Field.DataType = ftBCD) and (Buffer <> nil) then
5022 begin
5023 Result := InternalGetFieldData(Field, @lTempCurr);
5024 if Result then
5025 CurrToBCD(lTempCurr, TBCD(Buffer^), 32, Field.Size);
5026 end
5027 else
5028 {$ELSE}
5029 begin
5030 {$ENDIF}
5031 Result := InternalGetFieldData(Field, Buffer);
5032 end;
5033
5034 function TIBCustomDataSet.GetFieldData(Field: TField; Buffer: Pointer;
5035 NativeFormat: Boolean): Boolean;
5036 begin
5037 {These datatypes use IBX conventions and not TDataset conventions}
5038 if (Field.DataType in [ftBCD,ftDateTime,ftDate,ftTime]) and not NativeFormat then
5039 Result := InternalGetFieldData(Field, Buffer)
5040 else
5041 Result := inherited GetFieldData(Field, Buffer, NativeFormat);
5042 end;
5043
5044 procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer);
5045 {$IFDEF TDBDFIELD_IS_BCD}
5046 var
5047 lTempCurr : System.Currency;
5048 begin
5049 if (Field.DataType = ftBCD) and (Buffer <> nil) then
5050 begin
5051 BCDToCurr(TBCD(Buffer^), lTempCurr);
5052 InternalSetFieldData(Field, @lTempCurr);
5053 end
5054 else
5055 {$ELSE}
5056 begin
5057 {$ENDIF}
5058 InternalSetFieldData(Field, Buffer);
5059 end;
5060
5061 procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer;
5062 NativeFormat: Boolean);
5063 begin
5064 {These datatypes use IBX conventions and not TDataset conventions}
5065 if (not NativeFormat) and (Field.DataType in [ftBCD,ftDateTime,ftDate,ftTime]) then
5066 InternalSetfieldData(Field, Buffer)
5067 else
5068 inherited SetFieldData(Field, buffer, NativeFormat);
5069 end;
5070
5071 { TIBDataSetUpdateObject }
5072
5073 constructor TIBDataSetUpdateObject.Create(AOwner: TComponent);
5074 begin
5075 inherited Create(AOwner);
5076 FRefreshSQL := TStringList.Create;
5077 end;
5078
5079 destructor TIBDataSetUpdateObject.Destroy;
5080 begin
5081 FRefreshSQL.Free;
5082 inherited Destroy;
5083 end;
5084
5085 function TIBDataSetUpdateObject.GetRowsAffected(
5086 var SelectCount, InsertCount, UpdateCount, DeleteCount: integer): boolean;
5087 begin
5088 SelectCount := 0;
5089 InsertCount := 0;
5090 UpdateCount := 0;
5091 DeleteCount := 0;
5092 end;
5093
5094 procedure TIBDataSetUpdateObject.SetRefreshSQL(value: TStrings);
5095 begin
5096 FRefreshSQL.Assign(Value);
5097 end;
5098
5099 procedure TIBDataSetUpdateObject.InternalSetParams(Params: ISQLParams;
5100 buff: PChar);
5101 begin
5102 if not Assigned(DataSet) then Exit;
5103 DataSet.SetInternalSQLParams(Params, buff);
5104 end;
5105
5106 procedure TIBDataSetUpdateObject.InternalSetParams(Query: TIBSQL; buff: PChar);
5107 begin
5108 InternalSetParams(Query.Params,buff);
5109 end;
5110
5111 procedure TIBDataSetUpdateObject.UpdateRecordFromQuery(UpdateKind: TUpdateKind;
5112 QryResults: IResults; Buffer: PChar);
5113 begin
5114 if not Assigned(DataSet) then Exit;
5115 case UpdateKind of
5116 ukModify, ukInsert:
5117 DataSet.UpdateRecordFromQuery(QryResults, Buffer);
5118 ukDelete:
5119 DataSet.DoDeleteReturning(QryResults);
5120 end;
5121 end;
5122
5123 function TIBDSBlobStream.GetSize: Int64;
5124 begin
5125 Result := FBlobStream.BlobSize;
5126 end;
5127
5128 { TIBDSBlobStream }
5129 constructor TIBDSBlobStream.Create(AField: TField; ABlobStream: TIBBlobStream;
5130 Mode: TBlobStreamMode);
5131 begin
5132 FField := AField;
5133 FBlobStream := ABlobStream;
5134 FBlobStream.Seek(0, soFromBeginning);
5135 if (Mode = bmWrite) then
5136 begin
5137 FBlobStream.Truncate;
5138 TIBCustomDataSet(FField.DataSet).RecordModified(True);
5139 TBlobField(FField).Modified := true;
5140 FHasWritten := true;
5141 end;
5142 end;
5143
5144 destructor TIBDSBlobStream.Destroy;
5145 begin
5146 if FHasWritten then
5147 TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
5148 inherited Destroy;
5149 end;
5150
5151 function TIBDSBlobStream.Read(var Buffer; Count: Longint): Longint;
5152 begin
5153 result := FBlobStream.Read(Buffer, Count);
5154 end;
5155
5156 function TIBDSBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
5157 begin
5158 result := FBlobStream.Seek(Offset, Origin);
5159 end;
5160
5161 procedure TIBDSBlobStream.SetSize(NewSize: Longint);
5162 begin
5163 FBlobStream.SetSize(NewSize);
5164 end;
5165
5166 function TIBDSBlobStream.Write(const Buffer; Count: Longint): Longint;
5167 begin
5168 if not (FField.DataSet.State in [dsEdit, dsInsert]) then
5169 IBError(ibxeNotEditing, [nil]);
5170 TIBCustomDataSet(FField.DataSet).RecordModified(True);
5171 TBlobField(FField).Modified := true;
5172 result := FBlobStream.Write(Buffer, Count);
5173 FHasWritten := true;
5174 { TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
5175 Removed as this caused a seek to beginning of the blob stream thus corrupting
5176 the blob stream. Moved to the destructor i.e. called after blob written}
5177 end;
5178
5179 { TIBGenerator }
5180
5181 procedure TIBGenerator.SetIncrement(const AValue: integer);
5182 begin
5183 if FIncrement = AValue then Exit;
5184 if AValue < 0 then
5185 IBError(ibxeNegativeGenerator,[]);
5186 FIncrement := AValue;
5187 SetQuerySQL;
5188 end;
5189
5190 procedure TIBGenerator.SetTransaction(AValue: TIBTransaction);
5191 begin
5192 FQuery.Transaction := AValue;
5193 end;
5194
5195 procedure TIBGenerator.SetQuerySQL;
5196 begin
5197 FQuery.SQL.Text := Format('Select Gen_ID(%s,%d) From RDB$Database',[FGeneratorName,Increment]);
5198 end;
5199
5200 function TIBGenerator.GetDatabase: TIBDatabase;
5201 begin
5202 Result := FQuery.Database;
5203 end;
5204
5205 function TIBGenerator.GetTransaction: TIBTransaction;
5206 begin
5207 Result := FQuery.Transaction;
5208 end;
5209
5210 procedure TIBGenerator.SetDatabase(AValue: TIBDatabase);
5211 begin
5212 FQuery.Database := AValue;
5213 end;
5214
5215 procedure TIBGenerator.SetGeneratorName(AValue: string);
5216 begin
5217 if FGeneratorName = AValue then Exit;
5218 FGeneratorName := AValue;
5219 SetQuerySQL;
5220 end;
5221
5222 function TIBGenerator.GetNextValue: integer;
5223 begin
5224 with FQuery do
5225 begin
5226 Transaction.Active := true;
5227 ExecQuery;
5228 try
5229 Result := Fields[0].AsInteger
5230 finally
5231 Close
5232 end;
5233 end;
5234 end;
5235
5236 constructor TIBGenerator.Create(Owner: TIBCustomDataSet);
5237 begin
5238 FOwner := Owner;
5239 FIncrement := 1;
5240 FQuery := TIBSQL.Create(nil);
5241 end;
5242
5243 destructor TIBGenerator.Destroy;
5244 begin
5245 if assigned(FQuery) then FQuery.Free;
5246 inherited Destroy;
5247 end;
5248
5249
5250 procedure TIBGenerator.Apply;
5251 begin
5252 if assigned(Database) and assigned(Transaction) and
5253 (FGeneratorName <> '') and (FFieldName <> '') and Owner.FieldByName(FFieldName).IsNull then
5254 Owner.FieldByName(FFieldName).AsInteger := GetNextValue;
5255 end;
5256
5257
5258 end.