ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 43
Committed: Thu Sep 22 17:10:15 2016 UTC (7 years, 7 months ago) by tony
Content type: text/x-pascal
File size: 134829 byte(s)
Log Message:
Committing updates for Release R1-4-3

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