ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 45
Committed: Tue Dec 6 10:33:46 2016 UTC (7 years, 11 months ago) by tony
Content type: text/x-pascal
File size: 140716 byte(s)
Log Message:
Committing updates for Release R2-0-0

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