ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 23
Committed: Fri Mar 13 10:26:52 2015 UTC (9 years, 8 months ago) by tony
Content type: text/x-pascal
File size: 127086 byte(s)
Log Message:
Committing updates for Release R1-2-1

File Contents

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