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

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     LocalDate, LocalDouble: Double;
2078     LocalInt: Integer;
2079     LocalBool: wordBool;
2080     LocalInt64: Int64;
2081     LocalCurrency: Currency;
2082     p: PRecordData;
2083     ColData: ISQLData;
2084     begin
2085     p := PRecordData(Buffer);
2086     LocalData := nil;
2087     with p^.rdFields[FieldIndex], FFieldColumns^[FieldIndex] do
2088     begin
2089     QryResults.GetData(ColumnIndex,fdIsNull,fdDataLength,LocalData);
2090     if not fdIsNull then
2091     begin
2092     ColData := QryResults[ColumnIndex];
2093     case fdDataType of {Get Formatted data for column types that need formatting}
2094     SQL_TIMESTAMP:
2095     begin
2096     LocalDate := TimeStampToMSecs(DateTimeToTimeStamp(ColData.AsDateTime));
2097     LocalData := PByte(@LocalDate);
2098     end;
2099     SQL_TYPE_DATE:
2100     begin
2101     LocalInt := DateTimeToTimeStamp(ColData.AsDateTime).Date;
2102     LocalData := PByte(@LocalInt);
2103     end;
2104     SQL_TYPE_TIME:
2105     begin
2106     LocalInt := DateTimeToTimeStamp(ColData.AsDateTime).Time;
2107     LocalData := PByte(@LocalInt);
2108     end;
2109     SQL_SHORT, SQL_LONG:
2110     begin
2111     if (fdDataScale = 0) then
2112     begin
2113     LocalInt := ColData.AsLong;
2114     LocalData := PByte(@LocalInt);
2115     end
2116     else
2117     if (fdDataScale >= (-4)) then
2118     begin
2119     LocalCurrency := ColData.AsCurrency;
2120     LocalData := PByte(@LocalCurrency);
2121     end
2122     else
2123     begin
2124     LocalDouble := ColData.AsDouble;
2125     LocalData := PByte(@LocalDouble);
2126     end;
2127     end;
2128     SQL_INT64:
2129     begin
2130     if (fdDataScale = 0) then
2131     begin
2132     LocalInt64 := ColData.AsInt64;
2133     LocalData := PByte(@LocalInt64);
2134     end
2135     else
2136     if (fdDataScale >= (-4)) then
2137     begin
2138     LocalCurrency := ColData.AsCurrency;
2139     LocalData := PByte(@LocalCurrency);
2140     end
2141     else
2142     begin
2143     LocalDouble := ColData.AsDouble;
2144     LocalData := PByte(@LocalDouble);
2145     end
2146     end;
2147     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2148     begin
2149     LocalDouble := ColData.AsDouble;
2150     LocalData := PByte(@LocalDouble);
2151     end;
2152     SQL_BOOLEAN:
2153     begin
2154     LocalBool := ColData.AsBoolean;
2155     LocalData := PByte(@LocalBool);
2156     end;
2157     end;
2158    
2159     if fdDataType = SQL_VARYING then
2160     Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2161     else
2162     Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2163     end
2164     else {Null column}
2165     if fdDataType = SQL_VARYING then
2166     FillChar(Buffer[fdDataOfs],fdDataLength,0)
2167     else
2168     FillChar(Buffer[fdDataOfs],fdDataSize,0);
2169     end;
2170     end;
2171    
2172 tony 33 { Read the record from FQSelect.Current into the record buffer
2173     Then write the buffer to in memory cache }
2174     procedure TIBCustomDataSet.FetchCurrentRecordToBuffer(Qry: TIBSQL;
2175     RecordNumber: Integer; Buffer: PChar);
2176     var
2177     pbd: PBlobDataArray;
2178 tony 45 pda: PArrayDataArray;
2179 tony 33 i, j: Integer;
2180     FieldsLoaded: Integer;
2181 tony 45 p: PRecordData;
2182 tony 33 begin
2183 tony 45 if RecordNumber = -1 then
2184     begin
2185     InitModelBuffer(Qry,Buffer);
2186     Exit;
2187     end;
2188 tony 33 p := PRecordData(Buffer);
2189     { Make sure blob cache is empty }
2190     pbd := PBlobDataArray(Buffer + FBlobCacheOffset);
2191 tony 45 pda := PArrayDataArray(Buffer + FArrayCacheOffset);
2192     for i := 0 to BlobFieldCount - 1 do
2193     pbd^[i] := nil;
2194     for i := 0 to ArrayFieldCount - 1 do
2195     pda^[i] := nil;
2196    
2197 tony 33 { Get record information }
2198     p^.rdBookmarkFlag := bfCurrent;
2199 tony 45 p^.rdFieldCount := Qry.FieldCount;
2200 tony 33 p^.rdRecordNumber := RecordNumber;
2201     p^.rdUpdateStatus := usUnmodified;
2202     p^.rdCachedUpdateStatus := cusUnmodified;
2203     p^.rdSavedOffset := $FFFFFFFF;
2204    
2205     { Load up the fields }
2206 tony 45 FieldsLoaded := FQSelect.MetaData.Count;
2207 tony 33 j := 1;
2208 tony 45 for i := 0 to Qry.FieldCount - 1 do
2209 tony 33 begin
2210     if (Qry = FQSelect) then
2211     j := i + 1
2212 tony 45 else
2213     begin
2214 tony 33 if FieldsLoaded = 0 then
2215     break;
2216 tony 45 j := FQSelect.FieldIndex[Qry[i].Name] + 1;
2217 tony 33 if j < 1 then
2218     continue
2219     else
2220     Dec(FieldsLoaded);
2221     end;
2222 tony 45 with FQSelect.MetaData[j - 1] do
2223     if GetAliasname = 'IBX_INTERNAL_DBKEY' then {do not localize}
2224 tony 33 begin
2225 tony 45 if (GetSize <= 8) then
2226     p^.rdDBKey := PIBDBKEY(Qry[i].AsPointer)^;
2227 tony 33 continue;
2228     end;
2229 tony 45 if j > 0 then
2230 tony 101 ColumnDataToBuffer(Qry.Current,i,j,Buffer);
2231 tony 33 end;
2232 tony 45 WriteRecordCache(RecordNumber, Buffer);
2233 tony 33 end;
2234    
2235     function TIBCustomDataSet.GetActiveBuf: PChar;
2236     begin
2237     case State of
2238     dsBrowse:
2239     if IsEmpty then
2240     result := nil
2241     else
2242     result := ActiveBuffer;
2243     dsEdit, dsInsert:
2244     result := ActiveBuffer;
2245     dsCalcFields:
2246     result := CalcBuffer;
2247     dsFilter:
2248     result := FFilterBuffer;
2249     dsNewValue:
2250     result := ActiveBuffer;
2251     dsOldValue:
2252     if (PRecordData(ActiveBuffer)^.rdRecordNumber =
2253     PRecordData(FOldBuffer)^.rdRecordNumber) then
2254     result := FOldBuffer
2255     else
2256     result := ActiveBuffer;
2257     else if not FOpen then
2258     result := nil
2259     else
2260     result := ActiveBuffer;
2261     end;
2262     end;
2263    
2264     function TIBCustomDataSet.CachedUpdateStatus: TCachedUpdateStatus;
2265     begin
2266     if Active then
2267     result := PRecordData(GetActiveBuf)^.rdCachedUpdateStatus
2268     else
2269     result := cusUnmodified;
2270     end;
2271    
2272     function TIBCustomDataSet.GetDatabase: TIBDatabase;
2273     begin
2274     result := FBase.Database;
2275     end;
2276    
2277     function TIBCustomDataSet.GetDeleteSQL: TStrings;
2278     begin
2279     result := FQDelete.SQL;
2280     end;
2281    
2282     function TIBCustomDataSet.GetInsertSQL: TStrings;
2283     begin
2284     result := FQInsert.SQL;
2285     end;
2286    
2287 tony 45 function TIBCustomDataSet.GetSQLParams: ISQLParams;
2288 tony 33 begin
2289     if not FInternalPrepared then
2290     InternalPrepare;
2291     result := FQSelect.Params;
2292     end;
2293    
2294     function TIBCustomDataSet.GetRefreshSQL: TStrings;
2295     begin
2296     result := FQRefresh.SQL;
2297     end;
2298    
2299     function TIBCustomDataSet.GetSelectSQL: TStrings;
2300     begin
2301     result := FQSelect.SQL;
2302     end;
2303    
2304 tony 45 function TIBCustomDataSet.GetStatementType: TIBSQLStatementTypes;
2305 tony 33 begin
2306 tony 45 result := FQSelect.SQLStatementType;
2307 tony 33 end;
2308    
2309     function TIBCustomDataSet.GetModifySQL: TStrings;
2310     begin
2311     result := FQModify.SQL;
2312     end;
2313    
2314     function TIBCustomDataSet.GetTransaction: TIBTransaction;
2315     begin
2316     result := FBase.Transaction;
2317     end;
2318    
2319     procedure TIBCustomDataSet.InternalDeleteRecord(Qry: TIBSQL; Buff: Pointer);
2320     begin
2321     if (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukDelete).Text <> '')) then
2322     FUpdateObject.Apply(ukDelete,Buff)
2323     else
2324     begin
2325 tony 80 SetInternalSQLParams(FQDelete.Params, Buff);
2326 tony 33 FQDelete.ExecQuery;
2327     end;
2328     with PRecordData(Buff)^ do
2329     begin
2330     rdUpdateStatus := usDeleted;
2331     rdCachedUpdateStatus := cusUnmodified;
2332     end;
2333     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2334     end;
2335    
2336     function TIBCustomDataSet.InternalLocate(const KeyFields: string;
2337     const KeyValues: Variant; Options: TLocateOptions): Boolean;
2338     var
2339     keyFieldList: TList;
2340     CurBookmark: TBookmark;
2341     fieldValue: Variant;
2342     lookupValues: array of variant;
2343     i, fieldCount: Integer;
2344     fieldValueAsString: string;
2345     lookupValueAsString: string;
2346     begin
2347     keyFieldList := TList.Create;
2348     try
2349     GetFieldList(keyFieldList, KeyFields);
2350     fieldCount := keyFieldList.Count;
2351     CurBookmark := Bookmark;
2352     result := false;
2353     SetLength(lookupValues, fieldCount);
2354     if not EOF then
2355     begin
2356     for i := 0 to fieldCount - 1 do {expand key values into lookupValues array}
2357     begin
2358     if VarIsArray(KeyValues) then
2359     lookupValues[i] := KeyValues[i]
2360     else
2361     if i > 0 then
2362     lookupValues[i] := NULL
2363     else
2364     lookupValues[0] := KeyValues;
2365    
2366     {convert to upper case is case insensitive search}
2367     if (TField(keyFieldList[i]).DataType = ftString) and
2368     not VarIsNull(lookupValues[i]) and (loCaseInsensitive in Options) then
2369     lookupValues[i] := UpperCase(lookupValues[i]);
2370     end;
2371     end;
2372     while not result and not EOF do {search for a matching record}
2373     begin
2374     i := 0;
2375     result := true;
2376     while result and (i < fieldCount) do
2377     {see if all of the key fields matches}
2378     begin
2379     fieldValue := TField(keyFieldList[i]).Value;
2380     result := not (VarIsNull(fieldValue) xor VarIsNull(lookupValues[i]));
2381     if result and not VarIsNull(fieldValue) then
2382     begin
2383     try
2384     if TField(keyFieldList[i]).DataType = ftString then
2385     begin
2386     {strings need special handling because of the locate options that
2387     apply to them}
2388     fieldValueAsString := TField(keyFieldList[i]).AsString;
2389     lookupValueAsString := lookupValues[i];
2390     if (loCaseInsensitive in Options) then
2391     fieldValueAsString := UpperCase(fieldValueAsString);
2392    
2393     if (loPartialKey in Options) then
2394     result := result and (Pos(lookupValueAsString, fieldValueAsString) = 1)
2395     else
2396     result := result and (fieldValueAsString = lookupValueAsString);
2397     end
2398     else
2399     result := result and (lookupValues[i] =
2400     VarAsType(fieldValue, VarType(lookupValues[i])));
2401     except on EVariantError do
2402     result := False;
2403     end;
2404     end;
2405     Inc(i);
2406     end;
2407     if not result then
2408     Next;
2409     end;
2410     if not result then
2411     Bookmark := CurBookmark
2412     else
2413     CursorPosChanged;
2414     finally
2415     keyFieldList.Free;
2416     SetLength(lookupValues,0)
2417     end;
2418     end;
2419    
2420     procedure TIBCustomDataSet.InternalPostRecord(Qry: TIBSQL; Buff: Pointer);
2421     var
2422 tony 45 i, j, k, arr: Integer;
2423 tony 33 pbd: PBlobDataArray;
2424 tony 45 pda: PArrayDataArray;
2425 tony 33 begin
2426     pbd := PBlobDataArray(PChar(Buff) + FBlobCacheOffset);
2427 tony 45 pda := PArrayDataArray(PChar(Buff) + FArrayCacheOffset);
2428     j := 0; arr := 0;
2429 tony 33 for i := 0 to FieldCount - 1 do
2430     if Fields[i].IsBlob then
2431     begin
2432     k := FMappedFieldPosition[Fields[i].FieldNo -1];
2433     if pbd^[j] <> nil then
2434     begin
2435     pbd^[j].Finalize;
2436     PISC_QUAD(
2437 tony 45 PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ :=
2438 tony 33 pbd^[j].BlobID;
2439     PRecordData(Buff)^.rdFields[k].fdIsNull := pbd^[j].Size = 0;
2440 tony 45 end
2441     else
2442     begin
2443     PRecordData(Buff)^.rdFields[k].fdIsNull := true;
2444     with PISC_QUAD(PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ do
2445     begin
2446     gds_quad_high := 0;
2447     gds_quad_low := 0;
2448     end;
2449 tony 33 end;
2450     Inc(j);
2451 tony 45 end
2452     else
2453     if Fields[i] is TIBArrayField then
2454     begin
2455     if pda^[arr] <> nil then
2456     begin
2457     k := FMappedFieldPosition[Fields[i].FieldNo -1];
2458     PISC_QUAD(
2459     PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ := pda^[arr].ArrayIntf.GetArrayID;
2460     PRecordData(Buff)^.rdFields[k].fdIsNull := pda^[arr].ArrayIntf.IsEmpty;
2461     end;
2462     Inc(arr);
2463 tony 33 end;
2464     if Assigned(FUpdateObject) then
2465     begin
2466     if (Qry = FQDelete) then
2467     FUpdateObject.Apply(ukDelete,Buff)
2468     else if (Qry = FQInsert) then
2469     FUpdateObject.Apply(ukInsert,Buff)
2470     else
2471     FUpdateObject.Apply(ukModify,Buff);
2472     end
2473     else begin
2474 tony 80 SetInternalSQLParams(Qry.Params, Buff);
2475 tony 33 Qry.ExecQuery;
2476     end;
2477 tony 101 if Qry.FieldCount > 0 then {Has RETURNING Clause}
2478     UpdateRecordFromQuery(Qry.Current,Buff);
2479 tony 33 PRecordData(Buff)^.rdUpdateStatus := usUnmodified;
2480     PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
2481     SetModified(False);
2482     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2483     if (FForcedRefresh or FNeedsRefresh) and CanRefresh then
2484     InternalRefreshRow;
2485     end;
2486    
2487     procedure TIBCustomDataSet.InternalRefreshRow;
2488     var
2489     Buff: PChar;
2490     ofs: DWORD;
2491     Qry: TIBSQL;
2492     begin
2493     FBase.SetCursor;
2494     try
2495     Buff := GetActiveBuf;
2496     if CanRefresh then
2497     begin
2498     if Buff <> nil then
2499     begin
2500     if (Assigned(FUpdateObject) and (FUpdateObject.RefreshSQL.Text <> '')) then
2501     begin
2502     Qry := TIBSQL.Create(self);
2503     Qry.Database := Database;
2504     Qry.Transaction := Transaction;
2505     Qry.GoToFirstRecordOnExecute := False;
2506     Qry.SQL.Text := FUpdateObject.RefreshSQL.Text;
2507     end
2508     else
2509     Qry := FQRefresh;
2510 tony 80 SetInternalSQLParams(Qry.Params, Buff);
2511 tony 33 Qry.ExecQuery;
2512     try
2513 tony 45 if (Qry.SQLStatementType = SQLExecProcedure) or Qry.Next then
2514 tony 33 begin
2515     ofs := PRecordData(Buff)^.rdSavedOffset;
2516     FetchCurrentRecordToBuffer(Qry,
2517     PRecordData(Buff)^.rdRecordNumber,
2518     Buff);
2519     if FCachedUpdates and (ofs <> $FFFFFFFF) then
2520     begin
2521     PRecordData(Buff)^.rdSavedOffset := ofs;
2522     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2523     SaveOldBuffer(Buff);
2524     end;
2525     end;
2526     finally
2527     Qry.Close;
2528     end;
2529     if Qry <> FQRefresh then
2530     Qry.Free;
2531     end
2532     end
2533     else
2534     IBError(ibxeCannotRefresh, [nil]);
2535     finally
2536     FBase.RestoreCursor;
2537     end;
2538     end;
2539    
2540     procedure TIBCustomDataSet.InternalRevertRecord(RecordNumber: Integer);
2541     var
2542     NewBuffer, OldBuffer: PRecordData;
2543    
2544     begin
2545     NewBuffer := nil;
2546     OldBuffer := nil;
2547     NewBuffer := PRecordData(AllocRecordBuffer);
2548     OldBuffer := PRecordData(AllocRecordBuffer);
2549     try
2550     ReadRecordCache(RecordNumber, PChar(NewBuffer), False);
2551     ReadRecordCache(RecordNumber, PChar(OldBuffer), True);
2552     case NewBuffer^.rdCachedUpdateStatus of
2553     cusInserted:
2554     begin
2555     NewBuffer^.rdCachedUpdateStatus := cusUninserted;
2556     Inc(FDeletedRecords);
2557     end;
2558     cusModified,
2559     cusDeleted:
2560     begin
2561     if (NewBuffer^.rdCachedUpdateStatus = cusDeleted) then
2562     Dec(FDeletedRecords);
2563     CopyRecordBuffer(OldBuffer, NewBuffer);
2564     end;
2565     end;
2566    
2567     if State in dsEditModes then
2568     Cancel;
2569    
2570     WriteRecordCache(RecordNumber, PChar(NewBuffer));
2571    
2572     if (NewBuffer^.rdCachedUpdateStatus = cusUninserted ) then
2573     ReSync([]);
2574     finally
2575     FreeRecordBuffer(PChar(NewBuffer));
2576     FreeRecordBuffer(PChar(OldBuffer));
2577     end;
2578     end;
2579    
2580     { A visible record is one that is not truly deleted,
2581     and it is also listed in the FUpdateRecordTypes set }
2582    
2583     function TIBCustomDataSet.IsVisible(Buffer: PChar): Boolean;
2584     begin
2585     result := True;
2586     if not (State = dsOldValue) then
2587     result :=
2588     (PRecordData(Buffer)^.rdCachedUpdateStatus in FUpdateRecordTypes) and
2589     (not ((PRecordData(Buffer)^.rdCachedUpdateStatus = cusUnmodified) and
2590     (PRecordData(Buffer)^.rdUpdateStatus = usDeleted)));
2591     end;
2592    
2593    
2594     function TIBCustomDataSet.LocateNext(const KeyFields: string;
2595     const KeyValues: Variant; Options: TLocateOptions): Boolean;
2596     begin
2597     DisableControls;
2598     try
2599     result := InternalLocate(KeyFields, KeyValues, Options);
2600     finally
2601     EnableControls;
2602     end;
2603     end;
2604    
2605     procedure TIBCustomDataSet.InternalPrepare;
2606     begin
2607     if FInternalPrepared then
2608     Exit;
2609     FBase.SetCursor;
2610     try
2611     ActivateConnection;
2612 tony 45 ActivateTransaction;
2613 tony 33 FBase.CheckDatabase;
2614     FBase.CheckTransaction;
2615     if HasParser and (FParser.SQLText <> FQSelect.SQL.Text) then
2616 tony 35 begin
2617     FQSelect.OnSQLChanged := nil; {Do not react to change}
2618     try
2619     FQSelect.SQL.Text := FParser.SQLText;
2620     finally
2621     FQSelect.OnSQLChanged := SQLChanged;
2622     end;
2623     end;
2624 tony 33 // writeln( FQSelect.SQL.Text);
2625     if FQSelect.SQL.Text <> '' then
2626     begin
2627     if not FQSelect.Prepared then
2628     begin
2629     FQSelect.GenerateParamNames := FGenerateParamNames;
2630     FQSelect.ParamCheck := ParamCheck;
2631     FQSelect.Prepare;
2632     end;
2633     FQDelete.GenerateParamNames := FGenerateParamNames;
2634     if (Trim(FQDelete.SQL.Text) <> '') and (not FQDelete.Prepared) then
2635     FQDelete.Prepare;
2636     FQInsert.GenerateParamNames := FGenerateParamNames;
2637     if (Trim(FQInsert.SQL.Text) <> '') and (not FQInsert.Prepared) then
2638     FQInsert.Prepare;
2639     FQRefresh.GenerateParamNames := FGenerateParamNames;
2640     if (Trim(FQRefresh.SQL.Text) <> '') and (not FQRefresh.Prepared) then
2641     FQRefresh.Prepare;
2642     FQModify.GenerateParamNames := FGenerateParamNames;
2643     if (Trim(FQModify.SQL.Text) <> '') and (not FQModify.Prepared) then
2644     FQModify.Prepare;
2645     FInternalPrepared := True;
2646     InternalInitFieldDefs;
2647     end else
2648     IBError(ibxeEmptyQuery, [nil]);
2649     finally
2650     FBase.RestoreCursor;
2651     end;
2652     end;
2653    
2654     procedure TIBCustomDataSet.RecordModified(Value: Boolean);
2655     begin
2656     SetModified(Value);
2657     end;
2658    
2659     procedure TIBCustomDataSet.RevertRecord;
2660     var
2661     Buff: PRecordData;
2662     begin
2663     if FCachedUpdates and FUpdatesPending then
2664     begin
2665     Buff := PRecordData(GetActiveBuf);
2666     InternalRevertRecord(Buff^.rdRecordNumber);
2667     ReadRecordCache(Buff^.rdRecordNumber, PChar(Buff), False);
2668     DataEvent(deRecordChange, 0);
2669     end;
2670     end;
2671    
2672     procedure TIBCustomDataSet.SaveOldBuffer(Buffer: PChar);
2673     var
2674     OldBuffer: Pointer;
2675     procedure CopyOldBuffer;
2676     begin
2677     CopyRecordBuffer(Buffer, OldBuffer);
2678     if BlobFieldCount > 0 then
2679 tony 45 FillChar(PChar(OldBuffer)[FBlobCacheOffset],
2680     BlobFieldCount * SizeOf(TIBBlobStream) + ArrayFieldCount * SizeOf(IArray),
2681 tony 33 0);
2682     end;
2683    
2684     begin
2685     if (Buffer <> nil) and (PRecordData(Buffer)^.rdRecordNumber >= 0) then
2686     begin
2687     OldBuffer := AllocRecordBuffer;
2688     try
2689     if (PRecordData(Buffer)^.rdSavedOffset = $FFFFFFFF) then
2690     begin
2691     PRecordData(Buffer)^.rdSavedOffset := AdjustPosition(FOldBufferCache, 0,
2692     FILE_END);
2693     CopyOldBuffer;
2694     WriteCache(FOldBufferCache, 0, FILE_CURRENT, OldBuffer);
2695     WriteCache(FBufferCache, PRecordData(Buffer)^.rdRecordNumber * FRecordBufferSize,
2696     FILE_BEGIN, Buffer);
2697     end
2698     else begin
2699     CopyOldBuffer;
2700     WriteCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
2701     OldBuffer);
2702     end;
2703     finally
2704     FreeRecordBuffer(PChar(OldBuffer));
2705     end;
2706     end;
2707     end;
2708    
2709     procedure TIBCustomDataSet.SetBufferChunks(Value: Integer);
2710     begin
2711     if (Value <= 0) then
2712     FBufferChunks := BufferCacheSize
2713     else
2714     FBufferChunks := Value;
2715     end;
2716    
2717     procedure TIBCustomDataSet.SetDatabase(Value: TIBDatabase);
2718     begin
2719     if (FBase.Database <> Value) then
2720     begin
2721     CheckDatasetClosed;
2722 tony 80 InternalUnPrepare;
2723 tony 33 FBase.Database := Value;
2724     FQDelete.Database := Value;
2725     FQInsert.Database := Value;
2726     FQRefresh.Database := Value;
2727     FQSelect.Database := Value;
2728     FQModify.Database := Value;
2729 tony 101 FDatabaseInfo.Database := Value;
2730 tony 104 FGeneratorField.Database := Value;
2731 tony 33 end;
2732     end;
2733    
2734     procedure TIBCustomDataSet.SetDeleteSQL(Value: TStrings);
2735     begin
2736     if FQDelete.SQL.Text <> Value.Text then
2737     begin
2738     Disconnect;
2739     FQDelete.SQL.Assign(Value);
2740     end;
2741     end;
2742    
2743     procedure TIBCustomDataSet.SetInsertSQL(Value: TStrings);
2744     begin
2745     if FQInsert.SQL.Text <> Value.Text then
2746     begin
2747     Disconnect;
2748     FQInsert.SQL.Assign(Value);
2749     end;
2750     end;
2751    
2752 tony 80 procedure TIBCustomDataSet.SetInternalSQLParams(Params: ISQLParams; Buffer: Pointer);
2753 tony 33 var
2754     i, j: Integer;
2755     cr, data: PChar;
2756 tony 43 fn: string;
2757     st: RawByteString;
2758 tony 33 OldBuffer: Pointer;
2759     ts: TTimeStamp;
2760 tony 45 Param: ISQLParam;
2761 tony 33 begin
2762     if (Buffer = nil) then
2763     IBError(ibxeBufferNotSet, [nil]);
2764     if (not FInternalPrepared) then
2765     InternalPrepare;
2766     OldBuffer := nil;
2767     try
2768 tony 80 for i := 0 to Params.GetCount - 1 do
2769 tony 33 begin
2770 tony 80 Param := Params[i];
2771 tony 45 fn := Param.Name;
2772 tony 33 if (Pos('OLD_', fn) = 1) then {mbcs ok}
2773     begin
2774     fn := Copy(fn, 5, Length(fn));
2775     if not Assigned(OldBuffer) then
2776     begin
2777     OldBuffer := AllocRecordBuffer;
2778     ReadRecordCache(PRecordData(Buffer)^.rdRecordNumber, OldBuffer, True);
2779     end;
2780     cr := OldBuffer;
2781     end
2782     else if (Pos('NEW_', fn) = 1) then {mbcs ok}
2783     begin
2784     fn := Copy(fn, 5, Length(fn));
2785     cr := Buffer;
2786     end
2787     else
2788     cr := Buffer;
2789     j := FQSelect.FieldIndex[fn] + 1;
2790     if (j > 0) then
2791 tony 45 with PRecordData(cr)^,rdFields[j], FFieldColumns^[j] do
2792 tony 33 begin
2793 tony 45 if Param.name = 'IBX_INTERNAL_DBKEY' then {do not localize}
2794 tony 33 begin
2795 tony 45 PIBDBKey(Param.AsPointer)^ := rdDBKey;
2796 tony 33 continue;
2797     end;
2798 tony 45 if fdIsNull then
2799     Param.IsNull := True
2800 tony 33 else begin
2801 tony 45 Param.IsNull := False;
2802     data := cr + fdDataOfs;
2803     case fdDataType of
2804 tony 33 SQL_TEXT, SQL_VARYING:
2805     begin
2806 tony 45 SetString(st, data, fdDataLength);
2807     SetCodePage(st,fdCodePage,false);
2808     Param.AsString := st;
2809 tony 33 end;
2810     SQL_FLOAT, SQL_DOUBLE, SQL_D_FLOAT:
2811 tony 45 Param.AsDouble := PDouble(data)^;
2812 tony 33 SQL_SHORT, SQL_LONG:
2813     begin
2814 tony 45 if fdDataScale = 0 then
2815     Param.AsLong := PLong(data)^
2816 tony 33 else
2817 tony 45 if fdDataScale >= (-4) then
2818     Param.AsCurrency := PCurrency(data)^
2819     else
2820     Param.AsDouble := PDouble(data)^;
2821 tony 33 end;
2822     SQL_INT64:
2823     begin
2824 tony 45 if fdDataScale = 0 then
2825     Param.AsInt64 := PInt64(data)^
2826 tony 33 else
2827 tony 45 if fdDataScale >= (-4) then
2828     Param.AsCurrency := PCurrency(data)^
2829     else
2830     Param.AsDouble := PDouble(data)^;
2831 tony 33 end;
2832     SQL_BLOB, SQL_ARRAY, SQL_QUAD:
2833 tony 45 Param.AsQuad := PISC_QUAD(data)^;
2834 tony 33 SQL_TYPE_DATE:
2835     begin
2836     ts.Date := PInt(data)^;
2837     ts.Time := 0;
2838 tony 45 Param.AsDate := TimeStampToDateTime(ts);
2839 tony 33 end;
2840     SQL_TYPE_TIME:
2841     begin
2842     ts.Date := 0;
2843     ts.Time := PInt(data)^;
2844 tony 45 Param.AsTime := TimeStampToDateTime(ts);
2845 tony 33 end;
2846     SQL_TIMESTAMP:
2847 tony 45 Param.AsDateTime :=
2848 tony 33 TimeStampToDateTime(MSecsToTimeStamp(trunc(PDouble(data)^)));
2849     SQL_BOOLEAN:
2850 tony 45 Param.AsBoolean := PWordBool(data)^;
2851 tony 33 end;
2852     end;
2853     end;
2854     end;
2855     finally
2856     if (OldBuffer <> nil) then
2857     FreeRecordBuffer(PChar(OldBuffer));
2858     end;
2859     end;
2860    
2861     procedure TIBCustomDataSet.SetRefreshSQL(Value: TStrings);
2862     begin
2863     if FQRefresh.SQL.Text <> Value.Text then
2864     begin
2865     Disconnect;
2866     FQRefresh.SQL.Assign(Value);
2867     end;
2868     end;
2869    
2870     procedure TIBCustomDataSet.SetSelectSQL(Value: TStrings);
2871     begin
2872     if FQSelect.SQL.Text <> Value.Text then
2873     begin
2874     Disconnect;
2875     FQSelect.SQL.Assign(Value);
2876     end;
2877     end;
2878    
2879     procedure TIBCustomDataSet.SetModifySQL(Value: TStrings);
2880     begin
2881     if FQModify.SQL.Text <> Value.Text then
2882     begin
2883     Disconnect;
2884     FQModify.SQL.Assign(Value);
2885     end;
2886     end;
2887    
2888     procedure TIBCustomDataSet.SetTransaction(Value: TIBTransaction);
2889     begin
2890     if (FBase.Transaction <> Value) then
2891     begin
2892     CheckDatasetClosed;
2893     FBase.Transaction := Value;
2894     FQDelete.Transaction := Value;
2895     FQInsert.Transaction := Value;
2896     FQRefresh.Transaction := Value;
2897     FQSelect.Transaction := Value;
2898     FQModify.Transaction := Value;
2899 tony 104 FGeneratorField.Transaction := Value;
2900 tony 33 end;
2901     end;
2902    
2903     procedure TIBCustomDataSet.SetUniDirectional(Value: Boolean);
2904     begin
2905     CheckDatasetClosed;
2906     FUniDirectional := Value;
2907     end;
2908    
2909     procedure TIBCustomDataSet.SetUpdateRecordTypes(Value: TIBUpdateRecordTypes);
2910     begin
2911     FUpdateRecordTypes := Value;
2912     if Active then
2913     First;
2914     end;
2915    
2916     procedure TIBCustomDataSet.RefreshParams;
2917     var
2918     DataSet: TDataSet;
2919     begin
2920     DisableControls;
2921     try
2922     if FDataLink.DataSource <> nil then
2923     begin
2924     DataSet := FDataLink.DataSource.DataSet;
2925     if DataSet <> nil then
2926     if DataSet.Active and (DataSet.State <> dsSetKey) then
2927     begin
2928     Close;
2929     Open;
2930     end;
2931     end;
2932     finally
2933     EnableControls;
2934     end;
2935     end;
2936    
2937     procedure TIBCustomDataSet.RegisterIBLink(Sender: TIBControlLink);
2938     begin
2939     if FIBLinks.IndexOf(Sender) = -1 then
2940     FIBLinks.Add(Sender);
2941     end;
2942    
2943    
2944     procedure TIBCustomDataSet.SQLChanging(Sender: TObject);
2945     begin
2946     Active := false;
2947     { if FOpen then
2948     InternalClose;}
2949     if FInternalPrepared then
2950     InternalUnPrepare;
2951     FieldDefs.Clear;
2952     FieldDefs.Updated := false;
2953     end;
2954    
2955 tony 35 procedure TIBCustomDataSet.SQLChanged(Sender: TObject);
2956     begin
2957     FBaseSQLSelect.assign(FQSelect.SQL);
2958     end;
2959    
2960 tony 33 { I can "undelete" uninserted records (make them "inserted" again).
2961     I can "undelete" cached deleted (the deletion hasn't yet occurred) }
2962     procedure TIBCustomDataSet.Undelete;
2963     var
2964     Buff: PRecordData;
2965     begin
2966     CheckActive;
2967     Buff := PRecordData(GetActiveBuf);
2968     with Buff^ do
2969     begin
2970     if rdCachedUpdateStatus = cusUninserted then
2971     begin
2972     rdCachedUpdateStatus := cusInserted;
2973     Dec(FDeletedRecords);
2974     end
2975     else if (rdUpdateStatus = usDeleted) and
2976     (rdCachedUpdateStatus = cusDeleted) then
2977     begin
2978     rdCachedUpdateStatus := cusUnmodified;
2979     rdUpdateStatus := usUnmodified;
2980     Dec(FDeletedRecords);
2981     end;
2982     WriteRecordCache(rdRecordNumber, PChar(Buff));
2983     end;
2984     end;
2985    
2986     procedure TIBCustomDataSet.UnRegisterIBLink(Sender: TIBControlLink);
2987     begin
2988     FIBLinks.Remove(Sender);
2989     end;
2990    
2991     function TIBCustomDataSet.UpdateStatus: TUpdateStatus;
2992     begin
2993     if Active then
2994     if GetActiveBuf <> nil then
2995     result := PRecordData(GetActiveBuf)^.rdUpdateStatus
2996     else
2997     result := usUnmodified
2998     else
2999     result := usUnmodified;
3000     end;
3001    
3002     function TIBCustomDataSet.IsSequenced: Boolean;
3003     begin
3004     Result := Assigned( FQSelect ) and FQSelect.EOF;
3005     end;
3006    
3007 tony 45 function TIBCustomDataSet.ParamByName(ParamName: String): ISQLParam;
3008 tony 33 begin
3009     ActivateConnection;
3010     ActivateTransaction;
3011     if not FInternalPrepared then
3012     InternalPrepare;
3013     Result := Params.ByName(ParamName);
3014     end;
3015    
3016     {Beware: the parameter FCache is used as an identifier to determine which
3017     cache is being operated on and is not referenced in the computation.
3018     The result is an adjusted offset into the identified cache, either the
3019     Buffer Cache or the old Buffer Cache.}
3020    
3021     function TIBCustomDataSet.AdjustPosition(FCache: PChar; Offset: DWORD;
3022     Origin: Integer): DWORD;
3023     var
3024     OldCacheSize: Integer;
3025     begin
3026     if (FCache = FBufferCache) then
3027     begin
3028     case Origin of
3029     FILE_BEGIN: FBPos := Offset;
3030     FILE_CURRENT: FBPos := FBPos + Offset;
3031     FILE_END: FBPos := DWORD(FBEnd) + Offset;
3032     end;
3033     OldCacheSize := FCacheSize;
3034     while (FBPos >= DWORD(FCacheSize)) do
3035     Inc(FCacheSize, FBufferChunkSize);
3036     if FCacheSize > OldCacheSize then
3037     IBAlloc(FBufferCache, FCacheSize, FCacheSize);
3038     result := FBPos;
3039     end
3040     else begin
3041     case Origin of
3042     FILE_BEGIN: FOBPos := Offset;
3043     FILE_CURRENT: FOBPos := FOBPos + Offset;
3044     FILE_END: FOBPos := DWORD(FOBEnd) + Offset;
3045     end;
3046     OldCacheSize := FOldCacheSize;
3047     while (FBPos >= DWORD(FOldCacheSize)) do
3048     Inc(FOldCacheSize, FBufferChunkSize);
3049     if FOldCacheSize > OldCacheSize then
3050     IBAlloc(FOldBufferCache, FOldCacheSize, FOldCacheSize);
3051     result := FOBPos;
3052     end;
3053     end;
3054    
3055     procedure TIBCustomDataSet.ReadCache(FCache: PChar; Offset: DWORD; Origin: Integer;
3056     Buffer: PChar);
3057     var
3058     pCache: PChar;
3059     AdjustedOffset: DWORD;
3060     bOld: Boolean;
3061     begin
3062     bOld := (FCache = FOldBufferCache);
3063     AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
3064     if not bOld then
3065     pCache := FBufferCache + AdjustedOffset
3066     else
3067     pCache := FOldBufferCache + AdjustedOffset;
3068     Move(pCache^, Buffer^, DWORD(FRecordBufferSize));
3069     AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
3070     end;
3071    
3072     procedure TIBCustomDataSet.ReadRecordCache(RecordNumber: Integer; Buffer: PChar;
3073     ReadOldBuffer: Boolean);
3074     begin
3075     if FUniDirectional then
3076     RecordNumber := RecordNumber mod UniCache;
3077     if (ReadOldBuffer) then
3078     begin
3079     ReadRecordCache(RecordNumber, Buffer, False);
3080     if FCachedUpdates and
3081     (PRecordData(Buffer)^.rdSavedOffset <> $FFFFFFFF) then
3082     ReadCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
3083     Buffer)
3084     else
3085     if ReadOldBuffer and
3086     (PRecordData(FOldBuffer)^.rdRecordNumber = RecordNumber) then
3087     CopyRecordBuffer( FOldBuffer, Buffer )
3088     end
3089     else
3090     ReadCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
3091     end;
3092    
3093     procedure TIBCustomDataSet.WriteCache(FCache: PChar; Offset: DWORD; Origin: Integer;
3094     Buffer: PChar);
3095     var
3096     pCache: PChar;
3097     AdjustedOffset: DWORD;
3098     bOld: Boolean;
3099     dwEnd: DWORD;
3100     begin
3101     bOld := (FCache = FOldBufferCache);
3102     AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
3103     if not bOld then
3104     pCache := FBufferCache + AdjustedOffset
3105     else
3106     pCache := FOldBufferCache + AdjustedOffset;
3107     Move(Buffer^, pCache^, FRecordBufferSize);
3108     dwEnd := AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
3109     if not bOld then
3110     begin
3111     if (dwEnd > FBEnd) then
3112     FBEnd := dwEnd;
3113     end
3114     else begin
3115     if (dwEnd > FOBEnd) then
3116     FOBEnd := dwEnd;
3117     end;
3118     end;
3119    
3120     procedure TIBCustomDataSet.WriteRecordCache(RecordNumber: Integer; Buffer: PChar);
3121     begin
3122     if RecordNumber >= 0 then
3123     begin
3124     if FUniDirectional then
3125     RecordNumber := RecordNumber mod UniCache;
3126     WriteCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
3127     end;
3128     end;
3129    
3130     function TIBCustomDataSet.AllocRecordBuffer: PChar;
3131     begin
3132     result := nil;
3133     IBAlloc(result, FRecordBufferSize, FRecordBufferSize);
3134     Move(FModelBuffer^, result^, FRecordBufferSize);
3135     end;
3136    
3137     function TIBCustomDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
3138     var
3139     pb: PBlobDataArray;
3140     fs: TIBBlobStream;
3141     Buff: PChar;
3142     bTr, bDB: Boolean;
3143     begin
3144 tony 45 if (Field = nil) or (Field.DataSet <> self) then
3145     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3146 tony 33 Buff := GetActiveBuf;
3147     if Buff = nil then
3148     begin
3149     fs := TIBBlobStream.Create;
3150     fs.Mode := bmReadWrite;
3151 tony 45 fs.Database := Database;
3152     fs.Transaction := Transaction;
3153     fs.SetField(Field);
3154 tony 33 FBlobStreamList.Add(Pointer(fs));
3155     result := TIBDSBlobStream.Create(Field, fs, Mode);
3156     exit;
3157     end;
3158     pb := PBlobDataArray(Buff + FBlobCacheOffset);
3159     if pb^[Field.Offset] = nil then
3160     begin
3161     AdjustRecordOnInsert(Buff);
3162     pb^[Field.Offset] := TIBBlobStream.Create;
3163     fs := pb^[Field.Offset];
3164     FBlobStreamList.Add(Pointer(fs));
3165     fs.Mode := bmReadWrite;
3166     fs.Database := Database;
3167     fs.Transaction := Transaction;
3168 tony 45 fs.SetField(Field);
3169 tony 33 fs.BlobID :=
3170 tony 45 PISC_QUAD(@Buff[FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]].fdDataOfs])^;
3171 tony 33 if (CachedUpdates) then
3172     begin
3173     bTr := not Transaction.InTransaction;
3174     bDB := not Database.Connected;
3175     if bDB then
3176     Database.Open;
3177     if bTr then
3178     Transaction.StartTransaction;
3179     fs.Seek(0, soFromBeginning);
3180     if bTr then
3181     Transaction.Commit;
3182     if bDB then
3183     Database.Close;
3184     end;
3185     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3186     end else
3187     fs := pb^[Field.Offset];
3188     result := TIBDSBlobStream.Create(Field, fs, Mode);
3189     end;
3190    
3191 tony 45 function TIBCustomDataSet.GetArray(Field: TIBArrayField): IArray;
3192     var Buff: PChar;
3193     pda: PArrayDataArray;
3194     bTr, bDB: Boolean;
3195     begin
3196     if (Field = nil) or (Field.DataSet <> self) then
3197     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3198     Buff := GetActiveBuf;
3199     if Buff = nil then
3200     Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3201     Field.FRelationName,Field.FieldName)
3202     else
3203     begin
3204     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3205     if pda^[Field.FCacheOffset] = nil then
3206     begin
3207     AdjustRecordOnInsert(Buff);
3208     if Field.IsNull then
3209     Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3210     Field.FRelationName,Field.FieldName)
3211     else
3212     Result := Database.Attachment.OpenArray(Transaction.TransactionIntf,
3213     Field.FRelationName,Field.FieldName,Field.ArrayID);
3214     pda^[Field.FCacheOffset] := TIBArray.Create(Field,Result);
3215     FArrayList.Add(pda^[Field.FCacheOffset]);
3216     if (CachedUpdates) then
3217     begin
3218     bTr := not Transaction.InTransaction;
3219     bDB := not Database.Connected;
3220     if bDB then
3221     Database.Open;
3222     if bTr then
3223     Transaction.StartTransaction;
3224     pda^[Field.FCacheOffset].ArrayIntf.PreLoad;
3225     if bTr then
3226     Transaction.Commit;
3227     if bDB then
3228     Database.Close;
3229     end;
3230     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3231     end
3232     else
3233     Result := pda^[Field.FCacheOffset].ArrayIntf;
3234     end;
3235     end;
3236    
3237     procedure TIBCustomDataSet.SetArrayIntf(AnArray: IArray; Field: TIBArrayField);
3238     var Buff: PChar;
3239     pda: PArrayDataArray;
3240     begin
3241     if (Field = nil) or (Field.DataSet <> self) then
3242     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3243     Buff := GetActiveBuf;
3244     if Buff <> nil then
3245     begin
3246     AdjustRecordOnInsert(Buff);
3247     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3248     pda^[Field.FCacheOffset].FArray := AnArray;
3249     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3250     end;
3251     end;
3252    
3253 tony 33 function TIBCustomDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
3254     const
3255     CMPLess = -1;
3256     CMPEql = 0;
3257     CMPGtr = 1;
3258     RetCodes: array[Boolean, Boolean] of ShortInt = ((2, CMPLess),
3259     (CMPGtr, CMPEql));
3260     begin
3261     result := RetCodes[Bookmark1 = nil, Bookmark2 = nil];
3262    
3263     if Result = 2 then
3264     begin
3265     if PInteger(Bookmark1)^ < PInteger(Bookmark2)^ then
3266     Result := CMPLess
3267     else
3268     if PInteger(Bookmark1)^ > PInteger(Bookmark2)^ then
3269     Result := CMPGtr
3270     else
3271     Result := CMPEql;
3272     end;
3273     end;
3274    
3275     procedure TIBCustomDataSet.DoBeforeDelete;
3276     var
3277     Buff: PRecordData;
3278     begin
3279     if not CanDelete then
3280     IBError(ibxeCannotDelete, [nil]);
3281     Buff := PRecordData(GetActiveBuf);
3282     if FCachedUpdates and
3283     (Buff^.rdCachedUpdateStatus in [cusUnmodified]) then
3284     SaveOldBuffer(PChar(Buff));
3285     inherited DoBeforeDelete;
3286     end;
3287    
3288     procedure TIBCustomDataSet.DoAfterDelete;
3289     begin
3290     inherited DoAfterDelete;
3291     FBase.DoAfterDelete(self);
3292     InternalAutoCommit;
3293     end;
3294    
3295     procedure TIBCustomDataSet.DoBeforeEdit;
3296     var
3297     Buff: PRecordData;
3298     begin
3299     Buff := PRecordData(GetActiveBuf);
3300     if not(CanEdit or (FQModify.SQL.Count <> 0) or
3301     (FCachedUpdates and Assigned(FOnUpdateRecord))) then
3302     IBError(ibxeCannotUpdate, [nil]);
3303     if FCachedUpdates and (Buff^.rdCachedUpdateStatus in [cusUnmodified, cusInserted]) then
3304     SaveOldBuffer(PChar(Buff));
3305     CopyRecordBuffer(GetActiveBuf, FOldBuffer);
3306     inherited DoBeforeEdit;
3307     end;
3308    
3309     procedure TIBCustomDataSet.DoAfterEdit;
3310     begin
3311     inherited DoAfterEdit;
3312     FBase.DoAfterEdit(self);
3313     end;
3314    
3315     procedure TIBCustomDataSet.DoBeforeInsert;
3316     begin
3317     if not CanInsert then
3318     IBError(ibxeCannotInsert, [nil]);
3319     inherited DoBeforeInsert;
3320     end;
3321    
3322     procedure TIBCustomDataSet.DoAfterInsert;
3323     begin
3324     if GeneratorField.ApplyOnEvent = gaeOnNewRecord then
3325     GeneratorField.Apply;
3326     inherited DoAfterInsert;
3327     FBase.DoAfterInsert(self);
3328     end;
3329    
3330     procedure TIBCustomDataSet.DoBeforeClose;
3331     begin
3332     inherited DoBeforeClose;
3333 tony 45 if FInTransactionEnd and (FCloseAction = TARollback) then
3334     Exit;
3335 tony 33 if State in [dsInsert,dsEdit] then
3336     begin
3337     if DataSetCloseAction = dcSaveChanges then
3338     Post;
3339     {Note this can fail with an exception e.g. due to
3340     database validation error. In which case the dataset remains open }
3341     end;
3342 tony 45 if FCachedUpdates and FUpdatesPending and (DataSetCloseAction = dcSaveChanges) then
3343     ApplyUpdates;
3344 tony 33 end;
3345    
3346     procedure TIBCustomDataSet.DoBeforeOpen;
3347     var i: integer;
3348     begin
3349     if assigned(FParser) then
3350     FParser.Reset;
3351     for i := 0 to FIBLinks.Count - 1 do
3352     TIBControlLink(FIBLinks[i]).UpdateSQL(self);
3353     inherited DoBeforeOpen;
3354     for i := 0 to FIBLinks.Count - 1 do
3355     TIBControlLink(FIBLinks[i]).UpdateParams(self);
3356     end;
3357    
3358     procedure TIBCustomDataSet.DoBeforePost;
3359     begin
3360     inherited DoBeforePost;
3361     if (State = dsInsert) and
3362     (GeneratorField.ApplyOnEvent = gaeOnPostRecord) then
3363     GeneratorField.Apply
3364     end;
3365    
3366     procedure TIBCustomDataSet.DoAfterPost;
3367     begin
3368     inherited DoAfterPost;
3369     FBase.DoAfterPost(self);
3370     InternalAutoCommit;
3371     end;
3372    
3373     procedure TIBCustomDataSet.FetchAll;
3374     var
3375     CurBookmark: TBookmark;
3376     begin
3377     FBase.SetCursor;
3378     try
3379     if FQSelect.EOF or not FQSelect.Open then
3380     exit;
3381     DisableControls;
3382     try
3383     CurBookmark := Bookmark;
3384     Last;
3385     Bookmark := CurBookmark;
3386     finally
3387     EnableControls;
3388     end;
3389     finally
3390     FBase.RestoreCursor;
3391     end;
3392     end;
3393    
3394     procedure TIBCustomDataSet.FreeRecordBuffer(var Buffer: PChar);
3395     begin
3396     FreeMem(Buffer);
3397     Buffer := nil;
3398     end;
3399    
3400     procedure TIBCustomDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
3401     begin
3402     Move(PRecordData(Buffer)^.rdRecordNumber, Data^, BookmarkSize);
3403     end;
3404    
3405     function TIBCustomDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
3406     begin
3407     result := PRecordData(Buffer)^.rdBookmarkFlag;
3408     end;
3409    
3410     function TIBCustomDataSet.GetCanModify: Boolean;
3411     begin
3412     result := (FQInsert.SQL.Text <> '') or
3413     (FQModify.SQL.Text <> '') or
3414     (FQDelete.SQL.Text <> '') or
3415     (Assigned(FUpdateObject));
3416     end;
3417    
3418     function TIBCustomDataSet.GetCurrentRecord(Buffer: PChar): Boolean;
3419     begin
3420     if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then
3421     begin
3422     UpdateCursorPos;
3423     ReadRecordCache(PRecordData(ActiveBuffer)^.rdRecordNumber, Buffer, False);
3424     result := True;
3425     end
3426     else
3427     result := False;
3428     end;
3429    
3430     function TIBCustomDataSet.GetDataSource: TDataSource;
3431     begin
3432     if FDataLink = nil then
3433     result := nil
3434     else
3435     result := FDataLink.DataSource;
3436     end;
3437    
3438     function TIBCustomDataSet.GetDBAliasName(FieldNo: integer): string;
3439     begin
3440     Result := FAliasNameMap[FieldNo-1]
3441     end;
3442    
3443     function TIBCustomDataSet.GetFieldDefFromAlias(aliasName: string): TFieldDef;
3444     var
3445     i: integer;
3446     begin
3447     Result := nil;
3448     for i := 0 to Length(FAliasNameMap) - 1 do
3449     if FAliasNameMap[i] = aliasName then
3450     begin
3451     Result := FieldDefs[i];
3452     Exit
3453     end;
3454     end;
3455    
3456     function TIBCustomDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
3457     begin
3458     Result := DefaultFieldClasses[FieldType];
3459     end;
3460    
3461     function TIBCustomDataSet.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean;
3462     begin
3463     result := GetFieldData(FieldByNumber(FieldNo), buffer);
3464     end;
3465    
3466     function TIBCustomDataSet.InternalGetFieldData(Field: TField; Buffer: Pointer): Boolean;
3467     var
3468     Buff, Data: PChar;
3469     CurrentRecord: PRecordData;
3470     begin
3471     result := False;
3472     Buff := GetActiveBuf;
3473     if (Buff = nil) or
3474     (not IsVisible(Buff)) or not assigned(Field.DataSet) then
3475     exit;
3476     { The intention here is to stuff the buffer with the data for the
3477     referenced field for the current record }
3478     CurrentRecord := PRecordData(Buff);
3479     if (Field.FieldNo < 0) then
3480     begin
3481     Inc(Buff, FRecordSize + Field.Offset);
3482     result := Boolean(Buff[0]);
3483     if result and (Buffer <> nil) then
3484     Move(Buff[1], Buffer^, Field.DataSize);
3485     end
3486 tony 45 else
3487     if (FMappedFieldPosition[Field.FieldNo - 1] > 0) and
3488 tony 33 (FMappedFieldPosition[Field.FieldNo - 1] <= CurrentRecord^.rdFieldCount) then
3489 tony 45 with CurrentRecord^.rdFields[FMappedFieldPosition[Field.FieldNo - 1]],
3490     FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]] do
3491 tony 33 begin
3492 tony 45 result := not fdIsNull;
3493 tony 33 if result and (Buffer <> nil) then
3494     begin
3495 tony 45 Data := Buff + fdDataOfs;
3496 tony 33 if (fdDataType = SQL_VARYING) or (fdDataType = SQL_TEXT) then
3497     begin
3498     if fdDataLength < Field.DataSize then
3499     begin
3500     Move(Data^, Buffer^, fdDataLength);
3501     PChar(Buffer)[fdDataLength] := #0;
3502     end
3503     else
3504     IBError(ibxeFieldSizeError,[Field.FieldName])
3505     end
3506     else
3507     Move(Data^, Buffer^, Field.DataSize);
3508     end;
3509     end;
3510     end;
3511    
3512     { GetRecNo and SetRecNo both operate off of 1-based indexes as
3513     opposed to 0-based indexes.
3514     This is because we want LastRecordNumber/RecordCount = 1 }
3515    
3516     function TIBCustomDataSet.GetRecNo: Integer;
3517     begin
3518     if GetActiveBuf = nil then
3519     result := 0
3520     else
3521     result := PRecordData(GetActiveBuf)^.rdRecordNumber + 1;
3522     end;
3523    
3524     function TIBCustomDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
3525     DoCheck: Boolean): TGetResult;
3526     var
3527     Accept: Boolean;
3528     SaveState: TDataSetState;
3529     begin
3530     Result := grOK;
3531     if Filtered and Assigned(OnFilterRecord) then
3532     begin
3533     Accept := False;
3534     SaveState := SetTempState(dsFilter);
3535     while not Accept do
3536     begin
3537     Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3538     if Result <> grOK then
3539     break;
3540     FFilterBuffer := Buffer;
3541     try
3542     Accept := True;
3543     OnFilterRecord(Self, Accept);
3544     if not Accept and (GetMode = gmCurrent) then
3545     GetMode := gmPrior;
3546     except
3547     // FBase.HandleException(Self);
3548     end;
3549     end;
3550     RestoreState(SaveState);
3551     end
3552     else
3553     Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3554     end;
3555    
3556     function TIBCustomDataSet.InternalGetRecord(Buffer: PChar; GetMode: TGetMode;
3557     DoCheck: Boolean): TGetResult;
3558     begin
3559     result := grError;
3560     case GetMode of
3561     gmCurrent: begin
3562     if (FCurrentRecord >= 0) then begin
3563     if FCurrentRecord < FRecordCount then
3564     ReadRecordCache(FCurrentRecord, Buffer, False)
3565     else begin
3566 tony 45 while (not FQSelect.EOF) and FQSelect.Next and
3567 tony 33 (FCurrentRecord >= FRecordCount) do begin
3568     FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
3569     Inc(FRecordCount);
3570     end;
3571     FCurrentRecord := FRecordCount - 1;
3572     if (FCurrentRecord >= 0) then
3573     ReadRecordCache(FCurrentRecord, Buffer, False);
3574     end;
3575     result := grOk;
3576     end else
3577     result := grBOF;
3578     end;
3579     gmNext: begin
3580     result := grOk;
3581     if FCurrentRecord = FRecordCount then
3582     result := grEOF
3583     else if FCurrentRecord = FRecordCount - 1 then begin
3584     if (not FQSelect.EOF) then begin
3585     FQSelect.Next;
3586     Inc(FCurrentRecord);
3587     end;
3588     if (FQSelect.EOF) then begin
3589     result := grEOF;
3590     end else begin
3591     Inc(FRecordCount);
3592     FetchCurrentRecordToBuffer(FQSelect, FCurrentRecord, Buffer);
3593     end;
3594     end else if (FCurrentRecord < FRecordCount) then begin
3595     Inc(FCurrentRecord);
3596     ReadRecordCache(FCurrentRecord, Buffer, False);
3597     end;
3598     end;
3599     else { gmPrior }
3600     begin
3601     if (FCurrentRecord = 0) then begin
3602     Dec(FCurrentRecord);
3603     result := grBOF;
3604     end else if (FCurrentRecord > 0) and
3605     (FCurrentRecord <= FRecordCount) then begin
3606     Dec(FCurrentRecord);
3607     ReadRecordCache(FCurrentRecord, Buffer, False);
3608     result := grOk;
3609     end else if (FCurrentRecord = -1) then
3610     result := grBOF;
3611     end;
3612     end;
3613     if result = grOk then
3614     result := AdjustCurrentRecord(Buffer, GetMode);
3615     if result = grOk then with PRecordData(Buffer)^ do begin
3616     rdBookmarkFlag := bfCurrent;
3617     GetCalcFields(Buffer);
3618     end else if (result = grEOF) then begin
3619     CopyRecordBuffer(FModelBuffer, Buffer);
3620     PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3621     end else if (result = grBOF) then begin
3622     CopyRecordBuffer(FModelBuffer, Buffer);
3623     PRecordData(Buffer)^.rdBookmarkFlag := bfBOF;
3624     end else if (result = grError) then begin
3625     CopyRecordBuffer(FModelBuffer, Buffer);
3626     PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3627     end;;
3628     end;
3629    
3630     function TIBCustomDataSet.GetRecordCount: Integer;
3631     begin
3632     result := FRecordCount - FDeletedRecords;
3633     end;
3634    
3635     function TIBCustomDataSet.GetRecordSize: Word;
3636     begin
3637     result := FRecordBufferSize;
3638     end;
3639    
3640     procedure TIBCustomDataSet.InternalAutoCommit;
3641     begin
3642     with Transaction do
3643     if InTransaction and (FAutoCommit = acCommitRetaining) then
3644     begin
3645     if CachedUpdates then ApplyUpdates;
3646     CommitRetaining;
3647     end;
3648     end;
3649    
3650     procedure TIBCustomDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
3651     begin
3652     CheckEditState;
3653     begin
3654     { When adding records, we *always* append.
3655     Insertion is just too costly }
3656     AdjustRecordOnInsert(Buffer);
3657     with PRecordData(Buffer)^ do
3658     begin
3659     rdUpdateStatus := usInserted;
3660     rdCachedUpdateStatus := cusInserted;
3661     end;
3662     if not CachedUpdates then
3663     InternalPostRecord(FQInsert, Buffer)
3664     else begin
3665     WriteRecordCache(FCurrentRecord, Buffer);
3666     FUpdatesPending := True;
3667     end;
3668     Inc(FRecordCount);
3669     InternalSetToRecord(Buffer);
3670     end
3671     end;
3672    
3673     procedure TIBCustomDataSet.InternalCancel;
3674     var
3675     Buff: PChar;
3676     CurRec: Integer;
3677 tony 45 pda: PArrayDataArray;
3678     i: integer;
3679 tony 33 begin
3680     inherited InternalCancel;
3681     Buff := GetActiveBuf;
3682 tony 45 if Buff <> nil then
3683     begin
3684     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3685     for i := 0 to ArrayFieldCount - 1 do
3686     pda^[i].ArrayIntf.CancelChanges;
3687 tony 33 CurRec := FCurrentRecord;
3688     AdjustRecordOnInsert(Buff);
3689     if (State = dsEdit) then begin
3690     CopyRecordBuffer(FOldBuffer, Buff);
3691     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3692     end else begin
3693     CopyRecordBuffer(FModelBuffer, Buff);
3694     PRecordData(Buff)^.rdUpdateStatus := usDeleted;
3695     PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
3696     PRecordData(Buff)^.rdBookmarkFlag := bfEOF;
3697     FCurrentRecord := CurRec;
3698     end;
3699     end;
3700     end;
3701    
3702    
3703     procedure TIBCustomDataSet.InternalClose;
3704     begin
3705     if FDidActivate then
3706     DeactivateTransaction;
3707     FQSelect.Close;
3708     ClearBlobCache;
3709 tony 45 ClearArrayCache;
3710 tony 33 FreeRecordBuffer(FModelBuffer);
3711     FreeRecordBuffer(FOldBuffer);
3712     FCurrentRecord := -1;
3713     FOpen := False;
3714     FRecordCount := 0;
3715     FDeletedRecords := 0;
3716     FRecordSize := 0;
3717     FBPos := 0;
3718     FOBPos := 0;
3719     FCacheSize := 0;
3720     FOldCacheSize := 0;
3721     FBEnd := 0;
3722     FOBEnd := 0;
3723     FreeMem(FBufferCache);
3724     FBufferCache := nil;
3725 tony 45 FreeMem(FFieldColumns);
3726     FFieldColumns := nil;
3727 tony 33 FreeMem(FOldBufferCache);
3728     FOldBufferCache := nil;
3729     BindFields(False);
3730 tony 35 ResetParser;
3731 tony 33 if DefaultFields then DestroyFields;
3732     end;
3733    
3734     procedure TIBCustomDataSet.InternalDelete;
3735     var
3736     Buff: PChar;
3737     begin
3738     FBase.SetCursor;
3739     try
3740     Buff := GetActiveBuf;
3741     if CanDelete then
3742     begin
3743     if not CachedUpdates then
3744     InternalDeleteRecord(FQDelete, Buff)
3745     else
3746     begin
3747     with PRecordData(Buff)^ do
3748     begin
3749     if rdCachedUpdateStatus = cusInserted then
3750     rdCachedUpdateStatus := cusUninserted
3751     else begin
3752     rdUpdateStatus := usDeleted;
3753     rdCachedUpdateStatus := cusDeleted;
3754     end;
3755     end;
3756     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3757     end;
3758     Inc(FDeletedRecords);
3759     FUpdatesPending := True;
3760     end else
3761     IBError(ibxeCannotDelete, [nil]);
3762     finally
3763     FBase.RestoreCursor;
3764     end;
3765     end;
3766    
3767     procedure TIBCustomDataSet.InternalFirst;
3768     begin
3769     FCurrentRecord := -1;
3770     end;
3771    
3772     procedure TIBCustomDataSet.InternalGotoBookmark(Bookmark: Pointer);
3773     begin
3774     FCurrentRecord := PInteger(Bookmark)^;
3775     end;
3776    
3777     procedure TIBCustomDataSet.InternalHandleException;
3778     begin
3779     FBase.HandleException(Self)
3780     end;
3781    
3782     procedure TIBCustomDataSet.InternalInitFieldDefs;
3783     begin
3784     if not InternalPrepared then
3785     begin
3786     InternalPrepare;
3787     exit;
3788     end;
3789     FieldDefsFromQuery(FQSelect);
3790     end;
3791    
3792     procedure TIBCustomDataSet.FieldDefsFromQuery(SourceQuery: TIBSQL);
3793     const
3794     DefaultSQL = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3795     'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME ' + {do not localize}
3796     'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3797     'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3798     'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3799     'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3800     ' (not F.RDB$DEFAULT_VALUE is NULL)) '; {do not localize}
3801 tony 101
3802     DefaultSQLODS12 = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3803     'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME, R.RDB$IDENTITY_TYPE ' + {do not localize}
3804     'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3805     'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3806     'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3807     'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3808     ' (not F.RDB$DEFAULT_VALUE is NULL) or ' + {do not localize}
3809     ' ( not R.RDB$IDENTITY_TYPE is NULL))' ; {do not localize}
3810    
3811 tony 33 var
3812     FieldType: TFieldType;
3813     FieldSize: Word;
3814 tony 66 FieldDataSize: integer;
3815 tony 45 charSetID: short;
3816 tony 33 CharSetSize: integer;
3817 tony 39 CharSetName: RawByteString;
3818     FieldCodePage: TSystemCodePage;
3819 tony 33 FieldNullable : Boolean;
3820     i, FieldPosition, FieldPrecision: Integer;
3821     FieldAliasName, DBAliasName: string;
3822 tony 45 aRelationName, FieldName: string;
3823 tony 33 Query : TIBSQL;
3824     FieldIndex: Integer;
3825     FRelationNodes : TRelationNode;
3826 tony 45 aArrayDimensions: integer;
3827     aArrayBounds: TArrayBounds;
3828     ArrayMetaData: IArrayMetaData;
3829 tony 33
3830     function Add_Node(Relation, Field : String) : TRelationNode;
3831     var
3832     FField : TFieldNode;
3833     begin
3834     if FRelationNodes.RelationName = '' then
3835     Result := FRelationNodes
3836     else
3837     begin
3838     Result := TRelationNode.Create;
3839     Result.NextRelation := FRelationNodes;
3840     end;
3841     Result.RelationName := Relation;
3842     FRelationNodes := Result;
3843     Query.Params[0].AsString := Relation;
3844     Query.ExecQuery;
3845     while not Query.Eof do
3846     begin
3847     FField := TFieldNode.Create;
3848     FField.FieldName := Query.Fields[2].AsString;
3849     FField.DEFAULT_VALUE := not Query.Fields[1].IsNull;
3850     FField.COMPUTED_BLR := not Query.Fields[0].IsNull;
3851 tony 101 FField.IDENTITY_COLUMN := (Query.FieldCount > 3) and not Query.Fields[3].IsNull;
3852 tony 33 FField.NextField := Result.FieldNodes;
3853     Result.FieldNodes := FField;
3854     Query.Next;
3855     end;
3856     Query.Close;
3857     end;
3858    
3859     function Has_COMPUTED_BLR(Relation, Field : String) : Boolean;
3860     var
3861     FRelation : TRelationNode;
3862     FField : TFieldNode;
3863     begin
3864     FRelation := FRelationNodes;
3865     while Assigned(FRelation) and
3866     (FRelation.RelationName <> Relation) do
3867     FRelation := FRelation.NextRelation;
3868     if not Assigned(FRelation) then
3869     FRelation := Add_Node(Relation, Field);
3870     Result := false;
3871     FField := FRelation.FieldNodes;
3872     while Assigned(FField) do
3873     if FField.FieldName = Field then
3874     begin
3875     Result := Ffield.COMPUTED_BLR;
3876     Exit;
3877     end
3878     else
3879     FField := Ffield.NextField;
3880     end;
3881    
3882     function Has_DEFAULT_VALUE(Relation, Field : String) : Boolean;
3883     var
3884     FRelation : TRelationNode;
3885     FField : TFieldNode;
3886     begin
3887     FRelation := FRelationNodes;
3888     while Assigned(FRelation) and
3889     (FRelation.RelationName <> Relation) do
3890     FRelation := FRelation.NextRelation;
3891     if not Assigned(FRelation) then
3892     FRelation := Add_Node(Relation, Field);
3893     Result := false;
3894     FField := FRelation.FieldNodes;
3895     while Assigned(FField) do
3896     if FField.FieldName = Field then
3897     begin
3898     Result := Ffield.DEFAULT_VALUE;
3899     Exit;
3900     end
3901     else
3902     FField := Ffield.NextField;
3903     end;
3904    
3905 tony 101 function Is_IDENTITY_COLUMN(Relation, Field : String) : Boolean;
3906     var
3907     FRelation : TRelationNode;
3908     FField : TFieldNode;
3909     begin
3910     FRelation := FRelationNodes;
3911     while Assigned(FRelation) and
3912     (FRelation.RelationName <> Relation) do
3913     FRelation := FRelation.NextRelation;
3914     if not Assigned(FRelation) then
3915     FRelation := Add_Node(Relation, Field);
3916     Result := false;
3917     FField := FRelation.FieldNodes;
3918     while Assigned(FField) do
3919     if FField.FieldName = Field then
3920     begin
3921     Result := Ffield.IDENTITY_COLUMN;
3922     Exit;
3923     end
3924     else
3925     FField := Ffield.NextField;
3926     end;
3927    
3928 tony 33 Procedure FreeNodes;
3929     var
3930     FRelation : TRelationNode;
3931     FField : TFieldNode;
3932     begin
3933     while Assigned(FRelationNodes) do
3934     begin
3935     While Assigned(FRelationNodes.FieldNodes) do
3936     begin
3937     FField := FRelationNodes.FieldNodes.NextField;
3938     FRelationNodes.FieldNodes.Free;
3939     FRelationNodes.FieldNodes := FField;
3940     end;
3941     FRelation := FRelationNodes.NextRelation;
3942     FRelationNodes.Free;
3943     FRelationNodes := FRelation;
3944     end;
3945     end;
3946    
3947     begin
3948     FRelationNodes := TRelationNode.Create;
3949     FNeedsRefresh := False;
3950 tony 45 if not Database.InternalTransaction.InTransaction then
3951     Database.InternalTransaction.StartTransaction;
3952 tony 33 Query := TIBSQL.Create(self);
3953     try
3954     Query.Database := DataBase;
3955     Query.Transaction := Database.InternalTransaction;
3956     FieldDefs.BeginUpdate;
3957     FieldDefs.Clear;
3958     FieldIndex := 0;
3959 tony 45 if (Length(FMappedFieldPosition) < SourceQuery.MetaData.Count) then
3960     SetLength(FMappedFieldPosition, SourceQuery.MetaData.Count);
3961 tony 101 if FDatabaseInfo.ODSMajorVersion >= 12 then
3962     Query.SQL.Text := DefaultSQLODS12
3963     else
3964     Query.SQL.Text := DefaultSQL;
3965 tony 33 Query.Prepare;
3966 tony 45 SetLength(FAliasNameMap, SourceQuery.MetaData.Count);
3967     SetLength(FAliasNameList, SourceQuery.MetaData.Count);
3968     for i := 0 to SourceQuery.MetaData.GetCount - 1 do
3969     with SourceQuery.MetaData[i] do
3970 tony 33 begin
3971     { Get the field name }
3972 tony 45 FieldAliasName := GetName;
3973     DBAliasName := GetAliasname;
3974     aRelationName := getRelationName;
3975     FieldName := getSQLName;
3976 tony 33 FAliasNameList[i] := DBAliasName;
3977     FieldSize := 0;
3978 tony 67 FieldDataSize := GetSize;
3979 tony 33 FieldPrecision := 0;
3980 tony 45 FieldNullable := IsNullable;
3981 tony 35 CharSetSize := 0;
3982     CharSetName := '';
3983 tony 39 FieldCodePage := CP_NONE;
3984 tony 45 aArrayDimensions := 0;
3985     SetLength(aArrayBounds,0);
3986     case SQLType of
3987 tony 33 { All VARCHAR's must be converted to strings before recording
3988     their values }
3989     SQL_VARYING, SQL_TEXT:
3990     begin
3991 tony 60 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
3992     CharSetSize := 1;
3993     CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
3994     Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
3995 tony 66 FieldSize := FieldDataSize div CharSetSize;
3996 tony 43 FieldType := ftString;
3997 tony 33 end;
3998     { All Doubles/Floats should be cast to doubles }
3999     SQL_DOUBLE, SQL_FLOAT:
4000     FieldType := ftFloat;
4001     SQL_SHORT:
4002     begin
4003 tony 45 if (getScale = 0) then
4004 tony 33 FieldType := ftSmallInt
4005     else begin
4006     FieldType := ftBCD;
4007     FieldPrecision := 4;
4008 tony 45 FieldSize := -getScale;
4009 tony 33 end;
4010     end;
4011     SQL_LONG:
4012     begin
4013 tony 45 if (getScale = 0) then
4014 tony 33 FieldType := ftInteger
4015 tony 45 else if (getScale >= (-4)) then
4016 tony 33 begin
4017     FieldType := ftBCD;
4018     FieldPrecision := 9;
4019 tony 45 FieldSize := -getScale;
4020 tony 33 end
4021     else
4022     if Database.SQLDialect = 1 then
4023     FieldType := ftFloat
4024     else
4025     if (FieldCount > i) and (Fields[i] is TFloatField) then
4026     FieldType := ftFloat
4027     else
4028     begin
4029     FieldType := ftFMTBCD;
4030     FieldPrecision := 9;
4031 tony 45 FieldSize := -getScale;
4032 tony 33 end;
4033     end;
4034    
4035     SQL_INT64:
4036     begin
4037 tony 45 if (getScale = 0) then
4038 tony 33 FieldType := ftLargeInt
4039 tony 45 else if (getScale >= (-4)) then
4040 tony 33 begin
4041     FieldType := ftBCD;
4042     FieldPrecision := 18;
4043 tony 45 FieldSize := -getScale;
4044 tony 33 end
4045     else
4046 tony 66 FieldType := ftFloat;
4047 tony 33 end;
4048     SQL_TIMESTAMP: FieldType := ftDateTime;
4049     SQL_TYPE_TIME: FieldType := ftTime;
4050     SQL_TYPE_DATE: FieldType := ftDate;
4051     SQL_BLOB:
4052     begin
4053     FieldSize := sizeof (TISC_QUAD);
4054 tony 45 if (getSubtype = 1) then
4055 tony 35 begin
4056 tony 60 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
4057     CharSetSize := 1;
4058     CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
4059     Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
4060 tony 43 FieldType := ftMemo;
4061 tony 35 end
4062 tony 33 else
4063     FieldType := ftBlob;
4064     end;
4065     SQL_ARRAY:
4066     begin
4067     FieldSize := sizeof (TISC_QUAD);
4068 tony 45 FieldType := ftArray;
4069     ArrayMetaData := GetArrayMetaData;
4070     if ArrayMetaData <> nil then
4071     begin
4072     aArrayDimensions := ArrayMetaData.GetDimensions;
4073     aArrayBounds := ArrayMetaData.GetBounds;
4074     end;
4075 tony 33 end;
4076     SQL_BOOLEAN:
4077     FieldType:= ftBoolean;
4078     else
4079     FieldType := ftUnknown;
4080     end;
4081     FieldPosition := i + 1;
4082     if (FieldType <> ftUnknown) and (FieldAliasName <> 'IBX_INTERNAL_DBKEY') then {do not localize}
4083     begin
4084     FMappedFieldPosition[FieldIndex] := FieldPosition;
4085     Inc(FieldIndex);
4086 tony 35 with TIBFieldDef.Create(FieldDefs,'',FieldType,0,False,FieldDefs.Count+1) do
4087 tony 33 begin
4088     Name := FieldAliasName;
4089     FAliasNameMap[FieldNo-1] := DBAliasName;
4090     Size := FieldSize;
4091 tony 66 DataSize := FieldDataSize;
4092 tony 33 Precision := FieldPrecision;
4093     Required := not FieldNullable;
4094 tony 45 RelationName := aRelationName;
4095 tony 33 InternalCalcField := False;
4096 tony 35 CharacterSetSize := CharSetSize;
4097     CharacterSetName := CharSetName;
4098 tony 39 CodePage := FieldCodePage;
4099 tony 45 ArrayDimensions := aArrayDimensions;
4100     ArrayBounds := aArrayBounds;
4101 tony 33 if (FieldName <> '') and (RelationName <> '') then
4102     begin
4103 tony 101 IdentityColumn := Is_IDENTITY_COLUMN(RelationName, FieldName);
4104 tony 33 if Has_COMPUTED_BLR(RelationName, FieldName) then
4105     begin
4106     Attributes := [faReadOnly];
4107     InternalCalcField := True;
4108     FNeedsRefresh := True;
4109     end
4110     else
4111     begin
4112     if Has_DEFAULT_VALUE(RelationName, FieldName) then
4113     begin
4114     if not FieldNullable then
4115     Attributes := [faRequired];
4116     end
4117     else
4118     FNeedsRefresh := True;
4119     end;
4120     end;
4121     end;
4122     end;
4123     end;
4124     finally
4125     Query.free;
4126     FreeNodes;
4127     Database.InternalTransaction.Commit;
4128     FieldDefs.EndUpdate;
4129 tony 45 FieldDefs.Updated := true;
4130 tony 33 end;
4131     end;
4132    
4133     procedure TIBCustomDataSet.InternalInitRecord(Buffer: PChar);
4134     begin
4135     CopyRecordBuffer(FModelBuffer, Buffer);
4136     end;
4137    
4138     procedure TIBCustomDataSet.InternalLast;
4139     var
4140     Buffer: PChar;
4141     begin
4142     if (FQSelect.EOF) then
4143     FCurrentRecord := FRecordCount
4144     else begin
4145     Buffer := AllocRecordBuffer;
4146     try
4147 tony 45 while FQSelect.Next do
4148 tony 33 begin
4149     FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
4150     Inc(FRecordCount);
4151     end;
4152     FCurrentRecord := FRecordCount;
4153     finally
4154     FreeRecordBuffer(Buffer);
4155     end;
4156     end;
4157     end;
4158    
4159     procedure TIBCustomDataSet.InternalSetParamsFromCursor;
4160     var
4161     i: Integer;
4162 tony 45 cur_param: ISQLParam;
4163 tony 33 cur_field: TField;
4164     s: TStream;
4165     begin
4166     if FQSelect.SQL.Text = '' then
4167     IBError(ibxeEmptyQuery, [nil]);
4168     if not FInternalPrepared then
4169     InternalPrepare;
4170 tony 45 if (SQLParams.GetCount > 0) and (DataSource <> nil) and (DataSource.DataSet <> nil) then
4171 tony 33 begin
4172 tony 45 for i := 0 to SQLParams.GetCount - 1 do
4173 tony 33 begin
4174     cur_field := DataSource.DataSet.FindField(SQLParams[i].Name);
4175     cur_param := SQLParams[i];
4176     if (cur_field <> nil) then begin
4177     if (cur_field.IsNull) then
4178     cur_param.IsNull := True
4179     else case cur_field.DataType of
4180     ftString:
4181     cur_param.AsString := cur_field.AsString;
4182     ftBoolean:
4183     cur_param.AsBoolean := cur_field.AsBoolean;
4184     ftSmallint, ftWord:
4185     cur_param.AsShort := cur_field.AsInteger;
4186     ftInteger:
4187     cur_param.AsLong := cur_field.AsInteger;
4188     ftLargeInt:
4189     cur_param.AsInt64 := TLargeIntField(cur_field).AsLargeInt;
4190     ftFloat, ftCurrency:
4191     cur_param.AsDouble := cur_field.AsFloat;
4192     ftBCD:
4193     cur_param.AsCurrency := cur_field.AsCurrency;
4194     ftDate:
4195     cur_param.AsDate := cur_field.AsDateTime;
4196     ftTime:
4197     cur_param.AsTime := cur_field.AsDateTime;
4198     ftDateTime:
4199     cur_param.AsDateTime := cur_field.AsDateTime;
4200     ftBlob, ftMemo:
4201     begin
4202     s := nil;
4203     try
4204     s := DataSource.DataSet.
4205     CreateBlobStream(cur_field, bmRead);
4206 tony 45 cur_param.AsBlob := TIBDSBlobStream(s).FBlobStream.Blob;
4207 tony 33 finally
4208     s.free;
4209     end;
4210     end;
4211 tony 45 ftArray:
4212     cur_param.AsArray := TIBArrayField(cur_field).ArrayIntf;
4213 tony 33 else
4214     IBError(ibxeNotSupported, [nil]);
4215     end;
4216     end;
4217     end;
4218     end;
4219     end;
4220    
4221     procedure TIBCustomDataSet.ReQuery;
4222     begin
4223     FQSelect.Close;
4224     ClearBlobCache;
4225     FCurrentRecord := -1;
4226     FRecordCount := 0;
4227     FDeletedRecords := 0;
4228     FBPos := 0;
4229     FOBPos := 0;
4230     FBEnd := 0;
4231     FOBEnd := 0;
4232     FQSelect.Close;
4233     FQSelect.ExecQuery;
4234     FOpen := FQSelect.Open;
4235     First;
4236     end;
4237    
4238     procedure TIBCustomDataSet.InternalOpen;
4239    
4240     function RecordDataLength(n: Integer): Long;
4241     begin
4242     result := SizeOf(TRecordData) + ((n - 1) * SizeOf(TFieldData));
4243     end;
4244    
4245     begin
4246     FBase.SetCursor;
4247     try
4248     ActivateConnection;
4249     ActivateTransaction;
4250     if FQSelect.SQL.Text = '' then
4251     IBError(ibxeEmptyQuery, [nil]);
4252     if not FInternalPrepared then
4253     InternalPrepare;
4254 tony 45 if FQSelect.SQLStatementType = SQLSelect then
4255 tony 33 begin
4256     if DefaultFields then
4257     CreateFields;
4258 tony 45 FArrayFieldCount := 0;
4259 tony 33 BindFields(True);
4260     FCurrentRecord := -1;
4261     FQSelect.ExecQuery;
4262     FOpen := FQSelect.Open;
4263    
4264     { Initialize offsets, buffer sizes, etc...
4265     1. Initially FRecordSize is just the "RecordDataLength".
4266     2. Allocate a "model" buffer and do a dummy fetch
4267     3. After the dummy fetch, FRecordSize will be appropriately
4268     adjusted to reflect the additional "weight" of the field
4269     data.
4270 tony 45 4. Set up the FCalcFieldsOffset, FBlobCacheOffset, FArrayCacheOffset and FRecordBufferSize.
4271 tony 33 5. Now, with the BufferSize available, allocate memory for chunks of records
4272     6. Re-allocate the model buffer, accounting for the new
4273     FRecordBufferSize.
4274     7. Finally, calls to AllocRecordBuffer will work!.
4275     }
4276     {Step 1}
4277 tony 45 FRecordSize := RecordDataLength(FQSelect.FieldCount);
4278 tony 33 {Step 2, 3}
4279 tony 45 GetMem(FFieldColumns,sizeof(TFieldColumns) * (FQSelect.FieldCount));
4280 tony 33 IBAlloc(FModelBuffer, 0, FRecordSize);
4281 tony 45 InitModelBuffer(FQSelect, FModelBuffer);
4282 tony 33 {Step 4}
4283     FCalcFieldsOffset := FRecordSize;
4284     FBlobCacheOffset := FCalcFieldsOffset + CalcFieldsSize;
4285 tony 45 FArrayCacheOffset := (FBlobCacheOffset + (BlobFieldCount * SizeOf(TIBBlobStream)));
4286     FRecordBufferSize := FArrayCacheOffset + (ArrayFieldCount * sizeof(IArray));
4287 tony 33 {Step 5}
4288     if UniDirectional then
4289     FBufferChunkSize := FRecordBufferSize * UniCache
4290     else
4291     FBufferChunkSize := FRecordBufferSize * BufferChunks;
4292     IBAlloc(FBufferCache, FBufferChunkSize, FBufferChunkSize);
4293     if FCachedUpdates or (csReading in ComponentState) then
4294     IBAlloc(FOldBufferCache, FBufferChunkSize, FBufferChunkSize);
4295     FBPos := 0;
4296     FOBPos := 0;
4297     FBEnd := 0;
4298     FOBEnd := 0;
4299     FCacheSize := FBufferChunkSize;
4300     FOldCacheSize := FBufferChunkSize;
4301     {Step 6}
4302 tony 45 IBAlloc(FModelBuffer, RecordDataLength(FQSelect.FieldCount),
4303 tony 33 FRecordBufferSize);
4304     {Step 7}
4305     FOldBuffer := AllocRecordBuffer;
4306     end
4307     else
4308     FQSelect.ExecQuery;
4309     finally
4310     FBase.RestoreCursor;
4311     end;
4312     end;
4313    
4314     procedure TIBCustomDataSet.InternalPost;
4315     var
4316     Qry: TIBSQL;
4317     Buff: PChar;
4318     bInserting: Boolean;
4319     begin
4320     FBase.SetCursor;
4321     try
4322     Buff := GetActiveBuf;
4323     CheckEditState;
4324     AdjustRecordOnInsert(Buff);
4325     if (State = dsInsert) then
4326     begin
4327     bInserting := True;
4328     Qry := FQInsert;
4329     PRecordData(Buff)^.rdUpdateStatus := usInserted;
4330     PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4331     WriteRecordCache(FRecordCount, Buff);
4332     FCurrentRecord := FRecordCount;
4333     end
4334     else begin
4335     bInserting := False;
4336     Qry := FQModify;
4337     if PRecordData(Buff)^.rdCachedUpdateStatus = cusUnmodified then
4338     begin
4339     PRecordData(Buff)^.rdUpdateStatus := usModified;
4340     PRecordData(Buff)^.rdCachedUpdateStatus := cusModified;
4341     end
4342     else if PRecordData(Buff)^.
4343     rdCachedUpdateStatus = cusUninserted then
4344     begin
4345     PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4346     Dec(FDeletedRecords);
4347     end;
4348     end;
4349     if (not CachedUpdates) then
4350     InternalPostRecord(Qry, Buff)
4351     else begin
4352     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4353     FUpdatesPending := True;
4354     end;
4355     if bInserting then
4356     Inc(FRecordCount);
4357     finally
4358     FBase.RestoreCursor;
4359     end;
4360     end;
4361    
4362     procedure TIBCustomDataSet.InternalRefresh;
4363     begin
4364     inherited InternalRefresh;
4365     InternalRefreshRow;
4366     end;
4367    
4368     procedure TIBCustomDataSet.InternalSetToRecord(Buffer: PChar);
4369     begin
4370     InternalGotoBookmark(@(PRecordData(Buffer)^.rdRecordNumber));
4371     end;
4372    
4373     function TIBCustomDataSet.IsCursorOpen: Boolean;
4374     begin
4375     result := FOpen;
4376     end;
4377    
4378     procedure TIBCustomDataSet.Loaded;
4379     begin
4380     if assigned(FQSelect) then
4381     FBaseSQLSelect.assign(FQSelect.SQL);
4382     inherited Loaded;
4383     end;
4384    
4385     procedure TIBCustomDataSet.Post;
4386     var CancelPost: boolean;
4387     begin
4388     CancelPost := false;
4389     if assigned(FOnValidatePost) then
4390     OnValidatePost(self,CancelPost);
4391     if CancelPost then
4392     Cancel
4393     else
4394     inherited Post;
4395     end;
4396    
4397     function TIBCustomDataSet.Locate(const KeyFields: string; const KeyValues: Variant;
4398     Options: TLocateOptions): Boolean;
4399     var
4400     CurBookmark: TBookmark;
4401     begin
4402     DisableControls;
4403     try
4404     CurBookmark := Bookmark;
4405     First;
4406     result := InternalLocate(KeyFields, KeyValues, Options);
4407     if not result then
4408     Bookmark := CurBookmark;
4409     finally
4410     EnableControls;
4411     end;
4412     end;
4413    
4414     function TIBCustomDataSet.Lookup(const KeyFields: string; const KeyValues: Variant;
4415     const ResultFields: string): Variant;
4416     var
4417     fl: TList;
4418     CurBookmark: TBookmark;
4419     begin
4420     DisableControls;
4421     fl := TList.Create;
4422     CurBookmark := Bookmark;
4423     try
4424     First;
4425     if InternalLocate(KeyFields, KeyValues, []) then
4426     begin
4427     if (ResultFields <> '') then
4428     result := FieldValues[ResultFields]
4429     else
4430     result := NULL;
4431     end
4432     else
4433     result := Null;
4434     finally
4435     Bookmark := CurBookmark;
4436     fl.Free;
4437     EnableControls;
4438     end;
4439     end;
4440    
4441     procedure TIBCustomDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
4442     begin
4443     PRecordData(Buffer)^.rdRecordNumber := PInteger(Data)^;
4444     end;
4445    
4446     procedure TIBCustomDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
4447     begin
4448     PRecordData(Buffer)^.rdBookmarkFlag := Value;
4449     end;
4450    
4451     procedure TIBCustomDataSet.SetCachedUpdates(Value: Boolean);
4452     begin
4453     if not Value and FCachedUpdates then
4454     CancelUpdates;
4455     if (not (csReading in ComponentState)) and Value then
4456     CheckDatasetClosed;
4457     FCachedUpdates := Value;
4458     end;
4459    
4460     procedure TIBCustomDataSet.SetDataSource(Value: TDataSource);
4461     begin
4462     if IsLinkedTo(Value) then
4463     IBError(ibxeCircularReference, [nil]);
4464     if FDataLink <> nil then
4465     FDataLink.DataSource := Value;
4466     end;
4467    
4468     procedure TIBCustomDataSet.InternalSetFieldData(Field: TField; Buffer: Pointer);
4469     var
4470     Buff, TmpBuff: PChar;
4471     MappedFieldPos: integer;
4472     begin
4473     Buff := GetActiveBuf;
4474     if Field.FieldNo < 0 then
4475     begin
4476     TmpBuff := Buff + FRecordSize + Field.Offset;
4477     Boolean(TmpBuff[0]) := LongBool(Buffer);
4478     if Boolean(TmpBuff[0]) then
4479     Move(Buffer^, TmpBuff[1], Field.DataSize);
4480     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4481     end
4482     else begin
4483     CheckEditState;
4484     with PRecordData(Buff)^ do
4485     begin
4486     { If inserting, Adjust record position }
4487     AdjustRecordOnInsert(Buff);
4488     MappedFieldPos := FMappedFieldPosition[Field.FieldNo - 1];
4489     if (MappedFieldPos > 0) and
4490     (MappedFieldPos <= rdFieldCount) then
4491 tony 45 with rdFields[MappedFieldPos], FFieldColumns^[MappedFieldPos] do
4492 tony 33 begin
4493     Field.Validate(Buffer);
4494     if (Buffer = nil) or
4495     (Field is TIBStringField) and (PChar(Buffer)[0] = #0) then
4496 tony 45 fdIsNull := True
4497     else
4498     begin
4499     Move(Buffer^, Buff[fdDataOfs],fdDataSize);
4500     if (fdDataType = SQL_TEXT) or (fdDataType = SQL_VARYING) then
4501     fdDataLength := StrLen(PChar(Buffer));
4502     fdIsNull := False;
4503 tony 33 if rdUpdateStatus = usUnmodified then
4504     begin
4505     if CachedUpdates then
4506     begin
4507     FUpdatesPending := True;
4508     if State = dsInsert then
4509     rdCachedUpdateStatus := cusInserted
4510     else if State = dsEdit then
4511     rdCachedUpdateStatus := cusModified;
4512     end;
4513    
4514     if State = dsInsert then
4515     rdUpdateStatus := usInserted
4516     else
4517     rdUpdateStatus := usModified;
4518     end;
4519     WriteRecordCache(rdRecordNumber, Buff);
4520     SetModified(True);
4521     end;
4522     end;
4523     end;
4524     end;
4525     if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
4526     DataEvent(deFieldChange, PtrInt(Field));
4527     end;
4528    
4529     procedure TIBCustomDataSet.SetRecNo(Value: Integer);
4530     begin
4531     CheckBrowseMode;
4532     if (Value < 1) then
4533     Value := 1
4534     else if Value > FRecordCount then
4535     begin
4536     InternalLast;
4537     Value := Min(FRecordCount, Value);
4538     end;
4539     if (Value <> RecNo) then
4540     begin
4541     DoBeforeScroll;
4542     FCurrentRecord := Value - 1;
4543     Resync([]);
4544     DoAfterScroll;
4545     end;
4546     end;
4547    
4548     procedure TIBCustomDataSet.Disconnect;
4549     begin
4550     Close;
4551     InternalUnPrepare;
4552     end;
4553    
4554     procedure TIBCustomDataSet.SetUpdateMode(const Value: TUpdateMode);
4555     begin
4556     if not CanModify then
4557     IBError(ibxeCannotUpdate, [nil])
4558     else
4559     FUpdateMode := Value;
4560     end;
4561    
4562    
4563     procedure TIBCustomDataSet.SetUpdateObject(Value: TIBDataSetUpdateObject);
4564     begin
4565     if Value <> FUpdateObject then
4566     begin
4567     if Assigned(FUpdateObject) and (FUpdateObject.DataSet = Self) then
4568     FUpdateObject.DataSet := nil;
4569     FUpdateObject := Value;
4570     if Assigned(FUpdateObject) then
4571     begin
4572     if Assigned(FUpdateObject.DataSet) and
4573     (FUpdateObject.DataSet <> Self) then
4574     FUpdateObject.DataSet.UpdateObject := nil;
4575     FUpdateObject.DataSet := Self;
4576     end;
4577     end;
4578     end;
4579    
4580     function TIBCustomDataSet.ConstraintsStored: Boolean;
4581     begin
4582     Result := Constraints.Count > 0;
4583     end;
4584    
4585     procedure TIBCustomDataSet.ClearCalcFields(Buffer: PChar);
4586     begin
4587     FillChar(Buffer[FRecordSize], CalcFieldsSize, 0);
4588     end;
4589    
4590     procedure TIBCustomDataSet.ClearIBLinks;
4591     var i: integer;
4592     begin
4593     for i := FIBLinks.Count - 1 downto 0 do
4594     TIBControlLink(FIBLinks[i]).IBDataSet := nil;
4595     end;
4596    
4597    
4598     procedure TIBCustomDataSet.InternalUnPrepare;
4599     begin
4600     if FInternalPrepared then
4601     begin
4602     CheckDatasetClosed;
4603 tony 45 if FDidActivate then
4604     DeactivateTransaction;
4605 tony 33 FieldDefs.Clear;
4606     FieldDefs.Updated := false;
4607     FInternalPrepared := False;
4608     Setlength(FAliasNameList,0);
4609     end;
4610     end;
4611    
4612     procedure TIBCustomDataSet.InternalExecQuery;
4613     var
4614     DidActivate: Boolean;
4615     begin
4616     DidActivate := False;
4617     FBase.SetCursor;
4618     try
4619     ActivateConnection;
4620     DidActivate := ActivateTransaction;
4621     if FQSelect.SQL.Text = '' then
4622     IBError(ibxeEmptyQuery, [nil]);
4623     if not FInternalPrepared then
4624     InternalPrepare;
4625 tony 45 if FQSelect.SQLStatementType = SQLSelect then
4626 tony 33 begin
4627     IBError(ibxeIsASelectStatement, [nil]);
4628     end
4629     else
4630     FQSelect.ExecQuery;
4631     finally
4632     if DidActivate then
4633     DeactivateTransaction;
4634     FBase.RestoreCursor;
4635     end;
4636     end;
4637    
4638 tony 45 function TIBCustomDataSet.GetSelectStmtIntf: IStatement;
4639 tony 33 begin
4640 tony 45 Result := FQSelect.Statement;
4641 tony 33 end;
4642    
4643     function TIBCustomDataSet.GetParser: TSelectSQLParser;
4644     begin
4645     if not assigned(FParser) then
4646     FParser := CreateParser;
4647     Result := FParser
4648     end;
4649    
4650     procedure TIBCustomDataSet.ResetParser;
4651     begin
4652     if assigned(FParser) then
4653     begin
4654     FParser.Free;
4655     FParser := nil;
4656 tony 35 FQSelect.OnSQLChanged := nil; {Do not react to change}
4657     try
4658     FQSelect.SQL.Assign(FBaseSQLSelect);
4659     finally
4660     FQSelect.OnSQLChanged := SQLChanged;
4661     end;
4662 tony 33 end;
4663     end;
4664    
4665     function TIBCustomDataSet.HasParser: boolean;
4666     begin
4667     Result := not (csDesigning in ComponentState) and (FParser <> nil)
4668     end;
4669    
4670     procedure TIBCustomDataSet.SetGenerateParamNames(AValue: Boolean);
4671     begin
4672     if FGenerateParamNames = AValue then Exit;
4673     FGenerateParamNames := AValue;
4674     Disconnect
4675     end;
4676    
4677     procedure TIBCustomDataSet.InitRecord(Buffer: PChar);
4678     begin
4679     inherited InitRecord(Buffer);
4680     with PRecordData(Buffer)^ do
4681     begin
4682     rdUpdateStatus := TUpdateStatus(usInserted);
4683     rdBookMarkFlag := bfInserted;
4684     rdRecordNumber := -1;
4685     end;
4686     end;
4687    
4688     procedure TIBCustomDataSet.InternalInsert;
4689     begin
4690     CursorPosChanged;
4691     end;
4692    
4693     { TIBDataSet IProviderSupport }
4694    
4695 tony 45 procedure TIBCustomDataSet.PSEndTransaction(Commit: Boolean);
4696 tony 33 begin
4697     if Commit then
4698     Transaction.Commit else
4699     Transaction.Rollback;
4700     end;
4701    
4702     function TIBCustomDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams;
4703     ResultSet: Pointer = nil): Integer;
4704     var
4705     FQuery: TIBQuery;
4706     begin
4707     if Assigned(ResultSet) then
4708     begin
4709     TDataSet(ResultSet^) := TIBQuery.Create(nil);
4710     with TIBQuery(ResultSet^) do
4711     begin
4712     SQL.Text := ASQL;
4713     Params.Assign(AParams);
4714     Open;
4715     Result := RowsAffected;
4716     end;
4717     end
4718     else
4719     begin
4720     FQuery := TIBQuery.Create(nil);
4721     try
4722     FQuery.Database := Database;
4723     FQuery.Transaction := Transaction;
4724     FQuery.GenerateParamNames := True;
4725     FQuery.SQL.Text := ASQL;
4726     FQuery.Params.Assign(AParams);
4727     FQuery.ExecSQL;
4728     Result := FQuery.RowsAffected;
4729     finally
4730     FQuery.Free;
4731     end;
4732     end;
4733     end;
4734    
4735     function TIBCustomDataSet.PSGetQuoteChar: string;
4736     begin
4737     if Database.SQLDialect = 3 then
4738     Result := '"' else
4739     Result := '';
4740     end;
4741    
4742     function TIBCustomDataSet.PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError;
4743     var
4744     PrevErr: Integer;
4745     begin
4746     if Prev <> nil then
4747     PrevErr := Prev.ErrorCode else
4748     PrevErr := 0;
4749     if E is EIBError then
4750     with EIBError(E) do
4751     Result := EUpdateError.Create(E.Message, '', SQLCode, PrevErr, E) else
4752     Result := inherited PSGetUpdateException(E, Prev);
4753     end;
4754    
4755     function TIBCustomDataSet.PSInTransaction: Boolean;
4756     begin
4757     Result := Transaction.InTransaction;
4758     end;
4759    
4760     function TIBCustomDataSet.PSIsSQLBased: Boolean;
4761     begin
4762     Result := True;
4763     end;
4764    
4765     function TIBCustomDataSet.PSIsSQLSupported: Boolean;
4766     begin
4767     Result := True;
4768     end;
4769    
4770     procedure TIBCustomDataSet.PSReset;
4771     begin
4772     inherited PSReset;
4773     if Active then
4774     begin
4775     Close;
4776     Open;
4777     end;
4778     end;
4779    
4780     function TIBCustomDataSet.PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean;
4781     var
4782     UpdateAction: TIBUpdateAction;
4783     SQL: string;
4784     Params: TParams;
4785    
4786     procedure AssignParams(DataSet: TDataSet; Params: TParams);
4787     var
4788     I: Integer;
4789     Old: Boolean;
4790     Param: TParam;
4791     PName: string;
4792     Field: TField;
4793     Value: Variant;
4794     begin
4795     for I := 0 to Params.Count - 1 do
4796     begin
4797     Param := Params[I];
4798     PName := Param.Name;
4799     Old := CompareText(Copy(PName, 1, 4), 'OLD_') = 0; {do not localize}
4800     if Old then System.Delete(PName, 1, 4);
4801     Field := DataSet.FindField(PName);
4802     if not Assigned(Field) then Continue;
4803     if Old then Param.AssignFieldValue(Field, Field.OldValue) else
4804     begin
4805     Value := Field.NewValue;
4806     if VarIsEmpty(Value) then Value := Field.OldValue;
4807     Param.AssignFieldValue(Field, Value);
4808     end;
4809     end;
4810     end;
4811    
4812     begin
4813     Result := False;
4814     if Assigned(OnUpdateRecord) then
4815     begin
4816     UpdateAction := uaFail;
4817     if Assigned(FOnUpdateRecord) then
4818     begin
4819     FOnUpdateRecord(Delta, UpdateKind, UpdateAction);
4820     Result := UpdateAction = uaApplied;
4821     end;
4822     end
4823     else if Assigned(FUpdateObject) then
4824     begin
4825     SQL := FUpdateObject.GetSQL(UpdateKind).Text;
4826     if SQL <> '' then
4827     begin
4828     Params := TParams.Create;
4829     try
4830     Params.ParseSQL(SQL, True);
4831     AssignParams(Delta, Params);
4832     if PSExecuteStatement(SQL, Params) = 0 then
4833     IBError(ibxeNoRecordsAffected, [nil]);
4834     Result := True;
4835     finally
4836     Params.Free;
4837     end;
4838     end;
4839     end;
4840     end;
4841    
4842     procedure TIBCustomDataSet.PSStartTransaction;
4843     begin
4844     ActivateConnection;
4845     Transaction.StartTransaction;
4846     end;
4847    
4848 tony 80 function TIBCustomDataSet.PsGetTableName: string;
4849 tony 33 begin
4850     // if not FInternalPrepared then
4851     // InternalPrepare;
4852     { It is possible for the FQSelectSQL to be unprepared
4853     with FInternalPreprepared being true (see DoBeforeTransactionEnd).
4854     So check the Prepared of the SelectSQL instead }
4855     if not FQSelect.Prepared then
4856     FQSelect.Prepare;
4857     Result := FQSelect.UniqueRelationName;
4858 tony 45 end;
4859 tony 33
4860     procedure TIBDataSet.BatchInput(InputObject: TIBBatchInput);
4861     begin
4862     InternalBatchInput(InputObject);
4863     end;
4864    
4865     procedure TIBDataSet.BatchOutput(OutputObject: TIBBatchOutput);
4866     begin
4867     InternalBatchOutput(OutputObject);
4868     end;
4869    
4870     procedure TIBDataSet.ExecSQL;
4871     begin
4872     InternalExecQuery;
4873     end;
4874    
4875     procedure TIBDataSet.Prepare;
4876     begin
4877     InternalPrepare;
4878     end;
4879    
4880     procedure TIBDataSet.UnPrepare;
4881     begin
4882     InternalUnPrepare;
4883     end;
4884    
4885     function TIBDataSet.GetPrepared: Boolean;
4886     begin
4887     Result := InternalPrepared;
4888     end;
4889    
4890     procedure TIBDataSet.InternalOpen;
4891     begin
4892     ActivateConnection;
4893     ActivateTransaction;
4894     InternalSetParamsFromCursor;
4895     Inherited InternalOpen;
4896     end;
4897    
4898     procedure TIBDataSet.SetFiltered(Value: Boolean);
4899     begin
4900     if(Filtered <> Value) then
4901     begin
4902     inherited SetFiltered(value);
4903     if Active then
4904     begin
4905     Close;
4906     Open;
4907     end;
4908     end
4909     else
4910     inherited SetFiltered(value);
4911     end;
4912    
4913     function TIBCustomDataSet.BookmarkValid(Bookmark: TBookmark): Boolean;
4914     begin
4915     Result := false;
4916     if not Assigned(Bookmark) then
4917     exit;
4918     Result := PInteger(Bookmark)^ < FRecordCount;
4919     end;
4920    
4921     function TIBCustomDataSet.GetFieldData(Field: TField;
4922     Buffer: Pointer): Boolean;
4923     {$IFDEF TBCDFIELD_IS_BCD}
4924     var
4925     lTempCurr : System.Currency;
4926     begin
4927     if (Field.DataType = ftBCD) and (Buffer <> nil) then
4928     begin
4929     Result := InternalGetFieldData(Field, @lTempCurr);
4930     if Result then
4931     CurrToBCD(lTempCurr, TBCD(Buffer^), 32, Field.Size);
4932     end
4933     else
4934     {$ELSE}
4935     begin
4936     {$ENDIF}
4937     Result := InternalGetFieldData(Field, Buffer);
4938     end;
4939    
4940     function TIBCustomDataSet.GetFieldData(Field: TField; Buffer: Pointer;
4941     NativeFormat: Boolean): Boolean;
4942     begin
4943     if (Field.DataType = ftBCD) and not NativeFormat then
4944     Result := InternalGetFieldData(Field, Buffer)
4945     else
4946     Result := inherited GetFieldData(Field, Buffer, NativeFormat);
4947     end;
4948    
4949     procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer);
4950     {$IFDEF TDBDFIELD_IS_BCD}
4951     var
4952     lTempCurr : System.Currency;
4953     begin
4954     if (Field.DataType = ftBCD) and (Buffer <> nil) then
4955     begin
4956     BCDToCurr(TBCD(Buffer^), lTempCurr);
4957     InternalSetFieldData(Field, @lTempCurr);
4958     end
4959     else
4960     {$ELSE}
4961     begin
4962     {$ENDIF}
4963     InternalSetFieldData(Field, Buffer);
4964     end;
4965    
4966     procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer;
4967     NativeFormat: Boolean);
4968     begin
4969     if (not NativeFormat) and (Field.DataType = ftBCD) then
4970     InternalSetfieldData(Field, Buffer)
4971     else
4972     inherited SetFieldData(Field, buffer, NativeFormat);
4973     end;
4974    
4975     { TIBDataSetUpdateObject }
4976    
4977     constructor TIBDataSetUpdateObject.Create(AOwner: TComponent);
4978     begin
4979     inherited Create(AOwner);
4980     FRefreshSQL := TStringList.Create;
4981     end;
4982    
4983     destructor TIBDataSetUpdateObject.Destroy;
4984     begin
4985     FRefreshSQL.Free;
4986     inherited Destroy;
4987     end;
4988    
4989 tony 80 procedure TIBDataSetUpdateObject.SetRefreshSQL(value: TStrings);
4990 tony 33 begin
4991     FRefreshSQL.Assign(Value);
4992     end;
4993    
4994 tony 80 procedure TIBDataSetUpdateObject.InternalSetParams(Params: ISQLParams;
4995     buff: PChar);
4996 tony 33 begin
4997     if not Assigned(DataSet) then Exit;
4998 tony 80 DataSet.SetInternalSQLParams(Params, buff);
4999 tony 33 end;
5000    
5001 tony 80 procedure TIBDataSetUpdateObject.InternalSetParams(Query: TIBSQL; buff: PChar);
5002     begin
5003     InternalSetParams(Query.Params,buff);
5004     end;
5005    
5006 tony 101 procedure TIBDataSetUpdateObject.UpdateRecordFromQuery(QryResults: IResults;
5007     Buffer: PChar);
5008     begin
5009     if not Assigned(DataSet) then Exit;
5010     DataSet.UpdateRecordFromQuery(QryResults, Buffer);
5011     end;
5012    
5013 tony 41 function TIBDSBlobStream.GetSize: Int64;
5014     begin
5015     Result := FBlobStream.BlobSize;
5016     end;
5017    
5018 tony 33 { TIBDSBlobStream }
5019     constructor TIBDSBlobStream.Create(AField: TField; ABlobStream: TIBBlobStream;
5020     Mode: TBlobStreamMode);
5021     begin
5022     FField := AField;
5023     FBlobStream := ABlobStream;
5024     FBlobStream.Seek(0, soFromBeginning);
5025     if (Mode = bmWrite) then
5026 tony 41 begin
5027 tony 33 FBlobStream.Truncate;
5028 tony 41 TIBCustomDataSet(FField.DataSet).RecordModified(True);
5029     TBlobField(FField).Modified := true;
5030     FHasWritten := true;
5031     end;
5032 tony 33 end;
5033    
5034     destructor TIBDSBlobStream.Destroy;
5035     begin
5036     if FHasWritten then
5037     TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
5038     inherited Destroy;
5039     end;
5040    
5041     function TIBDSBlobStream.Read(var Buffer; Count: Longint): Longint;
5042     begin
5043     result := FBlobStream.Read(Buffer, Count);
5044     end;
5045    
5046     function TIBDSBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
5047     begin
5048     result := FBlobStream.Seek(Offset, Origin);
5049     end;
5050    
5051     procedure TIBDSBlobStream.SetSize(NewSize: Longint);
5052     begin
5053     FBlobStream.SetSize(NewSize);
5054     end;
5055    
5056     function TIBDSBlobStream.Write(const Buffer; Count: Longint): Longint;
5057     begin
5058     if not (FField.DataSet.State in [dsEdit, dsInsert]) then
5059     IBError(ibxeNotEditing, [nil]);
5060     TIBCustomDataSet(FField.DataSet).RecordModified(True);
5061     TBlobField(FField).Modified := true;
5062     result := FBlobStream.Write(Buffer, Count);
5063     FHasWritten := true;
5064     { TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
5065     Removed as this caused a seek to beginning of the blob stream thus corrupting
5066     the blob stream. Moved to the destructor i.e. called after blob written}
5067     end;
5068    
5069     { TIBGenerator }
5070    
5071     procedure TIBGenerator.SetIncrement(const AValue: integer);
5072     begin
5073 tony 104 if FIncrement = AValue then Exit;
5074 tony 33 if AValue < 0 then
5075 tony 104 IBError(ibxeNegativeGenerator,[]);
5076     FIncrement := AValue;
5077     SetQuerySQL;
5078 tony 33 end;
5079    
5080 tony 104 procedure TIBGenerator.SetTransaction(AValue: TIBTransaction);
5081 tony 33 begin
5082 tony 104 FQuery.Transaction := AValue;
5083     end;
5084    
5085     procedure TIBGenerator.SetQuerySQL;
5086     begin
5087     FQuery.SQL.Text := Format('Select Gen_ID(%s,%d) From RDB$Database',[FGeneratorName,Increment]);
5088     end;
5089    
5090     function TIBGenerator.GetDatabase: TIBDatabase;
5091     begin
5092     Result := FQuery.Database;
5093     end;
5094    
5095     function TIBGenerator.GetTransaction: TIBTransaction;
5096     begin
5097     Result := FQuery.Transaction;
5098     end;
5099    
5100     procedure TIBGenerator.SetDatabase(AValue: TIBDatabase);
5101     begin
5102     FQuery.Database := AValue;
5103     end;
5104    
5105     procedure TIBGenerator.SetGeneratorName(AValue: string);
5106     begin
5107     if FGeneratorName = AValue then Exit;
5108     FGeneratorName := AValue;
5109     SetQuerySQL;
5110     end;
5111    
5112     function TIBGenerator.GetNextValue: integer;
5113     begin
5114     with FQuery do
5115     begin
5116     Transaction.Active := true;
5117 tony 33 ExecQuery;
5118     try
5119 tony 104 Result := Fields[0].AsInteger
5120 tony 33 finally
5121     Close
5122     end;
5123     end;
5124     end;
5125    
5126     constructor TIBGenerator.Create(Owner: TIBCustomDataSet);
5127     begin
5128     FOwner := Owner;
5129     FIncrement := 1;
5130 tony 104 FQuery := TIBSQL.Create(nil);
5131 tony 33 end;
5132    
5133 tony 104 destructor TIBGenerator.Destroy;
5134     begin
5135     if assigned(FQuery) then FQuery.Free;
5136     inherited Destroy;
5137     end;
5138 tony 33
5139 tony 104
5140 tony 33 procedure TIBGenerator.Apply;
5141     begin
5142 tony 104 if assigned(Database) and assigned(Transaction) and
5143     (FGeneratorName <> '') and (FFieldName <> '') and Owner.FieldByName(FFieldName).IsNull then
5144     Owner.FieldByName(FFieldName).AsInteger := GetNextValue;
5145 tony 33 end;
5146    
5147 tony 35
5148 tony 33 end.