ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 139
Committed: Wed Jan 24 16:16:29 2018 UTC (6 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 147616 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 139 Result := Utf8EscapeControlChars(TextToSingleLine(Result))
1135 tony 35 else
1136     if UTF8Length(Result) > DisplayWidth then {Show truncation with elipses}
1137 tony 139 Result := Utf8EscapeControlChars(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     FIBLinks.Add(Sender);
2943     end;
2944    
2945    
2946     procedure TIBCustomDataSet.SQLChanging(Sender: TObject);
2947     begin
2948     Active := false;
2949     { if FOpen then
2950     InternalClose;}
2951     if FInternalPrepared then
2952     InternalUnPrepare;
2953     FieldDefs.Clear;
2954     FieldDefs.Updated := false;
2955     end;
2956    
2957 tony 35 procedure TIBCustomDataSet.SQLChanged(Sender: TObject);
2958     begin
2959     FBaseSQLSelect.assign(FQSelect.SQL);
2960     end;
2961    
2962 tony 33 { I can "undelete" uninserted records (make them "inserted" again).
2963     I can "undelete" cached deleted (the deletion hasn't yet occurred) }
2964     procedure TIBCustomDataSet.Undelete;
2965     var
2966     Buff: PRecordData;
2967     begin
2968     CheckActive;
2969     Buff := PRecordData(GetActiveBuf);
2970     with Buff^ do
2971     begin
2972     if rdCachedUpdateStatus = cusUninserted then
2973     begin
2974     rdCachedUpdateStatus := cusInserted;
2975     Dec(FDeletedRecords);
2976     end
2977     else if (rdUpdateStatus = usDeleted) and
2978     (rdCachedUpdateStatus = cusDeleted) then
2979     begin
2980     rdCachedUpdateStatus := cusUnmodified;
2981     rdUpdateStatus := usUnmodified;
2982     Dec(FDeletedRecords);
2983     end;
2984     WriteRecordCache(rdRecordNumber, PChar(Buff));
2985     end;
2986     end;
2987    
2988     procedure TIBCustomDataSet.UnRegisterIBLink(Sender: TIBControlLink);
2989     begin
2990     FIBLinks.Remove(Sender);
2991     end;
2992    
2993     function TIBCustomDataSet.UpdateStatus: TUpdateStatus;
2994     begin
2995     if Active then
2996     if GetActiveBuf <> nil then
2997     result := PRecordData(GetActiveBuf)^.rdUpdateStatus
2998     else
2999     result := usUnmodified
3000     else
3001     result := usUnmodified;
3002     end;
3003    
3004     function TIBCustomDataSet.IsSequenced: Boolean;
3005     begin
3006     Result := Assigned( FQSelect ) and FQSelect.EOF;
3007     end;
3008    
3009 tony 45 function TIBCustomDataSet.ParamByName(ParamName: String): ISQLParam;
3010 tony 33 begin
3011     ActivateConnection;
3012     ActivateTransaction;
3013     if not FInternalPrepared then
3014     InternalPrepare;
3015     Result := Params.ByName(ParamName);
3016     end;
3017    
3018     {Beware: the parameter FCache is used as an identifier to determine which
3019     cache is being operated on and is not referenced in the computation.
3020     The result is an adjusted offset into the identified cache, either the
3021     Buffer Cache or the old Buffer Cache.}
3022    
3023     function TIBCustomDataSet.AdjustPosition(FCache: PChar; Offset: DWORD;
3024     Origin: Integer): DWORD;
3025     var
3026     OldCacheSize: Integer;
3027     begin
3028     if (FCache = FBufferCache) then
3029     begin
3030     case Origin of
3031     FILE_BEGIN: FBPos := Offset;
3032     FILE_CURRENT: FBPos := FBPos + Offset;
3033     FILE_END: FBPos := DWORD(FBEnd) + Offset;
3034     end;
3035     OldCacheSize := FCacheSize;
3036     while (FBPos >= DWORD(FCacheSize)) do
3037     Inc(FCacheSize, FBufferChunkSize);
3038     if FCacheSize > OldCacheSize then
3039     IBAlloc(FBufferCache, FCacheSize, FCacheSize);
3040     result := FBPos;
3041     end
3042     else begin
3043     case Origin of
3044     FILE_BEGIN: FOBPos := Offset;
3045     FILE_CURRENT: FOBPos := FOBPos + Offset;
3046     FILE_END: FOBPos := DWORD(FOBEnd) + Offset;
3047     end;
3048     OldCacheSize := FOldCacheSize;
3049     while (FBPos >= DWORD(FOldCacheSize)) do
3050     Inc(FOldCacheSize, FBufferChunkSize);
3051     if FOldCacheSize > OldCacheSize then
3052     IBAlloc(FOldBufferCache, FOldCacheSize, FOldCacheSize);
3053     result := FOBPos;
3054     end;
3055     end;
3056    
3057     procedure TIBCustomDataSet.ReadCache(FCache: PChar; Offset: DWORD; Origin: Integer;
3058     Buffer: PChar);
3059     var
3060     pCache: PChar;
3061     AdjustedOffset: DWORD;
3062     bOld: Boolean;
3063     begin
3064     bOld := (FCache = FOldBufferCache);
3065     AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
3066     if not bOld then
3067     pCache := FBufferCache + AdjustedOffset
3068     else
3069     pCache := FOldBufferCache + AdjustedOffset;
3070     Move(pCache^, Buffer^, DWORD(FRecordBufferSize));
3071     AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
3072     end;
3073    
3074     procedure TIBCustomDataSet.ReadRecordCache(RecordNumber: Integer; Buffer: PChar;
3075     ReadOldBuffer: Boolean);
3076     begin
3077     if FUniDirectional then
3078     RecordNumber := RecordNumber mod UniCache;
3079     if (ReadOldBuffer) then
3080     begin
3081     ReadRecordCache(RecordNumber, Buffer, False);
3082     if FCachedUpdates and
3083     (PRecordData(Buffer)^.rdSavedOffset <> $FFFFFFFF) then
3084     ReadCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
3085     Buffer)
3086     else
3087     if ReadOldBuffer and
3088     (PRecordData(FOldBuffer)^.rdRecordNumber = RecordNumber) then
3089     CopyRecordBuffer( FOldBuffer, Buffer )
3090     end
3091     else
3092     ReadCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
3093     end;
3094    
3095     procedure TIBCustomDataSet.WriteCache(FCache: PChar; Offset: DWORD; Origin: Integer;
3096     Buffer: PChar);
3097     var
3098     pCache: PChar;
3099     AdjustedOffset: DWORD;
3100     bOld: Boolean;
3101     dwEnd: DWORD;
3102     begin
3103     bOld := (FCache = FOldBufferCache);
3104     AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
3105     if not bOld then
3106     pCache := FBufferCache + AdjustedOffset
3107     else
3108     pCache := FOldBufferCache + AdjustedOffset;
3109     Move(Buffer^, pCache^, FRecordBufferSize);
3110     dwEnd := AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
3111     if not bOld then
3112     begin
3113     if (dwEnd > FBEnd) then
3114     FBEnd := dwEnd;
3115     end
3116     else begin
3117     if (dwEnd > FOBEnd) then
3118     FOBEnd := dwEnd;
3119     end;
3120     end;
3121    
3122     procedure TIBCustomDataSet.WriteRecordCache(RecordNumber: Integer; Buffer: PChar);
3123     begin
3124     if RecordNumber >= 0 then
3125     begin
3126     if FUniDirectional then
3127     RecordNumber := RecordNumber mod UniCache;
3128     WriteCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
3129     end;
3130     end;
3131    
3132     function TIBCustomDataSet.AllocRecordBuffer: PChar;
3133     begin
3134     result := nil;
3135     IBAlloc(result, FRecordBufferSize, FRecordBufferSize);
3136     Move(FModelBuffer^, result^, FRecordBufferSize);
3137     end;
3138    
3139     function TIBCustomDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
3140     var
3141     pb: PBlobDataArray;
3142     fs: TIBBlobStream;
3143     Buff: PChar;
3144     bTr, bDB: Boolean;
3145     begin
3146 tony 45 if (Field = nil) or (Field.DataSet <> self) then
3147     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3148 tony 33 Buff := GetActiveBuf;
3149     if Buff = nil then
3150     begin
3151     fs := TIBBlobStream.Create;
3152     fs.Mode := bmReadWrite;
3153 tony 45 fs.Database := Database;
3154     fs.Transaction := Transaction;
3155     fs.SetField(Field);
3156 tony 33 FBlobStreamList.Add(Pointer(fs));
3157     result := TIBDSBlobStream.Create(Field, fs, Mode);
3158     exit;
3159     end;
3160     pb := PBlobDataArray(Buff + FBlobCacheOffset);
3161     if pb^[Field.Offset] = nil then
3162     begin
3163     AdjustRecordOnInsert(Buff);
3164     pb^[Field.Offset] := TIBBlobStream.Create;
3165     fs := pb^[Field.Offset];
3166     FBlobStreamList.Add(Pointer(fs));
3167     fs.Mode := bmReadWrite;
3168     fs.Database := Database;
3169     fs.Transaction := Transaction;
3170 tony 45 fs.SetField(Field);
3171 tony 33 fs.BlobID :=
3172 tony 45 PISC_QUAD(@Buff[FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]].fdDataOfs])^;
3173 tony 33 if (CachedUpdates) then
3174     begin
3175     bTr := not Transaction.InTransaction;
3176     bDB := not Database.Connected;
3177     if bDB then
3178     Database.Open;
3179     if bTr then
3180     Transaction.StartTransaction;
3181     fs.Seek(0, soFromBeginning);
3182     if bTr then
3183     Transaction.Commit;
3184     if bDB then
3185     Database.Close;
3186     end;
3187     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3188     end else
3189     fs := pb^[Field.Offset];
3190     result := TIBDSBlobStream.Create(Field, fs, Mode);
3191     end;
3192    
3193 tony 45 function TIBCustomDataSet.GetArray(Field: TIBArrayField): IArray;
3194     var Buff: PChar;
3195     pda: PArrayDataArray;
3196     bTr, bDB: Boolean;
3197     begin
3198     if (Field = nil) or (Field.DataSet <> self) then
3199     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3200     Buff := GetActiveBuf;
3201     if Buff = nil then
3202     Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3203     Field.FRelationName,Field.FieldName)
3204     else
3205     begin
3206     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3207     if pda^[Field.FCacheOffset] = nil then
3208     begin
3209     AdjustRecordOnInsert(Buff);
3210     if Field.IsNull then
3211     Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3212     Field.FRelationName,Field.FieldName)
3213     else
3214     Result := Database.Attachment.OpenArray(Transaction.TransactionIntf,
3215     Field.FRelationName,Field.FieldName,Field.ArrayID);
3216     pda^[Field.FCacheOffset] := TIBArray.Create(Field,Result);
3217     FArrayList.Add(pda^[Field.FCacheOffset]);
3218     if (CachedUpdates) then
3219     begin
3220     bTr := not Transaction.InTransaction;
3221     bDB := not Database.Connected;
3222     if bDB then
3223     Database.Open;
3224     if bTr then
3225     Transaction.StartTransaction;
3226     pda^[Field.FCacheOffset].ArrayIntf.PreLoad;
3227     if bTr then
3228     Transaction.Commit;
3229     if bDB then
3230     Database.Close;
3231     end;
3232     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3233     end
3234     else
3235     Result := pda^[Field.FCacheOffset].ArrayIntf;
3236     end;
3237     end;
3238    
3239     procedure TIBCustomDataSet.SetArrayIntf(AnArray: IArray; Field: TIBArrayField);
3240     var Buff: PChar;
3241     pda: PArrayDataArray;
3242     begin
3243     if (Field = nil) or (Field.DataSet <> self) then
3244     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3245     Buff := GetActiveBuf;
3246     if Buff <> nil then
3247     begin
3248     AdjustRecordOnInsert(Buff);
3249     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3250     pda^[Field.FCacheOffset].FArray := AnArray;
3251     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3252     end;
3253     end;
3254    
3255 tony 33 function TIBCustomDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
3256     const
3257     CMPLess = -1;
3258     CMPEql = 0;
3259     CMPGtr = 1;
3260     RetCodes: array[Boolean, Boolean] of ShortInt = ((2, CMPLess),
3261     (CMPGtr, CMPEql));
3262     begin
3263     result := RetCodes[Bookmark1 = nil, Bookmark2 = nil];
3264    
3265     if Result = 2 then
3266     begin
3267     if PInteger(Bookmark1)^ < PInteger(Bookmark2)^ then
3268     Result := CMPLess
3269     else
3270     if PInteger(Bookmark1)^ > PInteger(Bookmark2)^ then
3271     Result := CMPGtr
3272     else
3273     Result := CMPEql;
3274     end;
3275     end;
3276    
3277     procedure TIBCustomDataSet.DoBeforeDelete;
3278     var
3279     Buff: PRecordData;
3280     begin
3281     if not CanDelete then
3282     IBError(ibxeCannotDelete, [nil]);
3283     Buff := PRecordData(GetActiveBuf);
3284     if FCachedUpdates and
3285     (Buff^.rdCachedUpdateStatus in [cusUnmodified]) then
3286     SaveOldBuffer(PChar(Buff));
3287     inherited DoBeforeDelete;
3288     end;
3289    
3290     procedure TIBCustomDataSet.DoAfterDelete;
3291     begin
3292     inherited DoAfterDelete;
3293     FBase.DoAfterDelete(self);
3294     InternalAutoCommit;
3295     end;
3296    
3297     procedure TIBCustomDataSet.DoBeforeEdit;
3298     var
3299     Buff: PRecordData;
3300     begin
3301     Buff := PRecordData(GetActiveBuf);
3302     if not(CanEdit or (FQModify.SQL.Count <> 0) or
3303     (FCachedUpdates and Assigned(FOnUpdateRecord))) then
3304     IBError(ibxeCannotUpdate, [nil]);
3305     if FCachedUpdates and (Buff^.rdCachedUpdateStatus in [cusUnmodified, cusInserted]) then
3306     SaveOldBuffer(PChar(Buff));
3307     CopyRecordBuffer(GetActiveBuf, FOldBuffer);
3308     inherited DoBeforeEdit;
3309     end;
3310    
3311     procedure TIBCustomDataSet.DoAfterEdit;
3312     begin
3313     inherited DoAfterEdit;
3314     FBase.DoAfterEdit(self);
3315     end;
3316    
3317     procedure TIBCustomDataSet.DoBeforeInsert;
3318     begin
3319     if not CanInsert then
3320     IBError(ibxeCannotInsert, [nil]);
3321     inherited DoBeforeInsert;
3322     end;
3323    
3324     procedure TIBCustomDataSet.DoAfterInsert;
3325     begin
3326     if GeneratorField.ApplyOnEvent = gaeOnNewRecord then
3327     GeneratorField.Apply;
3328     inherited DoAfterInsert;
3329     FBase.DoAfterInsert(self);
3330     end;
3331    
3332     procedure TIBCustomDataSet.DoBeforeClose;
3333     begin
3334     inherited DoBeforeClose;
3335 tony 45 if FInTransactionEnd and (FCloseAction = TARollback) then
3336     Exit;
3337 tony 33 if State in [dsInsert,dsEdit] then
3338     begin
3339     if DataSetCloseAction = dcSaveChanges then
3340     Post;
3341     {Note this can fail with an exception e.g. due to
3342     database validation error. In which case the dataset remains open }
3343     end;
3344 tony 45 if FCachedUpdates and FUpdatesPending and (DataSetCloseAction = dcSaveChanges) then
3345     ApplyUpdates;
3346 tony 33 end;
3347    
3348     procedure TIBCustomDataSet.DoBeforeOpen;
3349     var i: integer;
3350     begin
3351     if assigned(FParser) then
3352     FParser.Reset;
3353     for i := 0 to FIBLinks.Count - 1 do
3354     TIBControlLink(FIBLinks[i]).UpdateSQL(self);
3355     inherited DoBeforeOpen;
3356     for i := 0 to FIBLinks.Count - 1 do
3357     TIBControlLink(FIBLinks[i]).UpdateParams(self);
3358     end;
3359    
3360     procedure TIBCustomDataSet.DoBeforePost;
3361     begin
3362     inherited DoBeforePost;
3363     if (State = dsInsert) and
3364     (GeneratorField.ApplyOnEvent = gaeOnPostRecord) then
3365     GeneratorField.Apply
3366     end;
3367    
3368     procedure TIBCustomDataSet.DoAfterPost;
3369     begin
3370     inherited DoAfterPost;
3371     FBase.DoAfterPost(self);
3372     InternalAutoCommit;
3373     end;
3374    
3375     procedure TIBCustomDataSet.FetchAll;
3376     var
3377     CurBookmark: TBookmark;
3378     begin
3379     FBase.SetCursor;
3380     try
3381     if FQSelect.EOF or not FQSelect.Open then
3382     exit;
3383     DisableControls;
3384     try
3385     CurBookmark := Bookmark;
3386     Last;
3387     Bookmark := CurBookmark;
3388     finally
3389     EnableControls;
3390     end;
3391     finally
3392     FBase.RestoreCursor;
3393     end;
3394     end;
3395    
3396     procedure TIBCustomDataSet.FreeRecordBuffer(var Buffer: PChar);
3397     begin
3398     FreeMem(Buffer);
3399     Buffer := nil;
3400     end;
3401    
3402     procedure TIBCustomDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
3403     begin
3404     Move(PRecordData(Buffer)^.rdRecordNumber, Data^, BookmarkSize);
3405     end;
3406    
3407     function TIBCustomDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
3408     begin
3409     result := PRecordData(Buffer)^.rdBookmarkFlag;
3410     end;
3411    
3412     function TIBCustomDataSet.GetCanModify: Boolean;
3413     begin
3414     result := (FQInsert.SQL.Text <> '') or
3415     (FQModify.SQL.Text <> '') or
3416     (FQDelete.SQL.Text <> '') or
3417     (Assigned(FUpdateObject));
3418     end;
3419    
3420     function TIBCustomDataSet.GetCurrentRecord(Buffer: PChar): Boolean;
3421     begin
3422     if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then
3423     begin
3424     UpdateCursorPos;
3425     ReadRecordCache(PRecordData(ActiveBuffer)^.rdRecordNumber, Buffer, False);
3426     result := True;
3427     end
3428     else
3429     result := False;
3430     end;
3431    
3432     function TIBCustomDataSet.GetDataSource: TDataSource;
3433     begin
3434     if FDataLink = nil then
3435     result := nil
3436     else
3437     result := FDataLink.DataSource;
3438     end;
3439    
3440     function TIBCustomDataSet.GetDBAliasName(FieldNo: integer): string;
3441     begin
3442     Result := FAliasNameMap[FieldNo-1]
3443     end;
3444    
3445     function TIBCustomDataSet.GetFieldDefFromAlias(aliasName: string): TFieldDef;
3446     var
3447     i: integer;
3448     begin
3449     Result := nil;
3450     for i := 0 to Length(FAliasNameMap) - 1 do
3451     if FAliasNameMap[i] = aliasName then
3452     begin
3453     Result := FieldDefs[i];
3454     Exit
3455     end;
3456     end;
3457    
3458     function TIBCustomDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
3459     begin
3460     Result := DefaultFieldClasses[FieldType];
3461     end;
3462    
3463     function TIBCustomDataSet.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean;
3464     begin
3465     result := GetFieldData(FieldByNumber(FieldNo), buffer);
3466     end;
3467    
3468     function TIBCustomDataSet.InternalGetFieldData(Field: TField; Buffer: Pointer): Boolean;
3469     var
3470     Buff, Data: PChar;
3471     CurrentRecord: PRecordData;
3472     begin
3473     result := False;
3474     Buff := GetActiveBuf;
3475     if (Buff = nil) or
3476     (not IsVisible(Buff)) or not assigned(Field.DataSet) then
3477     exit;
3478     { The intention here is to stuff the buffer with the data for the
3479     referenced field for the current record }
3480     CurrentRecord := PRecordData(Buff);
3481     if (Field.FieldNo < 0) then
3482     begin
3483     Inc(Buff, FRecordSize + Field.Offset);
3484     result := Boolean(Buff[0]);
3485     if result and (Buffer <> nil) then
3486     Move(Buff[1], Buffer^, Field.DataSize);
3487     end
3488 tony 45 else
3489     if (FMappedFieldPosition[Field.FieldNo - 1] > 0) and
3490 tony 33 (FMappedFieldPosition[Field.FieldNo - 1] <= CurrentRecord^.rdFieldCount) then
3491 tony 45 with CurrentRecord^.rdFields[FMappedFieldPosition[Field.FieldNo - 1]],
3492     FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]] do
3493 tony 33 begin
3494 tony 45 result := not fdIsNull;
3495 tony 33 if result and (Buffer <> nil) then
3496     begin
3497 tony 45 Data := Buff + fdDataOfs;
3498 tony 33 if (fdDataType = SQL_VARYING) or (fdDataType = SQL_TEXT) then
3499     begin
3500     if fdDataLength < Field.DataSize then
3501     begin
3502     Move(Data^, Buffer^, fdDataLength);
3503     PChar(Buffer)[fdDataLength] := #0;
3504     end
3505     else
3506     IBError(ibxeFieldSizeError,[Field.FieldName])
3507     end
3508     else
3509     Move(Data^, Buffer^, Field.DataSize);
3510     end;
3511     end;
3512     end;
3513    
3514     { GetRecNo and SetRecNo both operate off of 1-based indexes as
3515     opposed to 0-based indexes.
3516     This is because we want LastRecordNumber/RecordCount = 1 }
3517    
3518     function TIBCustomDataSet.GetRecNo: Integer;
3519     begin
3520     if GetActiveBuf = nil then
3521     result := 0
3522     else
3523     result := PRecordData(GetActiveBuf)^.rdRecordNumber + 1;
3524     end;
3525    
3526     function TIBCustomDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
3527     DoCheck: Boolean): TGetResult;
3528     var
3529     Accept: Boolean;
3530     SaveState: TDataSetState;
3531     begin
3532     Result := grOK;
3533     if Filtered and Assigned(OnFilterRecord) then
3534     begin
3535     Accept := False;
3536     SaveState := SetTempState(dsFilter);
3537     while not Accept do
3538     begin
3539     Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3540     if Result <> grOK then
3541     break;
3542     FFilterBuffer := Buffer;
3543     try
3544     Accept := True;
3545     OnFilterRecord(Self, Accept);
3546     if not Accept and (GetMode = gmCurrent) then
3547     GetMode := gmPrior;
3548     except
3549     // FBase.HandleException(Self);
3550     end;
3551     end;
3552     RestoreState(SaveState);
3553     end
3554     else
3555     Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3556     end;
3557    
3558     function TIBCustomDataSet.InternalGetRecord(Buffer: PChar; GetMode: TGetMode;
3559     DoCheck: Boolean): TGetResult;
3560     begin
3561     result := grError;
3562     case GetMode of
3563     gmCurrent: begin
3564     if (FCurrentRecord >= 0) then begin
3565     if FCurrentRecord < FRecordCount then
3566     ReadRecordCache(FCurrentRecord, Buffer, False)
3567     else begin
3568 tony 45 while (not FQSelect.EOF) and FQSelect.Next and
3569 tony 33 (FCurrentRecord >= FRecordCount) do begin
3570     FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
3571     Inc(FRecordCount);
3572     end;
3573     FCurrentRecord := FRecordCount - 1;
3574     if (FCurrentRecord >= 0) then
3575     ReadRecordCache(FCurrentRecord, Buffer, False);
3576     end;
3577     result := grOk;
3578     end else
3579     result := grBOF;
3580     end;
3581     gmNext: begin
3582     result := grOk;
3583     if FCurrentRecord = FRecordCount then
3584     result := grEOF
3585     else if FCurrentRecord = FRecordCount - 1 then begin
3586     if (not FQSelect.EOF) then begin
3587     FQSelect.Next;
3588     Inc(FCurrentRecord);
3589     end;
3590     if (FQSelect.EOF) then begin
3591     result := grEOF;
3592     end else begin
3593     Inc(FRecordCount);
3594     FetchCurrentRecordToBuffer(FQSelect, FCurrentRecord, Buffer);
3595     end;
3596     end else if (FCurrentRecord < FRecordCount) then begin
3597     Inc(FCurrentRecord);
3598     ReadRecordCache(FCurrentRecord, Buffer, False);
3599     end;
3600     end;
3601     else { gmPrior }
3602     begin
3603     if (FCurrentRecord = 0) then begin
3604     Dec(FCurrentRecord);
3605     result := grBOF;
3606     end else if (FCurrentRecord > 0) and
3607     (FCurrentRecord <= FRecordCount) then begin
3608     Dec(FCurrentRecord);
3609     ReadRecordCache(FCurrentRecord, Buffer, False);
3610     result := grOk;
3611     end else if (FCurrentRecord = -1) then
3612     result := grBOF;
3613     end;
3614     end;
3615     if result = grOk then
3616     result := AdjustCurrentRecord(Buffer, GetMode);
3617     if result = grOk then with PRecordData(Buffer)^ do begin
3618     rdBookmarkFlag := bfCurrent;
3619     GetCalcFields(Buffer);
3620     end else if (result = grEOF) then begin
3621     CopyRecordBuffer(FModelBuffer, Buffer);
3622     PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3623     end else if (result = grBOF) then begin
3624     CopyRecordBuffer(FModelBuffer, Buffer);
3625     PRecordData(Buffer)^.rdBookmarkFlag := bfBOF;
3626     end else if (result = grError) then begin
3627     CopyRecordBuffer(FModelBuffer, Buffer);
3628     PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3629     end;;
3630     end;
3631    
3632     function TIBCustomDataSet.GetRecordCount: Integer;
3633     begin
3634     result := FRecordCount - FDeletedRecords;
3635     end;
3636    
3637     function TIBCustomDataSet.GetRecordSize: Word;
3638     begin
3639     result := FRecordBufferSize;
3640     end;
3641    
3642     procedure TIBCustomDataSet.InternalAutoCommit;
3643     begin
3644     with Transaction do
3645     if InTransaction and (FAutoCommit = acCommitRetaining) then
3646     begin
3647     if CachedUpdates then ApplyUpdates;
3648     CommitRetaining;
3649     end;
3650     end;
3651    
3652     procedure TIBCustomDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
3653     begin
3654     CheckEditState;
3655     begin
3656     { When adding records, we *always* append.
3657     Insertion is just too costly }
3658     AdjustRecordOnInsert(Buffer);
3659     with PRecordData(Buffer)^ do
3660     begin
3661     rdUpdateStatus := usInserted;
3662     rdCachedUpdateStatus := cusInserted;
3663     end;
3664     if not CachedUpdates then
3665     InternalPostRecord(FQInsert, Buffer)
3666     else begin
3667     WriteRecordCache(FCurrentRecord, Buffer);
3668     FUpdatesPending := True;
3669     end;
3670     Inc(FRecordCount);
3671     InternalSetToRecord(Buffer);
3672     end
3673     end;
3674    
3675     procedure TIBCustomDataSet.InternalCancel;
3676     var
3677     Buff: PChar;
3678     CurRec: Integer;
3679 tony 45 pda: PArrayDataArray;
3680     i: integer;
3681 tony 33 begin
3682     inherited InternalCancel;
3683     Buff := GetActiveBuf;
3684 tony 45 if Buff <> nil then
3685     begin
3686     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3687     for i := 0 to ArrayFieldCount - 1 do
3688     pda^[i].ArrayIntf.CancelChanges;
3689 tony 33 CurRec := FCurrentRecord;
3690     AdjustRecordOnInsert(Buff);
3691     if (State = dsEdit) then begin
3692     CopyRecordBuffer(FOldBuffer, Buff);
3693     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3694     end else begin
3695     CopyRecordBuffer(FModelBuffer, Buff);
3696     PRecordData(Buff)^.rdUpdateStatus := usDeleted;
3697     PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
3698     PRecordData(Buff)^.rdBookmarkFlag := bfEOF;
3699     FCurrentRecord := CurRec;
3700     end;
3701     end;
3702     end;
3703    
3704    
3705     procedure TIBCustomDataSet.InternalClose;
3706     begin
3707     if FDidActivate then
3708     DeactivateTransaction;
3709     FQSelect.Close;
3710     ClearBlobCache;
3711 tony 45 ClearArrayCache;
3712 tony 33 FreeRecordBuffer(FModelBuffer);
3713     FreeRecordBuffer(FOldBuffer);
3714     FCurrentRecord := -1;
3715     FOpen := False;
3716     FRecordCount := 0;
3717     FDeletedRecords := 0;
3718     FRecordSize := 0;
3719     FBPos := 0;
3720     FOBPos := 0;
3721     FCacheSize := 0;
3722     FOldCacheSize := 0;
3723     FBEnd := 0;
3724     FOBEnd := 0;
3725     FreeMem(FBufferCache);
3726     FBufferCache := nil;
3727 tony 45 FreeMem(FFieldColumns);
3728     FFieldColumns := nil;
3729 tony 33 FreeMem(FOldBufferCache);
3730     FOldBufferCache := nil;
3731     BindFields(False);
3732 tony 35 ResetParser;
3733 tony 33 if DefaultFields then DestroyFields;
3734     end;
3735    
3736     procedure TIBCustomDataSet.InternalDelete;
3737     var
3738     Buff: PChar;
3739     begin
3740     FBase.SetCursor;
3741     try
3742     Buff := GetActiveBuf;
3743     if CanDelete then
3744     begin
3745     if not CachedUpdates then
3746     InternalDeleteRecord(FQDelete, Buff)
3747     else
3748     begin
3749     with PRecordData(Buff)^ do
3750     begin
3751     if rdCachedUpdateStatus = cusInserted then
3752     rdCachedUpdateStatus := cusUninserted
3753     else begin
3754     rdUpdateStatus := usDeleted;
3755     rdCachedUpdateStatus := cusDeleted;
3756     end;
3757     end;
3758     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3759     end;
3760     Inc(FDeletedRecords);
3761     FUpdatesPending := True;
3762     end else
3763     IBError(ibxeCannotDelete, [nil]);
3764     finally
3765     FBase.RestoreCursor;
3766     end;
3767     end;
3768    
3769     procedure TIBCustomDataSet.InternalFirst;
3770     begin
3771     FCurrentRecord := -1;
3772     end;
3773    
3774     procedure TIBCustomDataSet.InternalGotoBookmark(Bookmark: Pointer);
3775     begin
3776     FCurrentRecord := PInteger(Bookmark)^;
3777     end;
3778    
3779     procedure TIBCustomDataSet.InternalHandleException;
3780     begin
3781     FBase.HandleException(Self)
3782     end;
3783    
3784     procedure TIBCustomDataSet.InternalInitFieldDefs;
3785     begin
3786     if not InternalPrepared then
3787     begin
3788     InternalPrepare;
3789     exit;
3790     end;
3791     FieldDefsFromQuery(FQSelect);
3792     end;
3793    
3794     procedure TIBCustomDataSet.FieldDefsFromQuery(SourceQuery: TIBSQL);
3795     const
3796     DefaultSQL = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3797     'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME ' + {do not localize}
3798     'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3799     'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3800     'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3801     'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3802     ' (not F.RDB$DEFAULT_VALUE is NULL)) '; {do not localize}
3803 tony 101
3804     DefaultSQLODS12 = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3805     'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME, R.RDB$IDENTITY_TYPE ' + {do not localize}
3806     'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3807     'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3808     'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3809     'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3810     ' (not F.RDB$DEFAULT_VALUE is NULL) or ' + {do not localize}
3811     ' ( not R.RDB$IDENTITY_TYPE is NULL))' ; {do not localize}
3812    
3813 tony 33 var
3814     FieldType: TFieldType;
3815     FieldSize: Word;
3816 tony 66 FieldDataSize: integer;
3817 tony 33 CharSetSize: integer;
3818 tony 39 CharSetName: RawByteString;
3819     FieldCodePage: TSystemCodePage;
3820 tony 33 FieldNullable : Boolean;
3821     i, FieldPosition, FieldPrecision: Integer;
3822     FieldAliasName, DBAliasName: string;
3823 tony 45 aRelationName, FieldName: string;
3824 tony 33 Query : TIBSQL;
3825     FieldIndex: Integer;
3826     FRelationNodes : TRelationNode;
3827 tony 45 aArrayDimensions: integer;
3828     aArrayBounds: TArrayBounds;
3829     ArrayMetaData: IArrayMetaData;
3830 tony 33
3831     function Add_Node(Relation, Field : String) : TRelationNode;
3832     var
3833     FField : TFieldNode;
3834     begin
3835     if FRelationNodes.RelationName = '' then
3836     Result := FRelationNodes
3837     else
3838     begin
3839     Result := TRelationNode.Create;
3840     Result.NextRelation := FRelationNodes;
3841     end;
3842     Result.RelationName := Relation;
3843     FRelationNodes := Result;
3844     Query.Params[0].AsString := Relation;
3845     Query.ExecQuery;
3846     while not Query.Eof do
3847     begin
3848     FField := TFieldNode.Create;
3849     FField.FieldName := Query.Fields[2].AsString;
3850     FField.DEFAULT_VALUE := not Query.Fields[1].IsNull;
3851     FField.COMPUTED_BLR := not Query.Fields[0].IsNull;
3852 tony 101 FField.IDENTITY_COLUMN := (Query.FieldCount > 3) and not Query.Fields[3].IsNull;
3853 tony 33 FField.NextField := Result.FieldNodes;
3854     Result.FieldNodes := FField;
3855     Query.Next;
3856     end;
3857     Query.Close;
3858     end;
3859    
3860     function Has_COMPUTED_BLR(Relation, Field : String) : Boolean;
3861     var
3862     FRelation : TRelationNode;
3863     FField : TFieldNode;
3864     begin
3865     FRelation := FRelationNodes;
3866     while Assigned(FRelation) and
3867     (FRelation.RelationName <> Relation) do
3868     FRelation := FRelation.NextRelation;
3869     if not Assigned(FRelation) then
3870     FRelation := Add_Node(Relation, Field);
3871     Result := false;
3872     FField := FRelation.FieldNodes;
3873     while Assigned(FField) do
3874     if FField.FieldName = Field then
3875     begin
3876     Result := Ffield.COMPUTED_BLR;
3877     Exit;
3878     end
3879     else
3880     FField := Ffield.NextField;
3881     end;
3882    
3883     function Has_DEFAULT_VALUE(Relation, Field : String) : Boolean;
3884     var
3885     FRelation : TRelationNode;
3886     FField : TFieldNode;
3887     begin
3888     FRelation := FRelationNodes;
3889     while Assigned(FRelation) and
3890     (FRelation.RelationName <> Relation) do
3891     FRelation := FRelation.NextRelation;
3892     if not Assigned(FRelation) then
3893     FRelation := Add_Node(Relation, Field);
3894     Result := false;
3895     FField := FRelation.FieldNodes;
3896     while Assigned(FField) do
3897     if FField.FieldName = Field then
3898     begin
3899     Result := Ffield.DEFAULT_VALUE;
3900     Exit;
3901     end
3902     else
3903     FField := Ffield.NextField;
3904     end;
3905    
3906 tony 101 function Is_IDENTITY_COLUMN(Relation, Field : String) : Boolean;
3907     var
3908     FRelation : TRelationNode;
3909     FField : TFieldNode;
3910     begin
3911     FRelation := FRelationNodes;
3912     while Assigned(FRelation) and
3913     (FRelation.RelationName <> Relation) do
3914     FRelation := FRelation.NextRelation;
3915     if not Assigned(FRelation) then
3916     FRelation := Add_Node(Relation, Field);
3917     Result := false;
3918     FField := FRelation.FieldNodes;
3919     while Assigned(FField) do
3920     if FField.FieldName = Field then
3921     begin
3922     Result := Ffield.IDENTITY_COLUMN;
3923     Exit;
3924     end
3925     else
3926     FField := Ffield.NextField;
3927     end;
3928    
3929 tony 33 Procedure FreeNodes;
3930     var
3931     FRelation : TRelationNode;
3932     FField : TFieldNode;
3933     begin
3934     while Assigned(FRelationNodes) do
3935     begin
3936     While Assigned(FRelationNodes.FieldNodes) do
3937     begin
3938     FField := FRelationNodes.FieldNodes.NextField;
3939     FRelationNodes.FieldNodes.Free;
3940     FRelationNodes.FieldNodes := FField;
3941     end;
3942     FRelation := FRelationNodes.NextRelation;
3943     FRelationNodes.Free;
3944     FRelationNodes := FRelation;
3945     end;
3946     end;
3947    
3948     begin
3949     FRelationNodes := TRelationNode.Create;
3950     FNeedsRefresh := False;
3951 tony 45 if not Database.InternalTransaction.InTransaction then
3952     Database.InternalTransaction.StartTransaction;
3953 tony 33 Query := TIBSQL.Create(self);
3954     try
3955     Query.Database := DataBase;
3956     Query.Transaction := Database.InternalTransaction;
3957     FieldDefs.BeginUpdate;
3958     FieldDefs.Clear;
3959     FieldIndex := 0;
3960 tony 45 if (Length(FMappedFieldPosition) < SourceQuery.MetaData.Count) then
3961     SetLength(FMappedFieldPosition, SourceQuery.MetaData.Count);
3962 tony 101 if FDatabaseInfo.ODSMajorVersion >= 12 then
3963     Query.SQL.Text := DefaultSQLODS12
3964     else
3965     Query.SQL.Text := DefaultSQL;
3966 tony 33 Query.Prepare;
3967 tony 45 SetLength(FAliasNameMap, SourceQuery.MetaData.Count);
3968     SetLength(FAliasNameList, SourceQuery.MetaData.Count);
3969     for i := 0 to SourceQuery.MetaData.GetCount - 1 do
3970     with SourceQuery.MetaData[i] do
3971 tony 33 begin
3972     { Get the field name }
3973 tony 45 FieldAliasName := GetName;
3974     DBAliasName := GetAliasname;
3975     aRelationName := getRelationName;
3976     FieldName := getSQLName;
3977 tony 33 FAliasNameList[i] := DBAliasName;
3978     FieldSize := 0;
3979 tony 67 FieldDataSize := GetSize;
3980 tony 33 FieldPrecision := 0;
3981 tony 45 FieldNullable := IsNullable;
3982 tony 35 CharSetSize := 0;
3983     CharSetName := '';
3984 tony 39 FieldCodePage := CP_NONE;
3985 tony 45 aArrayDimensions := 0;
3986     SetLength(aArrayBounds,0);
3987     case SQLType of
3988 tony 33 { All VARCHAR's must be converted to strings before recording
3989     their values }
3990     SQL_VARYING, SQL_TEXT:
3991     begin
3992 tony 60 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
3993     CharSetSize := 1;
3994     CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
3995     Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
3996 tony 66 FieldSize := FieldDataSize div CharSetSize;
3997 tony 43 FieldType := ftString;
3998 tony 33 end;
3999     { All Doubles/Floats should be cast to doubles }
4000     SQL_DOUBLE, SQL_FLOAT:
4001     FieldType := ftFloat;
4002     SQL_SHORT:
4003     begin
4004 tony 45 if (getScale = 0) then
4005 tony 33 FieldType := ftSmallInt
4006     else begin
4007     FieldType := ftBCD;
4008     FieldPrecision := 4;
4009 tony 45 FieldSize := -getScale;
4010 tony 33 end;
4011     end;
4012     SQL_LONG:
4013     begin
4014 tony 45 if (getScale = 0) then
4015 tony 33 FieldType := ftInteger
4016 tony 45 else if (getScale >= (-4)) then
4017 tony 33 begin
4018     FieldType := ftBCD;
4019     FieldPrecision := 9;
4020 tony 45 FieldSize := -getScale;
4021 tony 33 end
4022     else
4023     if Database.SQLDialect = 1 then
4024     FieldType := ftFloat
4025     else
4026     if (FieldCount > i) and (Fields[i] is TFloatField) then
4027     FieldType := ftFloat
4028     else
4029     begin
4030     FieldType := ftFMTBCD;
4031     FieldPrecision := 9;
4032 tony 45 FieldSize := -getScale;
4033 tony 33 end;
4034     end;
4035    
4036     SQL_INT64:
4037     begin
4038 tony 45 if (getScale = 0) then
4039 tony 33 FieldType := ftLargeInt
4040 tony 45 else if (getScale >= (-4)) then
4041 tony 33 begin
4042     FieldType := ftBCD;
4043     FieldPrecision := 18;
4044 tony 45 FieldSize := -getScale;
4045 tony 33 end
4046     else
4047 tony 66 FieldType := ftFloat;
4048 tony 33 end;
4049     SQL_TIMESTAMP: FieldType := ftDateTime;
4050     SQL_TYPE_TIME: FieldType := ftTime;
4051     SQL_TYPE_DATE: FieldType := ftDate;
4052     SQL_BLOB:
4053     begin
4054     FieldSize := sizeof (TISC_QUAD);
4055 tony 45 if (getSubtype = 1) then
4056 tony 35 begin
4057 tony 60 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
4058     CharSetSize := 1;
4059     CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
4060     Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
4061 tony 43 FieldType := ftMemo;
4062 tony 35 end
4063 tony 33 else
4064     FieldType := ftBlob;
4065     end;
4066     SQL_ARRAY:
4067     begin
4068     FieldSize := sizeof (TISC_QUAD);
4069 tony 45 FieldType := ftArray;
4070     ArrayMetaData := GetArrayMetaData;
4071     if ArrayMetaData <> nil then
4072     begin
4073     aArrayDimensions := ArrayMetaData.GetDimensions;
4074     aArrayBounds := ArrayMetaData.GetBounds;
4075     end;
4076 tony 33 end;
4077     SQL_BOOLEAN:
4078     FieldType:= ftBoolean;
4079     else
4080     FieldType := ftUnknown;
4081     end;
4082     FieldPosition := i + 1;
4083     if (FieldType <> ftUnknown) and (FieldAliasName <> 'IBX_INTERNAL_DBKEY') then {do not localize}
4084     begin
4085     FMappedFieldPosition[FieldIndex] := FieldPosition;
4086     Inc(FieldIndex);
4087 tony 35 with TIBFieldDef.Create(FieldDefs,'',FieldType,0,False,FieldDefs.Count+1) do
4088 tony 33 begin
4089     Name := FieldAliasName;
4090     FAliasNameMap[FieldNo-1] := DBAliasName;
4091     Size := FieldSize;
4092 tony 66 DataSize := FieldDataSize;
4093 tony 33 Precision := FieldPrecision;
4094     Required := not FieldNullable;
4095 tony 45 RelationName := aRelationName;
4096 tony 33 InternalCalcField := False;
4097 tony 35 CharacterSetSize := CharSetSize;
4098     CharacterSetName := CharSetName;
4099 tony 39 CodePage := FieldCodePage;
4100 tony 45 ArrayDimensions := aArrayDimensions;
4101     ArrayBounds := aArrayBounds;
4102 tony 33 if (FieldName <> '') and (RelationName <> '') then
4103     begin
4104 tony 101 IdentityColumn := Is_IDENTITY_COLUMN(RelationName, FieldName);
4105 tony 33 if Has_COMPUTED_BLR(RelationName, FieldName) then
4106     begin
4107     Attributes := [faReadOnly];
4108     InternalCalcField := True;
4109     FNeedsRefresh := True;
4110     end
4111     else
4112     begin
4113     if Has_DEFAULT_VALUE(RelationName, FieldName) then
4114     begin
4115     if not FieldNullable then
4116     Attributes := [faRequired];
4117     end
4118     else
4119     FNeedsRefresh := True;
4120     end;
4121     end;
4122     end;
4123     end;
4124     end;
4125     finally
4126     Query.free;
4127     FreeNodes;
4128     Database.InternalTransaction.Commit;
4129     FieldDefs.EndUpdate;
4130 tony 45 FieldDefs.Updated := true;
4131 tony 33 end;
4132     end;
4133    
4134     procedure TIBCustomDataSet.InternalInitRecord(Buffer: PChar);
4135     begin
4136     CopyRecordBuffer(FModelBuffer, Buffer);
4137     end;
4138    
4139     procedure TIBCustomDataSet.InternalLast;
4140     var
4141     Buffer: PChar;
4142     begin
4143     if (FQSelect.EOF) then
4144     FCurrentRecord := FRecordCount
4145     else begin
4146     Buffer := AllocRecordBuffer;
4147     try
4148 tony 45 while FQSelect.Next do
4149 tony 33 begin
4150     FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
4151     Inc(FRecordCount);
4152     end;
4153     FCurrentRecord := FRecordCount;
4154     finally
4155     FreeRecordBuffer(Buffer);
4156     end;
4157     end;
4158     end;
4159    
4160     procedure TIBCustomDataSet.InternalSetParamsFromCursor;
4161     var
4162     i: Integer;
4163 tony 45 cur_param: ISQLParam;
4164 tony 33 cur_field: TField;
4165     s: TStream;
4166     begin
4167     if FQSelect.SQL.Text = '' then
4168     IBError(ibxeEmptyQuery, [nil]);
4169     if not FInternalPrepared then
4170     InternalPrepare;
4171 tony 45 if (SQLParams.GetCount > 0) and (DataSource <> nil) and (DataSource.DataSet <> nil) then
4172 tony 33 begin
4173 tony 45 for i := 0 to SQLParams.GetCount - 1 do
4174 tony 33 begin
4175     cur_field := DataSource.DataSet.FindField(SQLParams[i].Name);
4176 tony 106 if (cur_field <> nil) then
4177     begin
4178     cur_param := SQLParams[i];
4179 tony 33 if (cur_field.IsNull) then
4180     cur_param.IsNull := True
4181 tony 106 else
4182     case cur_field.DataType of
4183 tony 33 ftString:
4184     cur_param.AsString := cur_field.AsString;
4185     ftBoolean:
4186     cur_param.AsBoolean := cur_field.AsBoolean;
4187     ftSmallint, ftWord:
4188     cur_param.AsShort := cur_field.AsInteger;
4189     ftInteger:
4190     cur_param.AsLong := cur_field.AsInteger;
4191     ftLargeInt:
4192 tony 106 cur_param.AsInt64 := cur_field.AsLargeInt;
4193 tony 33 ftFloat, ftCurrency:
4194     cur_param.AsDouble := cur_field.AsFloat;
4195     ftBCD:
4196     cur_param.AsCurrency := cur_field.AsCurrency;
4197     ftDate:
4198     cur_param.AsDate := cur_field.AsDateTime;
4199     ftTime:
4200     cur_param.AsTime := cur_field.AsDateTime;
4201     ftDateTime:
4202     cur_param.AsDateTime := cur_field.AsDateTime;
4203     ftBlob, ftMemo:
4204     begin
4205     s := nil;
4206     try
4207     s := DataSource.DataSet.
4208     CreateBlobStream(cur_field, bmRead);
4209 tony 45 cur_param.AsBlob := TIBDSBlobStream(s).FBlobStream.Blob;
4210 tony 33 finally
4211     s.free;
4212     end;
4213     end;
4214 tony 45 ftArray:
4215     cur_param.AsArray := TIBArrayField(cur_field).ArrayIntf;
4216 tony 33 else
4217     IBError(ibxeNotSupported, [nil]);
4218     end;
4219     end;
4220     end;
4221     end;
4222     end;
4223    
4224     procedure TIBCustomDataSet.ReQuery;
4225     begin
4226     FQSelect.Close;
4227     ClearBlobCache;
4228     FCurrentRecord := -1;
4229     FRecordCount := 0;
4230     FDeletedRecords := 0;
4231     FBPos := 0;
4232     FOBPos := 0;
4233     FBEnd := 0;
4234     FOBEnd := 0;
4235     FQSelect.Close;
4236     FQSelect.ExecQuery;
4237     FOpen := FQSelect.Open;
4238     First;
4239     end;
4240    
4241     procedure TIBCustomDataSet.InternalOpen;
4242    
4243     function RecordDataLength(n: Integer): Long;
4244     begin
4245     result := SizeOf(TRecordData) + ((n - 1) * SizeOf(TFieldData));
4246     end;
4247    
4248     begin
4249     FBase.SetCursor;
4250     try
4251     ActivateConnection;
4252     ActivateTransaction;
4253     if FQSelect.SQL.Text = '' then
4254     IBError(ibxeEmptyQuery, [nil]);
4255     if not FInternalPrepared then
4256     InternalPrepare;
4257 tony 45 if FQSelect.SQLStatementType = SQLSelect then
4258 tony 33 begin
4259     if DefaultFields then
4260     CreateFields;
4261 tony 45 FArrayFieldCount := 0;
4262 tony 33 BindFields(True);
4263     FCurrentRecord := -1;
4264     FQSelect.ExecQuery;
4265     FOpen := FQSelect.Open;
4266    
4267     { Initialize offsets, buffer sizes, etc...
4268     1. Initially FRecordSize is just the "RecordDataLength".
4269     2. Allocate a "model" buffer and do a dummy fetch
4270     3. After the dummy fetch, FRecordSize will be appropriately
4271     adjusted to reflect the additional "weight" of the field
4272     data.
4273 tony 45 4. Set up the FCalcFieldsOffset, FBlobCacheOffset, FArrayCacheOffset and FRecordBufferSize.
4274 tony 33 5. Now, with the BufferSize available, allocate memory for chunks of records
4275     6. Re-allocate the model buffer, accounting for the new
4276     FRecordBufferSize.
4277     7. Finally, calls to AllocRecordBuffer will work!.
4278     }
4279     {Step 1}
4280 tony 45 FRecordSize := RecordDataLength(FQSelect.FieldCount);
4281 tony 33 {Step 2, 3}
4282 tony 45 GetMem(FFieldColumns,sizeof(TFieldColumns) * (FQSelect.FieldCount));
4283 tony 33 IBAlloc(FModelBuffer, 0, FRecordSize);
4284 tony 45 InitModelBuffer(FQSelect, FModelBuffer);
4285 tony 33 {Step 4}
4286     FCalcFieldsOffset := FRecordSize;
4287     FBlobCacheOffset := FCalcFieldsOffset + CalcFieldsSize;
4288 tony 45 FArrayCacheOffset := (FBlobCacheOffset + (BlobFieldCount * SizeOf(TIBBlobStream)));
4289     FRecordBufferSize := FArrayCacheOffset + (ArrayFieldCount * sizeof(IArray));
4290 tony 33 {Step 5}
4291     if UniDirectional then
4292     FBufferChunkSize := FRecordBufferSize * UniCache
4293     else
4294     FBufferChunkSize := FRecordBufferSize * BufferChunks;
4295     IBAlloc(FBufferCache, FBufferChunkSize, FBufferChunkSize);
4296     if FCachedUpdates or (csReading in ComponentState) then
4297     IBAlloc(FOldBufferCache, FBufferChunkSize, FBufferChunkSize);
4298     FBPos := 0;
4299     FOBPos := 0;
4300     FBEnd := 0;
4301     FOBEnd := 0;
4302     FCacheSize := FBufferChunkSize;
4303     FOldCacheSize := FBufferChunkSize;
4304     {Step 6}
4305 tony 45 IBAlloc(FModelBuffer, RecordDataLength(FQSelect.FieldCount),
4306 tony 33 FRecordBufferSize);
4307     {Step 7}
4308     FOldBuffer := AllocRecordBuffer;
4309     end
4310     else
4311     FQSelect.ExecQuery;
4312     finally
4313     FBase.RestoreCursor;
4314     end;
4315     end;
4316    
4317     procedure TIBCustomDataSet.InternalPost;
4318     var
4319     Qry: TIBSQL;
4320     Buff: PChar;
4321     bInserting: Boolean;
4322     begin
4323     FBase.SetCursor;
4324     try
4325     Buff := GetActiveBuf;
4326     CheckEditState;
4327     AdjustRecordOnInsert(Buff);
4328     if (State = dsInsert) then
4329     begin
4330     bInserting := True;
4331     Qry := FQInsert;
4332     PRecordData(Buff)^.rdUpdateStatus := usInserted;
4333     PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4334     WriteRecordCache(FRecordCount, Buff);
4335     FCurrentRecord := FRecordCount;
4336     end
4337     else begin
4338     bInserting := False;
4339     Qry := FQModify;
4340     if PRecordData(Buff)^.rdCachedUpdateStatus = cusUnmodified then
4341     begin
4342     PRecordData(Buff)^.rdUpdateStatus := usModified;
4343     PRecordData(Buff)^.rdCachedUpdateStatus := cusModified;
4344     end
4345     else if PRecordData(Buff)^.
4346     rdCachedUpdateStatus = cusUninserted then
4347     begin
4348     PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4349     Dec(FDeletedRecords);
4350     end;
4351     end;
4352     if (not CachedUpdates) then
4353     InternalPostRecord(Qry, Buff)
4354     else begin
4355     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4356     FUpdatesPending := True;
4357     end;
4358     if bInserting then
4359     Inc(FRecordCount);
4360     finally
4361     FBase.RestoreCursor;
4362     end;
4363     end;
4364    
4365     procedure TIBCustomDataSet.InternalRefresh;
4366     begin
4367     inherited InternalRefresh;
4368     InternalRefreshRow;
4369     end;
4370    
4371     procedure TIBCustomDataSet.InternalSetToRecord(Buffer: PChar);
4372     begin
4373     InternalGotoBookmark(@(PRecordData(Buffer)^.rdRecordNumber));
4374     end;
4375    
4376     function TIBCustomDataSet.IsCursorOpen: Boolean;
4377     begin
4378     result := FOpen;
4379     end;
4380    
4381     procedure TIBCustomDataSet.Loaded;
4382     begin
4383     if assigned(FQSelect) then
4384     FBaseSQLSelect.assign(FQSelect.SQL);
4385     inherited Loaded;
4386     end;
4387    
4388     procedure TIBCustomDataSet.Post;
4389     var CancelPost: boolean;
4390     begin
4391     CancelPost := false;
4392     if assigned(FOnValidatePost) then
4393     OnValidatePost(self,CancelPost);
4394     if CancelPost then
4395     Cancel
4396     else
4397     inherited Post;
4398     end;
4399    
4400     function TIBCustomDataSet.Locate(const KeyFields: string; const KeyValues: Variant;
4401     Options: TLocateOptions): Boolean;
4402     var
4403     CurBookmark: TBookmark;
4404     begin
4405     DisableControls;
4406     try
4407     CurBookmark := Bookmark;
4408     First;
4409     result := InternalLocate(KeyFields, KeyValues, Options);
4410     if not result then
4411     Bookmark := CurBookmark;
4412     finally
4413     EnableControls;
4414     end;
4415     end;
4416    
4417     function TIBCustomDataSet.Lookup(const KeyFields: string; const KeyValues: Variant;
4418     const ResultFields: string): Variant;
4419     var
4420     fl: TList;
4421     CurBookmark: TBookmark;
4422     begin
4423     DisableControls;
4424     fl := TList.Create;
4425     CurBookmark := Bookmark;
4426     try
4427     First;
4428     if InternalLocate(KeyFields, KeyValues, []) then
4429     begin
4430     if (ResultFields <> '') then
4431     result := FieldValues[ResultFields]
4432     else
4433     result := NULL;
4434     end
4435     else
4436     result := Null;
4437     finally
4438     Bookmark := CurBookmark;
4439     fl.Free;
4440     EnableControls;
4441     end;
4442     end;
4443    
4444     procedure TIBCustomDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
4445     begin
4446     PRecordData(Buffer)^.rdRecordNumber := PInteger(Data)^;
4447     end;
4448    
4449     procedure TIBCustomDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
4450     begin
4451     PRecordData(Buffer)^.rdBookmarkFlag := Value;
4452     end;
4453    
4454     procedure TIBCustomDataSet.SetCachedUpdates(Value: Boolean);
4455     begin
4456     if not Value and FCachedUpdates then
4457     CancelUpdates;
4458     if (not (csReading in ComponentState)) and Value then
4459     CheckDatasetClosed;
4460     FCachedUpdates := Value;
4461     end;
4462    
4463     procedure TIBCustomDataSet.SetDataSource(Value: TDataSource);
4464     begin
4465     if IsLinkedTo(Value) then
4466     IBError(ibxeCircularReference, [nil]);
4467     if FDataLink <> nil then
4468     FDataLink.DataSource := Value;
4469     end;
4470    
4471     procedure TIBCustomDataSet.InternalSetFieldData(Field: TField; Buffer: Pointer);
4472     var
4473     Buff, TmpBuff: PChar;
4474     MappedFieldPos: integer;
4475     begin
4476     Buff := GetActiveBuf;
4477     if Field.FieldNo < 0 then
4478     begin
4479     TmpBuff := Buff + FRecordSize + Field.Offset;
4480     Boolean(TmpBuff[0]) := LongBool(Buffer);
4481     if Boolean(TmpBuff[0]) then
4482     Move(Buffer^, TmpBuff[1], Field.DataSize);
4483     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4484     end
4485     else begin
4486     CheckEditState;
4487     with PRecordData(Buff)^ do
4488     begin
4489     { If inserting, Adjust record position }
4490     AdjustRecordOnInsert(Buff);
4491     MappedFieldPos := FMappedFieldPosition[Field.FieldNo - 1];
4492     if (MappedFieldPos > 0) and
4493     (MappedFieldPos <= rdFieldCount) then
4494 tony 45 with rdFields[MappedFieldPos], FFieldColumns^[MappedFieldPos] do
4495 tony 33 begin
4496     Field.Validate(Buffer);
4497     if (Buffer = nil) or
4498     (Field is TIBStringField) and (PChar(Buffer)[0] = #0) then
4499 tony 45 fdIsNull := True
4500     else
4501     begin
4502     Move(Buffer^, Buff[fdDataOfs],fdDataSize);
4503     if (fdDataType = SQL_TEXT) or (fdDataType = SQL_VARYING) then
4504     fdDataLength := StrLen(PChar(Buffer));
4505     fdIsNull := False;
4506 tony 33 if rdUpdateStatus = usUnmodified then
4507     begin
4508     if CachedUpdates then
4509     begin
4510     FUpdatesPending := True;
4511     if State = dsInsert then
4512     rdCachedUpdateStatus := cusInserted
4513     else if State = dsEdit then
4514     rdCachedUpdateStatus := cusModified;
4515     end;
4516    
4517     if State = dsInsert then
4518     rdUpdateStatus := usInserted
4519     else
4520     rdUpdateStatus := usModified;
4521     end;
4522     WriteRecordCache(rdRecordNumber, Buff);
4523     SetModified(True);
4524     end;
4525     end;
4526     end;
4527     end;
4528     if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
4529     DataEvent(deFieldChange, PtrInt(Field));
4530     end;
4531    
4532     procedure TIBCustomDataSet.SetRecNo(Value: Integer);
4533     begin
4534     CheckBrowseMode;
4535     if (Value < 1) then
4536     Value := 1
4537     else if Value > FRecordCount then
4538     begin
4539     InternalLast;
4540     Value := Min(FRecordCount, Value);
4541     end;
4542     if (Value <> RecNo) then
4543     begin
4544     DoBeforeScroll;
4545     FCurrentRecord := Value - 1;
4546     Resync([]);
4547     DoAfterScroll;
4548     end;
4549     end;
4550    
4551     procedure TIBCustomDataSet.Disconnect;
4552     begin
4553     Close;
4554     InternalUnPrepare;
4555     end;
4556    
4557     procedure TIBCustomDataSet.SetUpdateMode(const Value: TUpdateMode);
4558     begin
4559     if not CanModify then
4560     IBError(ibxeCannotUpdate, [nil])
4561     else
4562     FUpdateMode := Value;
4563     end;
4564    
4565    
4566     procedure TIBCustomDataSet.SetUpdateObject(Value: TIBDataSetUpdateObject);
4567     begin
4568     if Value <> FUpdateObject then
4569     begin
4570     if Assigned(FUpdateObject) and (FUpdateObject.DataSet = Self) then
4571     FUpdateObject.DataSet := nil;
4572     FUpdateObject := Value;
4573     if Assigned(FUpdateObject) then
4574     begin
4575     if Assigned(FUpdateObject.DataSet) and
4576     (FUpdateObject.DataSet <> Self) then
4577     FUpdateObject.DataSet.UpdateObject := nil;
4578     FUpdateObject.DataSet := Self;
4579     end;
4580     end;
4581     end;
4582    
4583     function TIBCustomDataSet.ConstraintsStored: Boolean;
4584     begin
4585     Result := Constraints.Count > 0;
4586     end;
4587    
4588     procedure TIBCustomDataSet.ClearCalcFields(Buffer: PChar);
4589     begin
4590     FillChar(Buffer[FRecordSize], CalcFieldsSize, 0);
4591     end;
4592    
4593     procedure TIBCustomDataSet.ClearIBLinks;
4594     var i: integer;
4595     begin
4596     for i := FIBLinks.Count - 1 downto 0 do
4597     TIBControlLink(FIBLinks[i]).IBDataSet := nil;
4598     end;
4599    
4600    
4601     procedure TIBCustomDataSet.InternalUnPrepare;
4602     begin
4603     if FInternalPrepared then
4604     begin
4605     CheckDatasetClosed;
4606 tony 45 if FDidActivate then
4607     DeactivateTransaction;
4608 tony 33 FieldDefs.Clear;
4609     FieldDefs.Updated := false;
4610     FInternalPrepared := False;
4611     Setlength(FAliasNameList,0);
4612     end;
4613     end;
4614    
4615     procedure TIBCustomDataSet.InternalExecQuery;
4616     var
4617     DidActivate: Boolean;
4618     begin
4619     DidActivate := False;
4620     FBase.SetCursor;
4621     try
4622     ActivateConnection;
4623     DidActivate := ActivateTransaction;
4624     if FQSelect.SQL.Text = '' then
4625     IBError(ibxeEmptyQuery, [nil]);
4626     if not FInternalPrepared then
4627     InternalPrepare;
4628 tony 45 if FQSelect.SQLStatementType = SQLSelect then
4629 tony 33 begin
4630     IBError(ibxeIsASelectStatement, [nil]);
4631     end
4632     else
4633     FQSelect.ExecQuery;
4634     finally
4635     if DidActivate then
4636     DeactivateTransaction;
4637     FBase.RestoreCursor;
4638     end;
4639     end;
4640    
4641 tony 45 function TIBCustomDataSet.GetSelectStmtIntf: IStatement;
4642 tony 33 begin
4643 tony 45 Result := FQSelect.Statement;
4644 tony 33 end;
4645    
4646     function TIBCustomDataSet.GetParser: TSelectSQLParser;
4647     begin
4648     if not assigned(FParser) then
4649     FParser := CreateParser;
4650     Result := FParser
4651     end;
4652    
4653     procedure TIBCustomDataSet.ResetParser;
4654     begin
4655     if assigned(FParser) then
4656     begin
4657     FParser.Free;
4658     FParser := nil;
4659 tony 35 FQSelect.OnSQLChanged := nil; {Do not react to change}
4660     try
4661     FQSelect.SQL.Assign(FBaseSQLSelect);
4662     finally
4663     FQSelect.OnSQLChanged := SQLChanged;
4664     end;
4665 tony 33 end;
4666     end;
4667    
4668     function TIBCustomDataSet.HasParser: boolean;
4669     begin
4670     Result := not (csDesigning in ComponentState) and (FParser <> nil)
4671     end;
4672    
4673     procedure TIBCustomDataSet.SetGenerateParamNames(AValue: Boolean);
4674     begin
4675     if FGenerateParamNames = AValue then Exit;
4676     FGenerateParamNames := AValue;
4677     Disconnect
4678     end;
4679    
4680     procedure TIBCustomDataSet.InitRecord(Buffer: PChar);
4681     begin
4682     inherited InitRecord(Buffer);
4683     with PRecordData(Buffer)^ do
4684     begin
4685     rdUpdateStatus := TUpdateStatus(usInserted);
4686     rdBookMarkFlag := bfInserted;
4687     rdRecordNumber := -1;
4688     end;
4689     end;
4690    
4691     procedure TIBCustomDataSet.InternalInsert;
4692     begin
4693     CursorPosChanged;
4694     end;
4695    
4696     { TIBDataSet IProviderSupport }
4697    
4698 tony 45 procedure TIBCustomDataSet.PSEndTransaction(Commit: Boolean);
4699 tony 33 begin
4700     if Commit then
4701     Transaction.Commit else
4702     Transaction.Rollback;
4703     end;
4704    
4705     function TIBCustomDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams;
4706     ResultSet: Pointer = nil): Integer;
4707     var
4708     FQuery: TIBQuery;
4709     begin
4710     if Assigned(ResultSet) then
4711     begin
4712     TDataSet(ResultSet^) := TIBQuery.Create(nil);
4713     with TIBQuery(ResultSet^) do
4714     begin
4715     SQL.Text := ASQL;
4716     Params.Assign(AParams);
4717     Open;
4718     Result := RowsAffected;
4719     end;
4720     end
4721     else
4722     begin
4723     FQuery := TIBQuery.Create(nil);
4724     try
4725     FQuery.Database := Database;
4726     FQuery.Transaction := Transaction;
4727     FQuery.GenerateParamNames := True;
4728     FQuery.SQL.Text := ASQL;
4729     FQuery.Params.Assign(AParams);
4730     FQuery.ExecSQL;
4731     Result := FQuery.RowsAffected;
4732     finally
4733     FQuery.Free;
4734     end;
4735     end;
4736     end;
4737    
4738     function TIBCustomDataSet.PSGetQuoteChar: string;
4739     begin
4740     if Database.SQLDialect = 3 then
4741     Result := '"' else
4742     Result := '';
4743     end;
4744    
4745     function TIBCustomDataSet.PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError;
4746     var
4747     PrevErr: Integer;
4748     begin
4749     if Prev <> nil then
4750     PrevErr := Prev.ErrorCode else
4751     PrevErr := 0;
4752     if E is EIBError then
4753     with EIBError(E) do
4754     Result := EUpdateError.Create(E.Message, '', SQLCode, PrevErr, E) else
4755     Result := inherited PSGetUpdateException(E, Prev);
4756     end;
4757    
4758     function TIBCustomDataSet.PSInTransaction: Boolean;
4759     begin
4760     Result := Transaction.InTransaction;
4761     end;
4762    
4763     function TIBCustomDataSet.PSIsSQLBased: Boolean;
4764     begin
4765     Result := True;
4766     end;
4767    
4768     function TIBCustomDataSet.PSIsSQLSupported: Boolean;
4769     begin
4770     Result := True;
4771     end;
4772    
4773     procedure TIBCustomDataSet.PSReset;
4774     begin
4775     inherited PSReset;
4776     if Active then
4777     begin
4778     Close;
4779     Open;
4780     end;
4781     end;
4782    
4783     function TIBCustomDataSet.PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean;
4784     var
4785     UpdateAction: TIBUpdateAction;
4786     SQL: string;
4787     Params: TParams;
4788    
4789     procedure AssignParams(DataSet: TDataSet; Params: TParams);
4790     var
4791     I: Integer;
4792     Old: Boolean;
4793     Param: TParam;
4794     PName: string;
4795     Field: TField;
4796     Value: Variant;
4797     begin
4798     for I := 0 to Params.Count - 1 do
4799     begin
4800     Param := Params[I];
4801     PName := Param.Name;
4802     Old := CompareText(Copy(PName, 1, 4), 'OLD_') = 0; {do not localize}
4803     if Old then System.Delete(PName, 1, 4);
4804     Field := DataSet.FindField(PName);
4805     if not Assigned(Field) then Continue;
4806     if Old then Param.AssignFieldValue(Field, Field.OldValue) else
4807     begin
4808     Value := Field.NewValue;
4809     if VarIsEmpty(Value) then Value := Field.OldValue;
4810     Param.AssignFieldValue(Field, Value);
4811     end;
4812     end;
4813     end;
4814    
4815     begin
4816     Result := False;
4817     if Assigned(OnUpdateRecord) then
4818     begin
4819     UpdateAction := uaFail;
4820     if Assigned(FOnUpdateRecord) then
4821     begin
4822     FOnUpdateRecord(Delta, UpdateKind, UpdateAction);
4823     Result := UpdateAction = uaApplied;
4824     end;
4825     end
4826     else if Assigned(FUpdateObject) then
4827     begin
4828     SQL := FUpdateObject.GetSQL(UpdateKind).Text;
4829     if SQL <> '' then
4830     begin
4831     Params := TParams.Create;
4832     try
4833     Params.ParseSQL(SQL, True);
4834     AssignParams(Delta, Params);
4835     if PSExecuteStatement(SQL, Params) = 0 then
4836     IBError(ibxeNoRecordsAffected, [nil]);
4837     Result := True;
4838     finally
4839     Params.Free;
4840     end;
4841     end;
4842     end;
4843     end;
4844    
4845     procedure TIBCustomDataSet.PSStartTransaction;
4846     begin
4847     ActivateConnection;
4848     Transaction.StartTransaction;
4849     end;
4850    
4851 tony 80 function TIBCustomDataSet.PsGetTableName: string;
4852 tony 33 begin
4853     // if not FInternalPrepared then
4854     // InternalPrepare;
4855     { It is possible for the FQSelectSQL to be unprepared
4856     with FInternalPreprepared being true (see DoBeforeTransactionEnd).
4857     So check the Prepared of the SelectSQL instead }
4858     if not FQSelect.Prepared then
4859     FQSelect.Prepare;
4860     Result := FQSelect.UniqueRelationName;
4861 tony 45 end;
4862 tony 33
4863     procedure TIBDataSet.BatchInput(InputObject: TIBBatchInput);
4864     begin
4865     InternalBatchInput(InputObject);
4866     end;
4867    
4868     procedure TIBDataSet.BatchOutput(OutputObject: TIBBatchOutput);
4869     begin
4870     InternalBatchOutput(OutputObject);
4871     end;
4872    
4873     procedure TIBDataSet.ExecSQL;
4874     begin
4875     InternalExecQuery;
4876     end;
4877    
4878     procedure TIBDataSet.Prepare;
4879     begin
4880     InternalPrepare;
4881     end;
4882    
4883     procedure TIBDataSet.UnPrepare;
4884     begin
4885     InternalUnPrepare;
4886     end;
4887    
4888     function TIBDataSet.GetPrepared: Boolean;
4889     begin
4890     Result := InternalPrepared;
4891     end;
4892    
4893     procedure TIBDataSet.InternalOpen;
4894     begin
4895     ActivateConnection;
4896     ActivateTransaction;
4897     InternalSetParamsFromCursor;
4898     Inherited InternalOpen;
4899     end;
4900    
4901     procedure TIBDataSet.SetFiltered(Value: Boolean);
4902     begin
4903     if(Filtered <> Value) then
4904     begin
4905     inherited SetFiltered(value);
4906     if Active then
4907     begin
4908     Close;
4909     Open;
4910     end;
4911     end
4912     else
4913     inherited SetFiltered(value);
4914     end;
4915    
4916     function TIBCustomDataSet.BookmarkValid(Bookmark: TBookmark): Boolean;
4917     begin
4918     Result := false;
4919     if not Assigned(Bookmark) then
4920     exit;
4921     Result := PInteger(Bookmark)^ < FRecordCount;
4922     end;
4923    
4924     function TIBCustomDataSet.GetFieldData(Field: TField;
4925     Buffer: Pointer): Boolean;
4926     {$IFDEF TBCDFIELD_IS_BCD}
4927     var
4928     lTempCurr : System.Currency;
4929     begin
4930     if (Field.DataType = ftBCD) and (Buffer <> nil) then
4931     begin
4932     Result := InternalGetFieldData(Field, @lTempCurr);
4933     if Result then
4934     CurrToBCD(lTempCurr, TBCD(Buffer^), 32, Field.Size);
4935     end
4936     else
4937     {$ELSE}
4938     begin
4939     {$ENDIF}
4940     Result := InternalGetFieldData(Field, Buffer);
4941     end;
4942    
4943     function TIBCustomDataSet.GetFieldData(Field: TField; Buffer: Pointer;
4944     NativeFormat: Boolean): Boolean;
4945     begin
4946 tony 106 {These datatypes use IBX conventions and not TDataset conventions}
4947     if (Field.DataType in [ftBCD,ftDateTime,ftDate,ftTime]) and not NativeFormat then
4948 tony 33 Result := InternalGetFieldData(Field, Buffer)
4949     else
4950     Result := inherited GetFieldData(Field, Buffer, NativeFormat);
4951     end;
4952    
4953     procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer);
4954     {$IFDEF TDBDFIELD_IS_BCD}
4955     var
4956     lTempCurr : System.Currency;
4957     begin
4958     if (Field.DataType = ftBCD) and (Buffer <> nil) then
4959     begin
4960     BCDToCurr(TBCD(Buffer^), lTempCurr);
4961     InternalSetFieldData(Field, @lTempCurr);
4962     end
4963     else
4964     {$ELSE}
4965     begin
4966     {$ENDIF}
4967     InternalSetFieldData(Field, Buffer);
4968     end;
4969    
4970     procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer;
4971     NativeFormat: Boolean);
4972     begin
4973 tony 106 {These datatypes use IBX conventions and not TDataset conventions}
4974     if (not NativeFormat) and (Field.DataType in [ftBCD,ftDateTime,ftDate,ftTime]) then
4975 tony 33 InternalSetfieldData(Field, Buffer)
4976     else
4977     inherited SetFieldData(Field, buffer, NativeFormat);
4978     end;
4979    
4980     { TIBDataSetUpdateObject }
4981    
4982     constructor TIBDataSetUpdateObject.Create(AOwner: TComponent);
4983     begin
4984     inherited Create(AOwner);
4985     FRefreshSQL := TStringList.Create;
4986     end;
4987    
4988     destructor TIBDataSetUpdateObject.Destroy;
4989     begin
4990     FRefreshSQL.Free;
4991     inherited Destroy;
4992     end;
4993    
4994 tony 80 procedure TIBDataSetUpdateObject.SetRefreshSQL(value: TStrings);
4995 tony 33 begin
4996     FRefreshSQL.Assign(Value);
4997     end;
4998    
4999 tony 80 procedure TIBDataSetUpdateObject.InternalSetParams(Params: ISQLParams;
5000     buff: PChar);
5001 tony 33 begin
5002     if not Assigned(DataSet) then Exit;
5003 tony 80 DataSet.SetInternalSQLParams(Params, buff);
5004 tony 33 end;
5005    
5006 tony 80 procedure TIBDataSetUpdateObject.InternalSetParams(Query: TIBSQL; buff: PChar);
5007     begin
5008     InternalSetParams(Query.Params,buff);
5009     end;
5010    
5011 tony 118 procedure TIBDataSetUpdateObject.UpdateRecordFromQuery(UpdateKind: TUpdateKind;
5012     QryResults: IResults; Buffer: PChar);
5013 tony 101 begin
5014     if not Assigned(DataSet) then Exit;
5015 tony 118 case UpdateKind of
5016     ukModify, ukInsert:
5017     DataSet.UpdateRecordFromQuery(QryResults, Buffer);
5018     ukDelete:
5019     DataSet.DoDeleteReturning(QryResults);
5020     end;
5021 tony 101 end;
5022    
5023 tony 41 function TIBDSBlobStream.GetSize: Int64;
5024     begin
5025     Result := FBlobStream.BlobSize;
5026     end;
5027    
5028 tony 33 { TIBDSBlobStream }
5029     constructor TIBDSBlobStream.Create(AField: TField; ABlobStream: TIBBlobStream;
5030     Mode: TBlobStreamMode);
5031     begin
5032     FField := AField;
5033     FBlobStream := ABlobStream;
5034     FBlobStream.Seek(0, soFromBeginning);
5035     if (Mode = bmWrite) then
5036 tony 41 begin
5037 tony 33 FBlobStream.Truncate;
5038 tony 41 TIBCustomDataSet(FField.DataSet).RecordModified(True);
5039     TBlobField(FField).Modified := true;
5040     FHasWritten := true;
5041     end;
5042 tony 33 end;
5043    
5044     destructor TIBDSBlobStream.Destroy;
5045     begin
5046     if FHasWritten then
5047     TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
5048     inherited Destroy;
5049     end;
5050    
5051     function TIBDSBlobStream.Read(var Buffer; Count: Longint): Longint;
5052     begin
5053     result := FBlobStream.Read(Buffer, Count);
5054     end;
5055    
5056     function TIBDSBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
5057     begin
5058     result := FBlobStream.Seek(Offset, Origin);
5059     end;
5060    
5061     procedure TIBDSBlobStream.SetSize(NewSize: Longint);
5062     begin
5063     FBlobStream.SetSize(NewSize);
5064     end;
5065    
5066     function TIBDSBlobStream.Write(const Buffer; Count: Longint): Longint;
5067     begin
5068     if not (FField.DataSet.State in [dsEdit, dsInsert]) then
5069     IBError(ibxeNotEditing, [nil]);
5070     TIBCustomDataSet(FField.DataSet).RecordModified(True);
5071     TBlobField(FField).Modified := true;
5072     result := FBlobStream.Write(Buffer, Count);
5073     FHasWritten := true;
5074     { TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
5075     Removed as this caused a seek to beginning of the blob stream thus corrupting
5076     the blob stream. Moved to the destructor i.e. called after blob written}
5077     end;
5078    
5079     { TIBGenerator }
5080    
5081     procedure TIBGenerator.SetIncrement(const AValue: integer);
5082     begin
5083 tony 104 if FIncrement = AValue then Exit;
5084 tony 33 if AValue < 0 then
5085 tony 104 IBError(ibxeNegativeGenerator,[]);
5086     FIncrement := AValue;
5087     SetQuerySQL;
5088 tony 33 end;
5089    
5090 tony 104 procedure TIBGenerator.SetTransaction(AValue: TIBTransaction);
5091 tony 33 begin
5092 tony 104 FQuery.Transaction := AValue;
5093     end;
5094    
5095     procedure TIBGenerator.SetQuerySQL;
5096     begin
5097     FQuery.SQL.Text := Format('Select Gen_ID(%s,%d) From RDB$Database',[FGeneratorName,Increment]);
5098     end;
5099    
5100     function TIBGenerator.GetDatabase: TIBDatabase;
5101     begin
5102     Result := FQuery.Database;
5103     end;
5104    
5105     function TIBGenerator.GetTransaction: TIBTransaction;
5106     begin
5107     Result := FQuery.Transaction;
5108     end;
5109    
5110     procedure TIBGenerator.SetDatabase(AValue: TIBDatabase);
5111     begin
5112     FQuery.Database := AValue;
5113     end;
5114    
5115     procedure TIBGenerator.SetGeneratorName(AValue: string);
5116     begin
5117     if FGeneratorName = AValue then Exit;
5118     FGeneratorName := AValue;
5119     SetQuerySQL;
5120     end;
5121    
5122     function TIBGenerator.GetNextValue: integer;
5123     begin
5124     with FQuery do
5125     begin
5126     Transaction.Active := true;
5127 tony 33 ExecQuery;
5128     try
5129 tony 104 Result := Fields[0].AsInteger
5130 tony 33 finally
5131     Close
5132     end;
5133     end;
5134     end;
5135    
5136     constructor TIBGenerator.Create(Owner: TIBCustomDataSet);
5137     begin
5138     FOwner := Owner;
5139     FIncrement := 1;
5140 tony 104 FQuery := TIBSQL.Create(nil);
5141 tony 33 end;
5142    
5143 tony 104 destructor TIBGenerator.Destroy;
5144     begin
5145     if assigned(FQuery) then FQuery.Free;
5146     inherited Destroy;
5147     end;
5148 tony 33
5149 tony 104
5150 tony 33 procedure TIBGenerator.Apply;
5151     begin
5152 tony 104 if assigned(Database) and assigned(Transaction) and
5153     (FGeneratorName <> '') and (FFieldName <> '') and Owner.FieldByName(FFieldName).IsNull then
5154     Owner.FieldByName(FFieldName).AsInteger := GetNextValue;
5155 tony 33 end;
5156    
5157 tony 35
5158 tony 33 end.