ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/runtime/nongui/IBCustomDataSet.pas
Revision: 315
Committed: Thu Feb 25 11:56:36 2021 UTC (3 years, 1 month ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/runtime/nongui/IBCustomDataSet.pas
File size: 167837 byte(s)
Log Message:
Updated for IBX 4 release

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