ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 19
Committed: Mon Jul 7 13:00:15 2014 UTC (9 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 122567 byte(s)
Log Message:
Committing updates for Release R1-1-0

File Contents

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