ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 106
Committed: Thu Jan 18 14:37:35 2018 UTC (6 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 146605 byte(s)
Log Message:
Updates Merged

File Contents

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