ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 35
Committed: Tue Jan 26 14:38:47 2016 UTC (8 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 133226 byte(s)
Log Message:
Committing updates for Release R1-3-2

File Contents

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