ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/runtime/nongui/IBCustomDataSet.pas
Revision: 363
Committed: Tue Dec 7 13:30:05 2021 UTC (2 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 168362 byte(s)
Log Message:
add fbintf

File Contents

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