ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/runtime/nongui/IBCustomDataSet.pas
Revision: 270
Committed: Fri Jan 18 11:10:37 2019 UTC (5 years, 3 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/runtime/nongui/IBCustomDataSet.pas
File size: 152258 byte(s)
Log Message:
Fixes merged

File Contents

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