ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 67
Committed: Tue Oct 3 14:08:11 2017 UTC (6 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 141116 byte(s)
Log Message:
Property Editor positioning tidy up

File Contents

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