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

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