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