ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 105
Committed: Thu Jan 18 14:37:32 2018 UTC (6 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 146898 byte(s)
Log Message:
Property Editor Fixes

File Contents

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