ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 33
Committed: Sat Jul 18 12:30:52 2015 UTC (9 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 125191 byte(s)
Log Message:
Committing updates for Release R1-3-1

File Contents

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