ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 39
Committed: Tue May 17 08:14:52 2016 UTC (8 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 137289 byte(s)
Log Message:
Committing updates for Release R1-4-1

File Contents

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