ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBCustomDataSet.pas
Revision: 214
Committed: Thu Mar 15 13:07:49 2018 UTC (6 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 150751 byte(s)
Log Message:
Fixes Merged

File Contents

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