ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 5
Committed: Fri Feb 18 16:26:16 2011 UTC (13 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 113383 byte(s)
Log Message:
Committing updates for Release pre-release

File Contents

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