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