ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 11
Committed: Tue Oct 9 08:10:32 2012 UTC (12 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 116753 byte(s)
Log Message:
Committing updates for Release R1-0-2

File Contents

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