ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 60
Committed: Mon Mar 27 15:21:02 2017 UTC (7 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 140875 byte(s)
Log Message:

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 tony 57 if StringCodePage(s) <> 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 tony 58 IBAlloc(Buffer, 0, DataSize);
1212 tony 33 try
1213 tony 39 s := Value;
1214     if StringCodePage(s) <> CodePage then
1215 tony 43 SetCodePage(s,CodePage,CodePage<>CP_NONE);
1216 tony 58 StrLCopy(Buffer, PChar(s), DataSize-1);
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 tony 50 InternalUnPrepare;
1853 tony 33 if Assigned(FBeforeTransactionEnd) then
1854     FBeforeTransactionEnd(Sender);
1855     end;
1856    
1857     procedure TIBCustomDataSet.DoAfterTransactionEnd(Sender: TObject);
1858     begin
1859     if Assigned(FAfterTransactionEnd) then
1860     FAfterTransactionEnd(Sender);
1861     end;
1862    
1863     procedure TIBCustomDataSet.DoTransactionFree(Sender: TObject);
1864     begin
1865     if Assigned(FTransactionFree) then
1866     FTransactionFree(Sender);
1867     end;
1868    
1869 tony 45 procedure TIBCustomDataSet.InitModelBuffer(Qry: TIBSQL; Buffer: PChar);
1870     var i, j: Integer;
1871     FieldsLoaded: integer;
1872     p: PRecordData;
1873     colMetadata: IColumnMetaData;
1874     begin
1875     p := PRecordData(Buffer);
1876     { Get record information }
1877     p^.rdBookmarkFlag := bfCurrent;
1878     p^.rdFieldCount := Qry.FieldCount;
1879     p^.rdRecordNumber := -1;
1880     p^.rdUpdateStatus := usUnmodified;
1881     p^.rdCachedUpdateStatus := cusUnmodified;
1882     p^.rdSavedOffset := $FFFFFFFF;
1883    
1884     { Load up the fields }
1885     FieldsLoaded := FQSelect.MetaData.Count;
1886     j := 1;
1887     for i := 0 to Qry.MetaData.Count - 1 do
1888     begin
1889     if (Qry = FQSelect) then
1890     j := i + 1
1891     else
1892     begin
1893     if FieldsLoaded = 0 then
1894     break;
1895     j := FQSelect.FieldIndex[Qry[i].Name] + 1;
1896     if j < 1 then
1897     continue
1898     else
1899     Dec(FieldsLoaded);
1900     end;
1901     if j > 0 then
1902     begin
1903     colMetadata := Qry.MetaData[i];
1904     with p^.rdFields[j], FFieldColumns^[j] do
1905     begin
1906     fdDataType := colMetadata.GetSQLType;
1907     if fdDataType = SQL_BLOB then
1908     fdDataScale := 0
1909     else
1910     fdDataScale := colMetadata.getScale;
1911     fdNullable := colMetadata.getIsNullable;
1912     fdIsNull := true;
1913     fdDataSize := colMetadata.GetSize;
1914     fdDataLength := 0;
1915     fdCodePage := CP_NONE;
1916    
1917     case fdDataType of
1918     SQL_TIMESTAMP,
1919     SQL_TYPE_DATE,
1920     SQL_TYPE_TIME:
1921     fdDataSize := SizeOf(TDateTime);
1922     SQL_SHORT, SQL_LONG:
1923     begin
1924     if (fdDataScale = 0) then
1925     fdDataSize := SizeOf(Integer)
1926     else
1927     if (fdDataScale >= (-4)) then
1928     fdDataSize := SizeOf(Currency)
1929     else
1930     fdDataSize := SizeOf(Double);
1931     end;
1932     SQL_INT64:
1933     begin
1934     if (fdDataScale = 0) then
1935     fdDataSize := SizeOf(Int64)
1936     else
1937     if (fdDataScale >= (-4)) then
1938     fdDataSize := SizeOf(Currency)
1939     else
1940     fdDataSize := SizeOf(Double);
1941     end;
1942     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1943     fdDataSize := SizeOf(Double);
1944     SQL_BOOLEAN:
1945     fdDataSize := SizeOf(wordBool);
1946     SQL_VARYING,
1947     SQL_TEXT,
1948     SQL_BLOB:
1949     fdCodePage := Qry.Metadata[i].getCodePage;
1950     end;
1951     fdDataOfs := FRecordSize;
1952     Inc(FRecordSize, fdDataSize);
1953     end;
1954     end;
1955     end;
1956     end;
1957    
1958 tony 33 { Read the record from FQSelect.Current into the record buffer
1959     Then write the buffer to in memory cache }
1960     procedure TIBCustomDataSet.FetchCurrentRecordToBuffer(Qry: TIBSQL;
1961     RecordNumber: Integer; Buffer: PChar);
1962     var
1963     pbd: PBlobDataArray;
1964 tony 45 pda: PArrayDataArray;
1965 tony 33 i, j: Integer;
1966 tony 56 LocalData: PByte;
1967 tony 33 LocalDate, LocalDouble: Double;
1968     LocalInt: Integer;
1969     LocalBool: wordBool;
1970     LocalInt64: Int64;
1971     LocalCurrency: Currency;
1972     FieldsLoaded: Integer;
1973 tony 45 p: PRecordData;
1974 tony 33 begin
1975 tony 45 if RecordNumber = -1 then
1976     begin
1977     InitModelBuffer(Qry,Buffer);
1978     Exit;
1979     end;
1980 tony 33 p := PRecordData(Buffer);
1981     { Make sure blob cache is empty }
1982     pbd := PBlobDataArray(Buffer + FBlobCacheOffset);
1983 tony 45 pda := PArrayDataArray(Buffer + FArrayCacheOffset);
1984     for i := 0 to BlobFieldCount - 1 do
1985     pbd^[i] := nil;
1986     for i := 0 to ArrayFieldCount - 1 do
1987     pda^[i] := nil;
1988    
1989 tony 33 { Get record information }
1990     p^.rdBookmarkFlag := bfCurrent;
1991 tony 45 p^.rdFieldCount := Qry.FieldCount;
1992 tony 33 p^.rdRecordNumber := RecordNumber;
1993     p^.rdUpdateStatus := usUnmodified;
1994     p^.rdCachedUpdateStatus := cusUnmodified;
1995     p^.rdSavedOffset := $FFFFFFFF;
1996    
1997     { Load up the fields }
1998 tony 45 FieldsLoaded := FQSelect.MetaData.Count;
1999 tony 33 j := 1;
2000 tony 45 for i := 0 to Qry.FieldCount - 1 do
2001 tony 33 begin
2002     if (Qry = FQSelect) then
2003     j := i + 1
2004 tony 45 else
2005     begin
2006 tony 33 if FieldsLoaded = 0 then
2007     break;
2008 tony 45 j := FQSelect.FieldIndex[Qry[i].Name] + 1;
2009 tony 33 if j < 1 then
2010     continue
2011     else
2012     Dec(FieldsLoaded);
2013     end;
2014 tony 45 with FQSelect.MetaData[j - 1] do
2015     if GetAliasname = 'IBX_INTERNAL_DBKEY' then {do not localize}
2016 tony 33 begin
2017 tony 45 if (GetSize <= 8) then
2018     p^.rdDBKey := PIBDBKEY(Qry[i].AsPointer)^;
2019 tony 33 continue;
2020     end;
2021 tony 45 if j > 0 then
2022 tony 33 begin
2023 tony 45 LocalData := nil;
2024     with p^.rdFields[j], FFieldColumns^[j] do
2025     begin
2026     Qry.Current.GetData(i,fdIsNull,fdDataLength,LocalData);
2027     if not fdIsNull then
2028 tony 33 begin
2029 tony 45 case fdDataType of {Get Formatted data for column types that need formatting}
2030     SQL_TIMESTAMP:
2031     begin
2032     LocalDate := TimeStampToMSecs(DateTimeToTimeStamp(Qry[i].AsDateTime));
2033 tony 56 LocalData := PByte(@LocalDate);
2034 tony 45 end;
2035     SQL_TYPE_DATE:
2036     begin
2037     LocalInt := DateTimeToTimeStamp(Qry[i].AsDateTime).Date;
2038 tony 56 LocalData := PByte(@LocalInt);
2039 tony 45 end;
2040     SQL_TYPE_TIME:
2041     begin
2042     LocalInt := DateTimeToTimeStamp(Qry[i].AsDateTime).Time;
2043 tony 56 LocalData := PByte(@LocalInt);
2044 tony 45 end;
2045     SQL_SHORT, SQL_LONG:
2046     begin
2047     if (fdDataScale = 0) then
2048     begin
2049     LocalInt := Qry[i].AsLong;
2050 tony 56 LocalData := PByte(@LocalInt);
2051 tony 45 end
2052     else
2053     if (fdDataScale >= (-4)) then
2054     begin
2055     LocalCurrency := Qry[i].AsCurrency;
2056 tony 56 LocalData := PByte(@LocalCurrency);
2057 tony 45 end
2058     else
2059     begin
2060     LocalDouble := Qry[i].AsDouble;
2061 tony 56 LocalData := PByte(@LocalDouble);
2062 tony 33 end;
2063 tony 45 end;
2064     SQL_INT64:
2065     begin
2066     if (fdDataScale = 0) then
2067     begin
2068     LocalInt64 := Qry[i].AsInt64;
2069 tony 56 LocalData := PByte(@LocalInt64);
2070 tony 45 end
2071     else
2072     if (fdDataScale >= (-4)) then
2073     begin
2074     LocalCurrency := Qry[i].AsCurrency;
2075 tony 56 LocalData := PByte(@LocalCurrency);
2076 tony 45 end
2077     else
2078     begin
2079     LocalDouble := Qry[i].AsDouble;
2080 tony 56 LocalData := PByte(@LocalDouble);
2081 tony 45 end
2082     end;
2083     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2084     begin
2085     LocalDouble := Qry[i].AsDouble;
2086 tony 56 LocalData := PByte(@LocalDouble);
2087 tony 45 end;
2088     SQL_BOOLEAN:
2089     begin
2090     LocalBool := Qry[i].AsBoolean;
2091 tony 56 LocalData := PByte(@LocalBool);
2092 tony 45 end;
2093 tony 33 end;
2094 tony 45
2095     if fdDataType = SQL_VARYING then
2096     Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2097     else
2098     Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2099 tony 33 end
2100 tony 45 else {Null column}
2101     if fdDataType = SQL_VARYING then
2102     FillChar(Buffer[fdDataOfs],fdDataLength,0)
2103 tony 33 else
2104 tony 45 FillChar(Buffer[fdDataOfs],fdDataSize,0);
2105 tony 33 end;
2106     end;
2107     end;
2108 tony 45 WriteRecordCache(RecordNumber, Buffer);
2109 tony 33 end;
2110    
2111     function TIBCustomDataSet.GetActiveBuf: PChar;
2112     begin
2113     case State of
2114     dsBrowse:
2115     if IsEmpty then
2116     result := nil
2117     else
2118     result := ActiveBuffer;
2119     dsEdit, dsInsert:
2120     result := ActiveBuffer;
2121     dsCalcFields:
2122     result := CalcBuffer;
2123     dsFilter:
2124     result := FFilterBuffer;
2125     dsNewValue:
2126     result := ActiveBuffer;
2127     dsOldValue:
2128     if (PRecordData(ActiveBuffer)^.rdRecordNumber =
2129     PRecordData(FOldBuffer)^.rdRecordNumber) then
2130     result := FOldBuffer
2131     else
2132     result := ActiveBuffer;
2133     else if not FOpen then
2134     result := nil
2135     else
2136     result := ActiveBuffer;
2137     end;
2138     end;
2139    
2140     function TIBCustomDataSet.CachedUpdateStatus: TCachedUpdateStatus;
2141     begin
2142     if Active then
2143     result := PRecordData(GetActiveBuf)^.rdCachedUpdateStatus
2144     else
2145     result := cusUnmodified;
2146     end;
2147    
2148     function TIBCustomDataSet.GetDatabase: TIBDatabase;
2149     begin
2150     result := FBase.Database;
2151     end;
2152    
2153     function TIBCustomDataSet.GetDeleteSQL: TStrings;
2154     begin
2155     result := FQDelete.SQL;
2156     end;
2157    
2158     function TIBCustomDataSet.GetInsertSQL: TStrings;
2159     begin
2160     result := FQInsert.SQL;
2161     end;
2162    
2163 tony 45 function TIBCustomDataSet.GetSQLParams: ISQLParams;
2164 tony 33 begin
2165     if not FInternalPrepared then
2166     InternalPrepare;
2167     result := FQSelect.Params;
2168     end;
2169    
2170     function TIBCustomDataSet.GetRefreshSQL: TStrings;
2171     begin
2172     result := FQRefresh.SQL;
2173     end;
2174    
2175     function TIBCustomDataSet.GetSelectSQL: TStrings;
2176     begin
2177     result := FQSelect.SQL;
2178     end;
2179    
2180 tony 45 function TIBCustomDataSet.GetStatementType: TIBSQLStatementTypes;
2181 tony 33 begin
2182 tony 45 result := FQSelect.SQLStatementType;
2183 tony 33 end;
2184    
2185     function TIBCustomDataSet.GetModifySQL: TStrings;
2186     begin
2187     result := FQModify.SQL;
2188     end;
2189    
2190     function TIBCustomDataSet.GetTransaction: TIBTransaction;
2191     begin
2192     result := FBase.Transaction;
2193     end;
2194    
2195     procedure TIBCustomDataSet.InternalDeleteRecord(Qry: TIBSQL; Buff: Pointer);
2196     begin
2197     if (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukDelete).Text <> '')) then
2198     FUpdateObject.Apply(ukDelete,Buff)
2199     else
2200     begin
2201     SetInternalSQLParams(FQDelete, Buff);
2202     FQDelete.ExecQuery;
2203     end;
2204     with PRecordData(Buff)^ do
2205     begin
2206     rdUpdateStatus := usDeleted;
2207     rdCachedUpdateStatus := cusUnmodified;
2208     end;
2209     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2210     end;
2211    
2212     function TIBCustomDataSet.InternalLocate(const KeyFields: string;
2213     const KeyValues: Variant; Options: TLocateOptions): Boolean;
2214     var
2215     keyFieldList: TList;
2216     CurBookmark: TBookmark;
2217     fieldValue: Variant;
2218     lookupValues: array of variant;
2219     i, fieldCount: Integer;
2220     fieldValueAsString: string;
2221     lookupValueAsString: string;
2222     begin
2223     keyFieldList := TList.Create;
2224     try
2225     GetFieldList(keyFieldList, KeyFields);
2226     fieldCount := keyFieldList.Count;
2227     CurBookmark := Bookmark;
2228     result := false;
2229     SetLength(lookupValues, fieldCount);
2230     if not EOF then
2231     begin
2232     for i := 0 to fieldCount - 1 do {expand key values into lookupValues array}
2233     begin
2234     if VarIsArray(KeyValues) then
2235     lookupValues[i] := KeyValues[i]
2236     else
2237     if i > 0 then
2238     lookupValues[i] := NULL
2239     else
2240     lookupValues[0] := KeyValues;
2241    
2242     {convert to upper case is case insensitive search}
2243     if (TField(keyFieldList[i]).DataType = ftString) and
2244     not VarIsNull(lookupValues[i]) and (loCaseInsensitive in Options) then
2245     lookupValues[i] := UpperCase(lookupValues[i]);
2246     end;
2247     end;
2248     while not result and not EOF do {search for a matching record}
2249     begin
2250     i := 0;
2251     result := true;
2252     while result and (i < fieldCount) do
2253     {see if all of the key fields matches}
2254     begin
2255     fieldValue := TField(keyFieldList[i]).Value;
2256     result := not (VarIsNull(fieldValue) xor VarIsNull(lookupValues[i]));
2257     if result and not VarIsNull(fieldValue) then
2258     begin
2259     try
2260     if TField(keyFieldList[i]).DataType = ftString then
2261     begin
2262     {strings need special handling because of the locate options that
2263     apply to them}
2264     fieldValueAsString := TField(keyFieldList[i]).AsString;
2265     lookupValueAsString := lookupValues[i];
2266     if (loCaseInsensitive in Options) then
2267     fieldValueAsString := UpperCase(fieldValueAsString);
2268    
2269     if (loPartialKey in Options) then
2270     result := result and (Pos(lookupValueAsString, fieldValueAsString) = 1)
2271     else
2272     result := result and (fieldValueAsString = lookupValueAsString);
2273     end
2274     else
2275     result := result and (lookupValues[i] =
2276     VarAsType(fieldValue, VarType(lookupValues[i])));
2277     except on EVariantError do
2278     result := False;
2279     end;
2280     end;
2281     Inc(i);
2282     end;
2283     if not result then
2284     Next;
2285     end;
2286     if not result then
2287     Bookmark := CurBookmark
2288     else
2289     CursorPosChanged;
2290     finally
2291     keyFieldList.Free;
2292     SetLength(lookupValues,0)
2293     end;
2294     end;
2295    
2296     procedure TIBCustomDataSet.InternalPostRecord(Qry: TIBSQL; Buff: Pointer);
2297     var
2298 tony 45 i, j, k, arr: Integer;
2299 tony 33 pbd: PBlobDataArray;
2300 tony 45 pda: PArrayDataArray;
2301 tony 33 begin
2302     pbd := PBlobDataArray(PChar(Buff) + FBlobCacheOffset);
2303 tony 45 pda := PArrayDataArray(PChar(Buff) + FArrayCacheOffset);
2304     j := 0; arr := 0;
2305 tony 33 for i := 0 to FieldCount - 1 do
2306     if Fields[i].IsBlob then
2307     begin
2308     k := FMappedFieldPosition[Fields[i].FieldNo -1];
2309     if pbd^[j] <> nil then
2310     begin
2311     pbd^[j].Finalize;
2312     PISC_QUAD(
2313 tony 45 PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ :=
2314 tony 33 pbd^[j].BlobID;
2315     PRecordData(Buff)^.rdFields[k].fdIsNull := pbd^[j].Size = 0;
2316 tony 45 end
2317     else
2318     begin
2319     PRecordData(Buff)^.rdFields[k].fdIsNull := true;
2320     with PISC_QUAD(PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ do
2321     begin
2322     gds_quad_high := 0;
2323     gds_quad_low := 0;
2324     end;
2325 tony 33 end;
2326     Inc(j);
2327 tony 45 end
2328     else
2329     if Fields[i] is TIBArrayField then
2330     begin
2331     if pda^[arr] <> nil then
2332     begin
2333     k := FMappedFieldPosition[Fields[i].FieldNo -1];
2334     PISC_QUAD(
2335     PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ := pda^[arr].ArrayIntf.GetArrayID;
2336     PRecordData(Buff)^.rdFields[k].fdIsNull := pda^[arr].ArrayIntf.IsEmpty;
2337     end;
2338     Inc(arr);
2339 tony 33 end;
2340     if Assigned(FUpdateObject) then
2341     begin
2342     if (Qry = FQDelete) then
2343     FUpdateObject.Apply(ukDelete,Buff)
2344     else if (Qry = FQInsert) then
2345     FUpdateObject.Apply(ukInsert,Buff)
2346     else
2347     FUpdateObject.Apply(ukModify,Buff);
2348     end
2349     else begin
2350     SetInternalSQLParams(Qry, Buff);
2351     Qry.ExecQuery;
2352     end;
2353     PRecordData(Buff)^.rdUpdateStatus := usUnmodified;
2354     PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
2355     SetModified(False);
2356     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2357     if (FForcedRefresh or FNeedsRefresh) and CanRefresh then
2358     InternalRefreshRow;
2359     end;
2360    
2361     procedure TIBCustomDataSet.InternalRefreshRow;
2362     var
2363     Buff: PChar;
2364     ofs: DWORD;
2365     Qry: TIBSQL;
2366     begin
2367     FBase.SetCursor;
2368     try
2369     Buff := GetActiveBuf;
2370     if CanRefresh then
2371     begin
2372     if Buff <> nil then
2373     begin
2374     if (Assigned(FUpdateObject) and (FUpdateObject.RefreshSQL.Text <> '')) then
2375     begin
2376     Qry := TIBSQL.Create(self);
2377     Qry.Database := Database;
2378     Qry.Transaction := Transaction;
2379     Qry.GoToFirstRecordOnExecute := False;
2380     Qry.SQL.Text := FUpdateObject.RefreshSQL.Text;
2381     end
2382     else
2383     Qry := FQRefresh;
2384     SetInternalSQLParams(Qry, Buff);
2385     Qry.ExecQuery;
2386     try
2387 tony 45 if (Qry.SQLStatementType = SQLExecProcedure) or Qry.Next then
2388 tony 33 begin
2389     ofs := PRecordData(Buff)^.rdSavedOffset;
2390     FetchCurrentRecordToBuffer(Qry,
2391     PRecordData(Buff)^.rdRecordNumber,
2392     Buff);
2393     if FCachedUpdates and (ofs <> $FFFFFFFF) then
2394     begin
2395     PRecordData(Buff)^.rdSavedOffset := ofs;
2396     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2397     SaveOldBuffer(Buff);
2398     end;
2399     end;
2400     finally
2401     Qry.Close;
2402     end;
2403     if Qry <> FQRefresh then
2404     Qry.Free;
2405     end
2406     end
2407     else
2408     IBError(ibxeCannotRefresh, [nil]);
2409     finally
2410     FBase.RestoreCursor;
2411     end;
2412     end;
2413    
2414     procedure TIBCustomDataSet.InternalRevertRecord(RecordNumber: Integer);
2415     var
2416     NewBuffer, OldBuffer: PRecordData;
2417    
2418     begin
2419     NewBuffer := nil;
2420     OldBuffer := nil;
2421     NewBuffer := PRecordData(AllocRecordBuffer);
2422     OldBuffer := PRecordData(AllocRecordBuffer);
2423     try
2424     ReadRecordCache(RecordNumber, PChar(NewBuffer), False);
2425     ReadRecordCache(RecordNumber, PChar(OldBuffer), True);
2426     case NewBuffer^.rdCachedUpdateStatus of
2427     cusInserted:
2428     begin
2429     NewBuffer^.rdCachedUpdateStatus := cusUninserted;
2430     Inc(FDeletedRecords);
2431     end;
2432     cusModified,
2433     cusDeleted:
2434     begin
2435     if (NewBuffer^.rdCachedUpdateStatus = cusDeleted) then
2436     Dec(FDeletedRecords);
2437     CopyRecordBuffer(OldBuffer, NewBuffer);
2438     end;
2439     end;
2440    
2441     if State in dsEditModes then
2442     Cancel;
2443    
2444     WriteRecordCache(RecordNumber, PChar(NewBuffer));
2445    
2446     if (NewBuffer^.rdCachedUpdateStatus = cusUninserted ) then
2447     ReSync([]);
2448     finally
2449     FreeRecordBuffer(PChar(NewBuffer));
2450     FreeRecordBuffer(PChar(OldBuffer));
2451     end;
2452     end;
2453    
2454     { A visible record is one that is not truly deleted,
2455     and it is also listed in the FUpdateRecordTypes set }
2456    
2457     function TIBCustomDataSet.IsVisible(Buffer: PChar): Boolean;
2458     begin
2459     result := True;
2460     if not (State = dsOldValue) then
2461     result :=
2462     (PRecordData(Buffer)^.rdCachedUpdateStatus in FUpdateRecordTypes) and
2463     (not ((PRecordData(Buffer)^.rdCachedUpdateStatus = cusUnmodified) and
2464     (PRecordData(Buffer)^.rdUpdateStatus = usDeleted)));
2465     end;
2466    
2467    
2468     function TIBCustomDataSet.LocateNext(const KeyFields: string;
2469     const KeyValues: Variant; Options: TLocateOptions): Boolean;
2470     begin
2471     DisableControls;
2472     try
2473     result := InternalLocate(KeyFields, KeyValues, Options);
2474     finally
2475     EnableControls;
2476     end;
2477     end;
2478    
2479     procedure TIBCustomDataSet.InternalPrepare;
2480     begin
2481     if FInternalPrepared then
2482     Exit;
2483     FBase.SetCursor;
2484     try
2485     ActivateConnection;
2486 tony 45 ActivateTransaction;
2487 tony 33 FBase.CheckDatabase;
2488     FBase.CheckTransaction;
2489     if HasParser and (FParser.SQLText <> FQSelect.SQL.Text) then
2490 tony 35 begin
2491     FQSelect.OnSQLChanged := nil; {Do not react to change}
2492     try
2493     FQSelect.SQL.Text := FParser.SQLText;
2494     finally
2495     FQSelect.OnSQLChanged := SQLChanged;
2496     end;
2497     end;
2498 tony 33 // writeln( FQSelect.SQL.Text);
2499     if FQSelect.SQL.Text <> '' then
2500     begin
2501     if not FQSelect.Prepared then
2502     begin
2503     FQSelect.GenerateParamNames := FGenerateParamNames;
2504     FQSelect.ParamCheck := ParamCheck;
2505     FQSelect.Prepare;
2506     end;
2507     FQDelete.GenerateParamNames := FGenerateParamNames;
2508     if (Trim(FQDelete.SQL.Text) <> '') and (not FQDelete.Prepared) then
2509     FQDelete.Prepare;
2510     FQInsert.GenerateParamNames := FGenerateParamNames;
2511     if (Trim(FQInsert.SQL.Text) <> '') and (not FQInsert.Prepared) then
2512     FQInsert.Prepare;
2513     FQRefresh.GenerateParamNames := FGenerateParamNames;
2514     if (Trim(FQRefresh.SQL.Text) <> '') and (not FQRefresh.Prepared) then
2515     FQRefresh.Prepare;
2516     FQModify.GenerateParamNames := FGenerateParamNames;
2517     if (Trim(FQModify.SQL.Text) <> '') and (not FQModify.Prepared) then
2518     FQModify.Prepare;
2519     FInternalPrepared := True;
2520     InternalInitFieldDefs;
2521     end else
2522     IBError(ibxeEmptyQuery, [nil]);
2523     finally
2524     FBase.RestoreCursor;
2525     end;
2526     end;
2527    
2528     procedure TIBCustomDataSet.RecordModified(Value: Boolean);
2529     begin
2530     SetModified(Value);
2531     end;
2532    
2533     procedure TIBCustomDataSet.RevertRecord;
2534     var
2535     Buff: PRecordData;
2536     begin
2537     if FCachedUpdates and FUpdatesPending then
2538     begin
2539     Buff := PRecordData(GetActiveBuf);
2540     InternalRevertRecord(Buff^.rdRecordNumber);
2541     ReadRecordCache(Buff^.rdRecordNumber, PChar(Buff), False);
2542     DataEvent(deRecordChange, 0);
2543     end;
2544     end;
2545    
2546     procedure TIBCustomDataSet.SaveOldBuffer(Buffer: PChar);
2547     var
2548     OldBuffer: Pointer;
2549     procedure CopyOldBuffer;
2550     begin
2551     CopyRecordBuffer(Buffer, OldBuffer);
2552     if BlobFieldCount > 0 then
2553 tony 45 FillChar(PChar(OldBuffer)[FBlobCacheOffset],
2554     BlobFieldCount * SizeOf(TIBBlobStream) + ArrayFieldCount * SizeOf(IArray),
2555 tony 33 0);
2556     end;
2557    
2558     begin
2559     if (Buffer <> nil) and (PRecordData(Buffer)^.rdRecordNumber >= 0) then
2560     begin
2561     OldBuffer := AllocRecordBuffer;
2562     try
2563     if (PRecordData(Buffer)^.rdSavedOffset = $FFFFFFFF) then
2564     begin
2565     PRecordData(Buffer)^.rdSavedOffset := AdjustPosition(FOldBufferCache, 0,
2566     FILE_END);
2567     CopyOldBuffer;
2568     WriteCache(FOldBufferCache, 0, FILE_CURRENT, OldBuffer);
2569     WriteCache(FBufferCache, PRecordData(Buffer)^.rdRecordNumber * FRecordBufferSize,
2570     FILE_BEGIN, Buffer);
2571     end
2572     else begin
2573     CopyOldBuffer;
2574     WriteCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
2575     OldBuffer);
2576     end;
2577     finally
2578     FreeRecordBuffer(PChar(OldBuffer));
2579     end;
2580     end;
2581     end;
2582    
2583     procedure TIBCustomDataSet.SetBufferChunks(Value: Integer);
2584     begin
2585     if (Value <= 0) then
2586     FBufferChunks := BufferCacheSize
2587     else
2588     FBufferChunks := Value;
2589     end;
2590    
2591     procedure TIBCustomDataSet.SetDatabase(Value: TIBDatabase);
2592     begin
2593     if (FBase.Database <> Value) then
2594     begin
2595     CheckDatasetClosed;
2596     FBase.Database := Value;
2597     FQDelete.Database := Value;
2598     FQInsert.Database := Value;
2599     FQRefresh.Database := Value;
2600     FQSelect.Database := Value;
2601     FQModify.Database := Value;
2602     end;
2603     end;
2604    
2605     procedure TIBCustomDataSet.SetDeleteSQL(Value: TStrings);
2606     begin
2607     if FQDelete.SQL.Text <> Value.Text then
2608     begin
2609     Disconnect;
2610     FQDelete.SQL.Assign(Value);
2611     end;
2612     end;
2613    
2614     procedure TIBCustomDataSet.SetInsertSQL(Value: TStrings);
2615     begin
2616     if FQInsert.SQL.Text <> Value.Text then
2617     begin
2618     Disconnect;
2619     FQInsert.SQL.Assign(Value);
2620     end;
2621     end;
2622    
2623     procedure TIBCustomDataSet.SetInternalSQLParams(Qry: TIBSQL; Buffer: Pointer);
2624     var
2625     i, j: Integer;
2626     cr, data: PChar;
2627 tony 43 fn: string;
2628     st: RawByteString;
2629 tony 33 OldBuffer: Pointer;
2630     ts: TTimeStamp;
2631 tony 45 Param: ISQLParam;
2632 tony 33 begin
2633     if (Buffer = nil) then
2634     IBError(ibxeBufferNotSet, [nil]);
2635     if (not FInternalPrepared) then
2636     InternalPrepare;
2637     OldBuffer := nil;
2638     try
2639 tony 45 for i := 0 to Qry.Params.GetCount - 1 do
2640 tony 33 begin
2641 tony 45 Param := Qry.Params[i];
2642     fn := Param.Name;
2643 tony 33 if (Pos('OLD_', fn) = 1) then {mbcs ok}
2644     begin
2645     fn := Copy(fn, 5, Length(fn));
2646     if not Assigned(OldBuffer) then
2647     begin
2648     OldBuffer := AllocRecordBuffer;
2649     ReadRecordCache(PRecordData(Buffer)^.rdRecordNumber, OldBuffer, True);
2650     end;
2651     cr := OldBuffer;
2652     end
2653     else if (Pos('NEW_', fn) = 1) then {mbcs ok}
2654     begin
2655     fn := Copy(fn, 5, Length(fn));
2656     cr := Buffer;
2657     end
2658     else
2659     cr := Buffer;
2660     j := FQSelect.FieldIndex[fn] + 1;
2661     if (j > 0) then
2662 tony 45 with PRecordData(cr)^,rdFields[j], FFieldColumns^[j] do
2663 tony 33 begin
2664 tony 45 if Param.name = 'IBX_INTERNAL_DBKEY' then {do not localize}
2665 tony 33 begin
2666 tony 45 PIBDBKey(Param.AsPointer)^ := rdDBKey;
2667 tony 33 continue;
2668     end;
2669 tony 45 if fdIsNull then
2670     Param.IsNull := True
2671 tony 33 else begin
2672 tony 45 Param.IsNull := False;
2673     data := cr + fdDataOfs;
2674     case fdDataType of
2675 tony 33 SQL_TEXT, SQL_VARYING:
2676     begin
2677 tony 45 SetString(st, data, fdDataLength);
2678     SetCodePage(st,fdCodePage,false);
2679     Param.AsString := st;
2680 tony 33 end;
2681     SQL_FLOAT, SQL_DOUBLE, SQL_D_FLOAT:
2682 tony 45 Param.AsDouble := PDouble(data)^;
2683 tony 33 SQL_SHORT, SQL_LONG:
2684     begin
2685 tony 45 if fdDataScale = 0 then
2686     Param.AsLong := PLong(data)^
2687 tony 33 else
2688 tony 45 if fdDataScale >= (-4) then
2689     Param.AsCurrency := PCurrency(data)^
2690     else
2691     Param.AsDouble := PDouble(data)^;
2692 tony 33 end;
2693     SQL_INT64:
2694     begin
2695 tony 45 if fdDataScale = 0 then
2696     Param.AsInt64 := PInt64(data)^
2697 tony 33 else
2698 tony 45 if fdDataScale >= (-4) then
2699     Param.AsCurrency := PCurrency(data)^
2700     else
2701     Param.AsDouble := PDouble(data)^;
2702 tony 33 end;
2703     SQL_BLOB, SQL_ARRAY, SQL_QUAD:
2704 tony 45 Param.AsQuad := PISC_QUAD(data)^;
2705 tony 33 SQL_TYPE_DATE:
2706     begin
2707     ts.Date := PInt(data)^;
2708     ts.Time := 0;
2709 tony 45 Param.AsDate := TimeStampToDateTime(ts);
2710 tony 33 end;
2711     SQL_TYPE_TIME:
2712     begin
2713     ts.Date := 0;
2714     ts.Time := PInt(data)^;
2715 tony 45 Param.AsTime := TimeStampToDateTime(ts);
2716 tony 33 end;
2717     SQL_TIMESTAMP:
2718 tony 45 Param.AsDateTime :=
2719 tony 33 TimeStampToDateTime(MSecsToTimeStamp(trunc(PDouble(data)^)));
2720     SQL_BOOLEAN:
2721 tony 45 Param.AsBoolean := PWordBool(data)^;
2722 tony 33 end;
2723     end;
2724     end;
2725     end;
2726     finally
2727     if (OldBuffer <> nil) then
2728     FreeRecordBuffer(PChar(OldBuffer));
2729     end;
2730     end;
2731    
2732     procedure TIBCustomDataSet.SetRefreshSQL(Value: TStrings);
2733     begin
2734     if FQRefresh.SQL.Text <> Value.Text then
2735     begin
2736     Disconnect;
2737     FQRefresh.SQL.Assign(Value);
2738     end;
2739     end;
2740    
2741     procedure TIBCustomDataSet.SetSelectSQL(Value: TStrings);
2742     begin
2743     if FQSelect.SQL.Text <> Value.Text then
2744     begin
2745     Disconnect;
2746     FQSelect.SQL.Assign(Value);
2747     end;
2748     end;
2749    
2750     procedure TIBCustomDataSet.SetModifySQL(Value: TStrings);
2751     begin
2752     if FQModify.SQL.Text <> Value.Text then
2753     begin
2754     Disconnect;
2755     FQModify.SQL.Assign(Value);
2756     end;
2757     end;
2758    
2759     procedure TIBCustomDataSet.SetTransaction(Value: TIBTransaction);
2760     begin
2761     if (FBase.Transaction <> Value) then
2762     begin
2763     CheckDatasetClosed;
2764     FBase.Transaction := Value;
2765     FQDelete.Transaction := Value;
2766     FQInsert.Transaction := Value;
2767     FQRefresh.Transaction := Value;
2768     FQSelect.Transaction := Value;
2769     FQModify.Transaction := Value;
2770     end;
2771     end;
2772    
2773     procedure TIBCustomDataSet.SetUniDirectional(Value: Boolean);
2774     begin
2775     CheckDatasetClosed;
2776     FUniDirectional := Value;
2777     end;
2778    
2779     procedure TIBCustomDataSet.SetUpdateRecordTypes(Value: TIBUpdateRecordTypes);
2780     begin
2781     FUpdateRecordTypes := Value;
2782     if Active then
2783     First;
2784     end;
2785    
2786     procedure TIBCustomDataSet.RefreshParams;
2787     var
2788     DataSet: TDataSet;
2789     begin
2790     DisableControls;
2791     try
2792     if FDataLink.DataSource <> nil then
2793     begin
2794     DataSet := FDataLink.DataSource.DataSet;
2795     if DataSet <> nil then
2796     if DataSet.Active and (DataSet.State <> dsSetKey) then
2797     begin
2798     Close;
2799     Open;
2800     end;
2801     end;
2802     finally
2803     EnableControls;
2804     end;
2805     end;
2806    
2807     procedure TIBCustomDataSet.RegisterIBLink(Sender: TIBControlLink);
2808     begin
2809     if FIBLinks.IndexOf(Sender) = -1 then
2810     FIBLinks.Add(Sender);
2811     end;
2812    
2813    
2814     procedure TIBCustomDataSet.SQLChanging(Sender: TObject);
2815     begin
2816     Active := false;
2817     { if FOpen then
2818     InternalClose;}
2819     if FInternalPrepared then
2820     InternalUnPrepare;
2821     FieldDefs.Clear;
2822     FieldDefs.Updated := false;
2823     end;
2824    
2825 tony 35 procedure TIBCustomDataSet.SQLChanged(Sender: TObject);
2826     begin
2827     FBaseSQLSelect.assign(FQSelect.SQL);
2828     end;
2829    
2830 tony 33 { I can "undelete" uninserted records (make them "inserted" again).
2831     I can "undelete" cached deleted (the deletion hasn't yet occurred) }
2832     procedure TIBCustomDataSet.Undelete;
2833     var
2834     Buff: PRecordData;
2835     begin
2836     CheckActive;
2837     Buff := PRecordData(GetActiveBuf);
2838     with Buff^ do
2839     begin
2840     if rdCachedUpdateStatus = cusUninserted then
2841     begin
2842     rdCachedUpdateStatus := cusInserted;
2843     Dec(FDeletedRecords);
2844     end
2845     else if (rdUpdateStatus = usDeleted) and
2846     (rdCachedUpdateStatus = cusDeleted) then
2847     begin
2848     rdCachedUpdateStatus := cusUnmodified;
2849     rdUpdateStatus := usUnmodified;
2850     Dec(FDeletedRecords);
2851     end;
2852     WriteRecordCache(rdRecordNumber, PChar(Buff));
2853     end;
2854     end;
2855    
2856     procedure TIBCustomDataSet.UnRegisterIBLink(Sender: TIBControlLink);
2857     begin
2858     FIBLinks.Remove(Sender);
2859     end;
2860    
2861     function TIBCustomDataSet.UpdateStatus: TUpdateStatus;
2862     begin
2863     if Active then
2864     if GetActiveBuf <> nil then
2865     result := PRecordData(GetActiveBuf)^.rdUpdateStatus
2866     else
2867     result := usUnmodified
2868     else
2869     result := usUnmodified;
2870     end;
2871    
2872     function TIBCustomDataSet.IsSequenced: Boolean;
2873     begin
2874     Result := Assigned( FQSelect ) and FQSelect.EOF;
2875     end;
2876    
2877 tony 45 function TIBCustomDataSet.ParamByName(ParamName: String): ISQLParam;
2878 tony 33 begin
2879     ActivateConnection;
2880     ActivateTransaction;
2881     if not FInternalPrepared then
2882     InternalPrepare;
2883     Result := Params.ByName(ParamName);
2884     end;
2885    
2886     {Beware: the parameter FCache is used as an identifier to determine which
2887     cache is being operated on and is not referenced in the computation.
2888     The result is an adjusted offset into the identified cache, either the
2889     Buffer Cache or the old Buffer Cache.}
2890    
2891     function TIBCustomDataSet.AdjustPosition(FCache: PChar; Offset: DWORD;
2892     Origin: Integer): DWORD;
2893     var
2894     OldCacheSize: Integer;
2895     begin
2896     if (FCache = FBufferCache) then
2897     begin
2898     case Origin of
2899     FILE_BEGIN: FBPos := Offset;
2900     FILE_CURRENT: FBPos := FBPos + Offset;
2901     FILE_END: FBPos := DWORD(FBEnd) + Offset;
2902     end;
2903     OldCacheSize := FCacheSize;
2904     while (FBPos >= DWORD(FCacheSize)) do
2905     Inc(FCacheSize, FBufferChunkSize);
2906     if FCacheSize > OldCacheSize then
2907     IBAlloc(FBufferCache, FCacheSize, FCacheSize);
2908     result := FBPos;
2909     end
2910     else begin
2911     case Origin of
2912     FILE_BEGIN: FOBPos := Offset;
2913     FILE_CURRENT: FOBPos := FOBPos + Offset;
2914     FILE_END: FOBPos := DWORD(FOBEnd) + Offset;
2915     end;
2916     OldCacheSize := FOldCacheSize;
2917     while (FBPos >= DWORD(FOldCacheSize)) do
2918     Inc(FOldCacheSize, FBufferChunkSize);
2919     if FOldCacheSize > OldCacheSize then
2920     IBAlloc(FOldBufferCache, FOldCacheSize, FOldCacheSize);
2921     result := FOBPos;
2922     end;
2923     end;
2924    
2925     procedure TIBCustomDataSet.ReadCache(FCache: PChar; Offset: DWORD; Origin: Integer;
2926     Buffer: PChar);
2927     var
2928     pCache: PChar;
2929     AdjustedOffset: DWORD;
2930     bOld: Boolean;
2931     begin
2932     bOld := (FCache = FOldBufferCache);
2933     AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
2934     if not bOld then
2935     pCache := FBufferCache + AdjustedOffset
2936     else
2937     pCache := FOldBufferCache + AdjustedOffset;
2938     Move(pCache^, Buffer^, DWORD(FRecordBufferSize));
2939     AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
2940     end;
2941    
2942     procedure TIBCustomDataSet.ReadRecordCache(RecordNumber: Integer; Buffer: PChar;
2943     ReadOldBuffer: Boolean);
2944     begin
2945     if FUniDirectional then
2946     RecordNumber := RecordNumber mod UniCache;
2947     if (ReadOldBuffer) then
2948     begin
2949     ReadRecordCache(RecordNumber, Buffer, False);
2950     if FCachedUpdates and
2951     (PRecordData(Buffer)^.rdSavedOffset <> $FFFFFFFF) then
2952     ReadCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
2953     Buffer)
2954     else
2955     if ReadOldBuffer and
2956     (PRecordData(FOldBuffer)^.rdRecordNumber = RecordNumber) then
2957     CopyRecordBuffer( FOldBuffer, Buffer )
2958     end
2959     else
2960     ReadCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
2961     end;
2962    
2963     procedure TIBCustomDataSet.WriteCache(FCache: PChar; Offset: DWORD; Origin: Integer;
2964     Buffer: PChar);
2965     var
2966     pCache: PChar;
2967     AdjustedOffset: DWORD;
2968     bOld: Boolean;
2969     dwEnd: DWORD;
2970     begin
2971     bOld := (FCache = FOldBufferCache);
2972     AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
2973     if not bOld then
2974     pCache := FBufferCache + AdjustedOffset
2975     else
2976     pCache := FOldBufferCache + AdjustedOffset;
2977     Move(Buffer^, pCache^, FRecordBufferSize);
2978     dwEnd := AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
2979     if not bOld then
2980     begin
2981     if (dwEnd > FBEnd) then
2982     FBEnd := dwEnd;
2983     end
2984     else begin
2985     if (dwEnd > FOBEnd) then
2986     FOBEnd := dwEnd;
2987     end;
2988     end;
2989    
2990     procedure TIBCustomDataSet.WriteRecordCache(RecordNumber: Integer; Buffer: PChar);
2991     begin
2992     if RecordNumber >= 0 then
2993     begin
2994     if FUniDirectional then
2995     RecordNumber := RecordNumber mod UniCache;
2996     WriteCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
2997     end;
2998     end;
2999    
3000     function TIBCustomDataSet.AllocRecordBuffer: PChar;
3001     begin
3002     result := nil;
3003     IBAlloc(result, FRecordBufferSize, FRecordBufferSize);
3004     Move(FModelBuffer^, result^, FRecordBufferSize);
3005     end;
3006    
3007     function TIBCustomDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
3008     var
3009     pb: PBlobDataArray;
3010     fs: TIBBlobStream;
3011     Buff: PChar;
3012     bTr, bDB: Boolean;
3013     begin
3014 tony 45 if (Field = nil) or (Field.DataSet <> self) then
3015     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3016 tony 33 Buff := GetActiveBuf;
3017     if Buff = nil then
3018     begin
3019     fs := TIBBlobStream.Create;
3020     fs.Mode := bmReadWrite;
3021 tony 45 fs.Database := Database;
3022     fs.Transaction := Transaction;
3023     fs.SetField(Field);
3024 tony 33 FBlobStreamList.Add(Pointer(fs));
3025     result := TIBDSBlobStream.Create(Field, fs, Mode);
3026     exit;
3027     end;
3028     pb := PBlobDataArray(Buff + FBlobCacheOffset);
3029     if pb^[Field.Offset] = nil then
3030     begin
3031     AdjustRecordOnInsert(Buff);
3032     pb^[Field.Offset] := TIBBlobStream.Create;
3033     fs := pb^[Field.Offset];
3034     FBlobStreamList.Add(Pointer(fs));
3035     fs.Mode := bmReadWrite;
3036     fs.Database := Database;
3037     fs.Transaction := Transaction;
3038 tony 45 fs.SetField(Field);
3039 tony 33 fs.BlobID :=
3040 tony 45 PISC_QUAD(@Buff[FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]].fdDataOfs])^;
3041 tony 33 if (CachedUpdates) then
3042     begin
3043     bTr := not Transaction.InTransaction;
3044     bDB := not Database.Connected;
3045     if bDB then
3046     Database.Open;
3047     if bTr then
3048     Transaction.StartTransaction;
3049     fs.Seek(0, soFromBeginning);
3050     if bTr then
3051     Transaction.Commit;
3052     if bDB then
3053     Database.Close;
3054     end;
3055     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3056     end else
3057     fs := pb^[Field.Offset];
3058     result := TIBDSBlobStream.Create(Field, fs, Mode);
3059     end;
3060    
3061 tony 45 function TIBCustomDataSet.GetArray(Field: TIBArrayField): IArray;
3062     var Buff: PChar;
3063     pda: PArrayDataArray;
3064     bTr, bDB: Boolean;
3065     begin
3066     if (Field = nil) or (Field.DataSet <> self) then
3067     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3068     Buff := GetActiveBuf;
3069     if Buff = nil then
3070     Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3071     Field.FRelationName,Field.FieldName)
3072     else
3073     begin
3074     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3075     if pda^[Field.FCacheOffset] = nil then
3076     begin
3077     AdjustRecordOnInsert(Buff);
3078     if Field.IsNull then
3079     Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3080     Field.FRelationName,Field.FieldName)
3081     else
3082     Result := Database.Attachment.OpenArray(Transaction.TransactionIntf,
3083     Field.FRelationName,Field.FieldName,Field.ArrayID);
3084     pda^[Field.FCacheOffset] := TIBArray.Create(Field,Result);
3085     FArrayList.Add(pda^[Field.FCacheOffset]);
3086     if (CachedUpdates) then
3087     begin
3088     bTr := not Transaction.InTransaction;
3089     bDB := not Database.Connected;
3090     if bDB then
3091     Database.Open;
3092     if bTr then
3093     Transaction.StartTransaction;
3094     pda^[Field.FCacheOffset].ArrayIntf.PreLoad;
3095     if bTr then
3096     Transaction.Commit;
3097     if bDB then
3098     Database.Close;
3099     end;
3100     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3101     end
3102     else
3103     Result := pda^[Field.FCacheOffset].ArrayIntf;
3104     end;
3105     end;
3106    
3107     procedure TIBCustomDataSet.SetArrayIntf(AnArray: IArray; Field: TIBArrayField);
3108     var Buff: PChar;
3109     pda: PArrayDataArray;
3110     begin
3111     if (Field = nil) or (Field.DataSet <> self) then
3112     IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3113     Buff := GetActiveBuf;
3114     if Buff <> nil then
3115     begin
3116     AdjustRecordOnInsert(Buff);
3117     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3118     pda^[Field.FCacheOffset].FArray := AnArray;
3119     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3120     end;
3121     end;
3122    
3123 tony 33 function TIBCustomDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
3124     const
3125     CMPLess = -1;
3126     CMPEql = 0;
3127     CMPGtr = 1;
3128     RetCodes: array[Boolean, Boolean] of ShortInt = ((2, CMPLess),
3129     (CMPGtr, CMPEql));
3130     begin
3131     result := RetCodes[Bookmark1 = nil, Bookmark2 = nil];
3132    
3133     if Result = 2 then
3134     begin
3135     if PInteger(Bookmark1)^ < PInteger(Bookmark2)^ then
3136     Result := CMPLess
3137     else
3138     if PInteger(Bookmark1)^ > PInteger(Bookmark2)^ then
3139     Result := CMPGtr
3140     else
3141     Result := CMPEql;
3142     end;
3143     end;
3144    
3145     procedure TIBCustomDataSet.DoBeforeDelete;
3146     var
3147     Buff: PRecordData;
3148     begin
3149     if not CanDelete then
3150     IBError(ibxeCannotDelete, [nil]);
3151     Buff := PRecordData(GetActiveBuf);
3152     if FCachedUpdates and
3153     (Buff^.rdCachedUpdateStatus in [cusUnmodified]) then
3154     SaveOldBuffer(PChar(Buff));
3155     inherited DoBeforeDelete;
3156     end;
3157    
3158     procedure TIBCustomDataSet.DoAfterDelete;
3159     begin
3160     inherited DoAfterDelete;
3161     FBase.DoAfterDelete(self);
3162     InternalAutoCommit;
3163     end;
3164    
3165     procedure TIBCustomDataSet.DoBeforeEdit;
3166     var
3167     Buff: PRecordData;
3168     begin
3169     Buff := PRecordData(GetActiveBuf);
3170     if not(CanEdit or (FQModify.SQL.Count <> 0) or
3171     (FCachedUpdates and Assigned(FOnUpdateRecord))) then
3172     IBError(ibxeCannotUpdate, [nil]);
3173     if FCachedUpdates and (Buff^.rdCachedUpdateStatus in [cusUnmodified, cusInserted]) then
3174     SaveOldBuffer(PChar(Buff));
3175     CopyRecordBuffer(GetActiveBuf, FOldBuffer);
3176     inherited DoBeforeEdit;
3177     end;
3178    
3179     procedure TIBCustomDataSet.DoAfterEdit;
3180     begin
3181     inherited DoAfterEdit;
3182     FBase.DoAfterEdit(self);
3183     end;
3184    
3185     procedure TIBCustomDataSet.DoBeforeInsert;
3186     begin
3187     if not CanInsert then
3188     IBError(ibxeCannotInsert, [nil]);
3189     inherited DoBeforeInsert;
3190     end;
3191    
3192     procedure TIBCustomDataSet.DoAfterInsert;
3193     begin
3194     if GeneratorField.ApplyOnEvent = gaeOnNewRecord then
3195     GeneratorField.Apply;
3196     inherited DoAfterInsert;
3197     FBase.DoAfterInsert(self);
3198     end;
3199    
3200     procedure TIBCustomDataSet.DoBeforeClose;
3201     begin
3202     inherited DoBeforeClose;
3203 tony 45 if FInTransactionEnd and (FCloseAction = TARollback) then
3204     Exit;
3205 tony 33 if State in [dsInsert,dsEdit] then
3206     begin
3207     if DataSetCloseAction = dcSaveChanges then
3208     Post;
3209     {Note this can fail with an exception e.g. due to
3210     database validation error. In which case the dataset remains open }
3211     end;
3212 tony 45 if FCachedUpdates and FUpdatesPending and (DataSetCloseAction = dcSaveChanges) then
3213     ApplyUpdates;
3214 tony 33 end;
3215    
3216     procedure TIBCustomDataSet.DoBeforeOpen;
3217     var i: integer;
3218     begin
3219     if assigned(FParser) then
3220     FParser.Reset;
3221     for i := 0 to FIBLinks.Count - 1 do
3222     TIBControlLink(FIBLinks[i]).UpdateSQL(self);
3223     inherited DoBeforeOpen;
3224     for i := 0 to FIBLinks.Count - 1 do
3225     TIBControlLink(FIBLinks[i]).UpdateParams(self);
3226     end;
3227    
3228     procedure TIBCustomDataSet.DoBeforePost;
3229     begin
3230     inherited DoBeforePost;
3231     if (State = dsInsert) and
3232     (GeneratorField.ApplyOnEvent = gaeOnPostRecord) then
3233     GeneratorField.Apply
3234     end;
3235    
3236     procedure TIBCustomDataSet.DoAfterPost;
3237     begin
3238     inherited DoAfterPost;
3239     FBase.DoAfterPost(self);
3240     InternalAutoCommit;
3241     end;
3242    
3243     procedure TIBCustomDataSet.FetchAll;
3244     var
3245     CurBookmark: TBookmark;
3246     begin
3247     FBase.SetCursor;
3248     try
3249     if FQSelect.EOF or not FQSelect.Open then
3250     exit;
3251     DisableControls;
3252     try
3253     CurBookmark := Bookmark;
3254     Last;
3255     Bookmark := CurBookmark;
3256     finally
3257     EnableControls;
3258     end;
3259     finally
3260     FBase.RestoreCursor;
3261     end;
3262     end;
3263    
3264     procedure TIBCustomDataSet.FreeRecordBuffer(var Buffer: PChar);
3265     begin
3266     FreeMem(Buffer);
3267     Buffer := nil;
3268     end;
3269    
3270     procedure TIBCustomDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
3271     begin
3272     Move(PRecordData(Buffer)^.rdRecordNumber, Data^, BookmarkSize);
3273     end;
3274    
3275     function TIBCustomDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
3276     begin
3277     result := PRecordData(Buffer)^.rdBookmarkFlag;
3278     end;
3279    
3280     function TIBCustomDataSet.GetCanModify: Boolean;
3281     begin
3282     result := (FQInsert.SQL.Text <> '') or
3283     (FQModify.SQL.Text <> '') or
3284     (FQDelete.SQL.Text <> '') or
3285     (Assigned(FUpdateObject));
3286     end;
3287    
3288     function TIBCustomDataSet.GetCurrentRecord(Buffer: PChar): Boolean;
3289     begin
3290     if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then
3291     begin
3292     UpdateCursorPos;
3293     ReadRecordCache(PRecordData(ActiveBuffer)^.rdRecordNumber, Buffer, False);
3294     result := True;
3295     end
3296     else
3297     result := False;
3298     end;
3299    
3300     function TIBCustomDataSet.GetDataSource: TDataSource;
3301     begin
3302     if FDataLink = nil then
3303     result := nil
3304     else
3305     result := FDataLink.DataSource;
3306     end;
3307    
3308     function TIBCustomDataSet.GetDBAliasName(FieldNo: integer): string;
3309     begin
3310     Result := FAliasNameMap[FieldNo-1]
3311     end;
3312    
3313     function TIBCustomDataSet.GetFieldDefFromAlias(aliasName: string): TFieldDef;
3314     var
3315     i: integer;
3316     begin
3317     Result := nil;
3318     for i := 0 to Length(FAliasNameMap) - 1 do
3319     if FAliasNameMap[i] = aliasName then
3320     begin
3321     Result := FieldDefs[i];
3322     Exit
3323     end;
3324     end;
3325    
3326     function TIBCustomDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
3327     begin
3328     Result := DefaultFieldClasses[FieldType];
3329     end;
3330    
3331     function TIBCustomDataSet.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean;
3332     begin
3333     result := GetFieldData(FieldByNumber(FieldNo), buffer);
3334     end;
3335    
3336     function TIBCustomDataSet.InternalGetFieldData(Field: TField; Buffer: Pointer): Boolean;
3337     var
3338     Buff, Data: PChar;
3339     CurrentRecord: PRecordData;
3340     begin
3341     result := False;
3342     Buff := GetActiveBuf;
3343     if (Buff = nil) or
3344     (not IsVisible(Buff)) or not assigned(Field.DataSet) then
3345     exit;
3346     { The intention here is to stuff the buffer with the data for the
3347     referenced field for the current record }
3348     CurrentRecord := PRecordData(Buff);
3349     if (Field.FieldNo < 0) then
3350     begin
3351     Inc(Buff, FRecordSize + Field.Offset);
3352     result := Boolean(Buff[0]);
3353     if result and (Buffer <> nil) then
3354     Move(Buff[1], Buffer^, Field.DataSize);
3355     end
3356 tony 45 else
3357     if (FMappedFieldPosition[Field.FieldNo - 1] > 0) and
3358 tony 33 (FMappedFieldPosition[Field.FieldNo - 1] <= CurrentRecord^.rdFieldCount) then
3359 tony 45 with CurrentRecord^.rdFields[FMappedFieldPosition[Field.FieldNo - 1]],
3360     FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]] do
3361 tony 33 begin
3362 tony 45 result := not fdIsNull;
3363 tony 33 if result and (Buffer <> nil) then
3364     begin
3365 tony 45 Data := Buff + fdDataOfs;
3366 tony 33 if (fdDataType = SQL_VARYING) or (fdDataType = SQL_TEXT) then
3367     begin
3368     if fdDataLength < Field.DataSize then
3369     begin
3370     Move(Data^, Buffer^, fdDataLength);
3371     PChar(Buffer)[fdDataLength] := #0;
3372     end
3373     else
3374     IBError(ibxeFieldSizeError,[Field.FieldName])
3375     end
3376     else
3377     Move(Data^, Buffer^, Field.DataSize);
3378     end;
3379     end;
3380     end;
3381    
3382     { GetRecNo and SetRecNo both operate off of 1-based indexes as
3383     opposed to 0-based indexes.
3384     This is because we want LastRecordNumber/RecordCount = 1 }
3385    
3386     function TIBCustomDataSet.GetRecNo: Integer;
3387     begin
3388     if GetActiveBuf = nil then
3389     result := 0
3390     else
3391     result := PRecordData(GetActiveBuf)^.rdRecordNumber + 1;
3392     end;
3393    
3394     function TIBCustomDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
3395     DoCheck: Boolean): TGetResult;
3396     var
3397     Accept: Boolean;
3398     SaveState: TDataSetState;
3399     begin
3400     Result := grOK;
3401     if Filtered and Assigned(OnFilterRecord) then
3402     begin
3403     Accept := False;
3404     SaveState := SetTempState(dsFilter);
3405     while not Accept do
3406     begin
3407     Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3408     if Result <> grOK then
3409     break;
3410     FFilterBuffer := Buffer;
3411     try
3412     Accept := True;
3413     OnFilterRecord(Self, Accept);
3414     if not Accept and (GetMode = gmCurrent) then
3415     GetMode := gmPrior;
3416     except
3417     // FBase.HandleException(Self);
3418     end;
3419     end;
3420     RestoreState(SaveState);
3421     end
3422     else
3423     Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3424     end;
3425    
3426     function TIBCustomDataSet.InternalGetRecord(Buffer: PChar; GetMode: TGetMode;
3427     DoCheck: Boolean): TGetResult;
3428     begin
3429     result := grError;
3430     case GetMode of
3431     gmCurrent: begin
3432     if (FCurrentRecord >= 0) then begin
3433     if FCurrentRecord < FRecordCount then
3434     ReadRecordCache(FCurrentRecord, Buffer, False)
3435     else begin
3436 tony 45 while (not FQSelect.EOF) and FQSelect.Next and
3437 tony 33 (FCurrentRecord >= FRecordCount) do begin
3438     FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
3439     Inc(FRecordCount);
3440     end;
3441     FCurrentRecord := FRecordCount - 1;
3442     if (FCurrentRecord >= 0) then
3443     ReadRecordCache(FCurrentRecord, Buffer, False);
3444     end;
3445     result := grOk;
3446     end else
3447     result := grBOF;
3448     end;
3449     gmNext: begin
3450     result := grOk;
3451     if FCurrentRecord = FRecordCount then
3452     result := grEOF
3453     else if FCurrentRecord = FRecordCount - 1 then begin
3454     if (not FQSelect.EOF) then begin
3455     FQSelect.Next;
3456     Inc(FCurrentRecord);
3457     end;
3458     if (FQSelect.EOF) then begin
3459     result := grEOF;
3460     end else begin
3461     Inc(FRecordCount);
3462     FetchCurrentRecordToBuffer(FQSelect, FCurrentRecord, Buffer);
3463     end;
3464     end else if (FCurrentRecord < FRecordCount) then begin
3465     Inc(FCurrentRecord);
3466     ReadRecordCache(FCurrentRecord, Buffer, False);
3467     end;
3468     end;
3469     else { gmPrior }
3470     begin
3471     if (FCurrentRecord = 0) then begin
3472     Dec(FCurrentRecord);
3473     result := grBOF;
3474     end else if (FCurrentRecord > 0) and
3475     (FCurrentRecord <= FRecordCount) then begin
3476     Dec(FCurrentRecord);
3477     ReadRecordCache(FCurrentRecord, Buffer, False);
3478     result := grOk;
3479     end else if (FCurrentRecord = -1) then
3480     result := grBOF;
3481     end;
3482     end;
3483     if result = grOk then
3484     result := AdjustCurrentRecord(Buffer, GetMode);
3485     if result = grOk then with PRecordData(Buffer)^ do begin
3486     rdBookmarkFlag := bfCurrent;
3487     GetCalcFields(Buffer);
3488     end else if (result = grEOF) then begin
3489     CopyRecordBuffer(FModelBuffer, Buffer);
3490     PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3491     end else if (result = grBOF) then begin
3492     CopyRecordBuffer(FModelBuffer, Buffer);
3493     PRecordData(Buffer)^.rdBookmarkFlag := bfBOF;
3494     end else if (result = grError) then begin
3495     CopyRecordBuffer(FModelBuffer, Buffer);
3496     PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3497     end;;
3498     end;
3499    
3500     function TIBCustomDataSet.GetRecordCount: Integer;
3501     begin
3502     result := FRecordCount - FDeletedRecords;
3503     end;
3504    
3505     function TIBCustomDataSet.GetRecordSize: Word;
3506     begin
3507     result := FRecordBufferSize;
3508     end;
3509    
3510     procedure TIBCustomDataSet.InternalAutoCommit;
3511     begin
3512     with Transaction do
3513     if InTransaction and (FAutoCommit = acCommitRetaining) then
3514     begin
3515     if CachedUpdates then ApplyUpdates;
3516     CommitRetaining;
3517     end;
3518     end;
3519    
3520     procedure TIBCustomDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
3521     begin
3522     CheckEditState;
3523     begin
3524     { When adding records, we *always* append.
3525     Insertion is just too costly }
3526     AdjustRecordOnInsert(Buffer);
3527     with PRecordData(Buffer)^ do
3528     begin
3529     rdUpdateStatus := usInserted;
3530     rdCachedUpdateStatus := cusInserted;
3531     end;
3532     if not CachedUpdates then
3533     InternalPostRecord(FQInsert, Buffer)
3534     else begin
3535     WriteRecordCache(FCurrentRecord, Buffer);
3536     FUpdatesPending := True;
3537     end;
3538     Inc(FRecordCount);
3539     InternalSetToRecord(Buffer);
3540     end
3541     end;
3542    
3543     procedure TIBCustomDataSet.InternalCancel;
3544     var
3545     Buff: PChar;
3546     CurRec: Integer;
3547 tony 45 pda: PArrayDataArray;
3548     i: integer;
3549 tony 33 begin
3550     inherited InternalCancel;
3551     Buff := GetActiveBuf;
3552 tony 45 if Buff <> nil then
3553     begin
3554     pda := PArrayDataArray(Buff + FArrayCacheOffset);
3555     for i := 0 to ArrayFieldCount - 1 do
3556     pda^[i].ArrayIntf.CancelChanges;
3557 tony 33 CurRec := FCurrentRecord;
3558     AdjustRecordOnInsert(Buff);
3559     if (State = dsEdit) then begin
3560     CopyRecordBuffer(FOldBuffer, Buff);
3561     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3562     end else begin
3563     CopyRecordBuffer(FModelBuffer, Buff);
3564     PRecordData(Buff)^.rdUpdateStatus := usDeleted;
3565     PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
3566     PRecordData(Buff)^.rdBookmarkFlag := bfEOF;
3567     FCurrentRecord := CurRec;
3568     end;
3569     end;
3570     end;
3571    
3572    
3573     procedure TIBCustomDataSet.InternalClose;
3574     begin
3575     if FDidActivate then
3576     DeactivateTransaction;
3577     FQSelect.Close;
3578     ClearBlobCache;
3579 tony 45 ClearArrayCache;
3580 tony 33 FreeRecordBuffer(FModelBuffer);
3581     FreeRecordBuffer(FOldBuffer);
3582     FCurrentRecord := -1;
3583     FOpen := False;
3584     FRecordCount := 0;
3585     FDeletedRecords := 0;
3586     FRecordSize := 0;
3587     FBPos := 0;
3588     FOBPos := 0;
3589     FCacheSize := 0;
3590     FOldCacheSize := 0;
3591     FBEnd := 0;
3592     FOBEnd := 0;
3593     FreeMem(FBufferCache);
3594     FBufferCache := nil;
3595 tony 45 FreeMem(FFieldColumns);
3596     FFieldColumns := nil;
3597 tony 33 FreeMem(FOldBufferCache);
3598     FOldBufferCache := nil;
3599     BindFields(False);
3600 tony 35 ResetParser;
3601 tony 33 if DefaultFields then DestroyFields;
3602     end;
3603    
3604     procedure TIBCustomDataSet.InternalDelete;
3605     var
3606     Buff: PChar;
3607     begin
3608     FBase.SetCursor;
3609     try
3610     Buff := GetActiveBuf;
3611     if CanDelete then
3612     begin
3613     if not CachedUpdates then
3614     InternalDeleteRecord(FQDelete, Buff)
3615     else
3616     begin
3617     with PRecordData(Buff)^ do
3618     begin
3619     if rdCachedUpdateStatus = cusInserted then
3620     rdCachedUpdateStatus := cusUninserted
3621     else begin
3622     rdUpdateStatus := usDeleted;
3623     rdCachedUpdateStatus := cusDeleted;
3624     end;
3625     end;
3626     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3627     end;
3628     Inc(FDeletedRecords);
3629     FUpdatesPending := True;
3630     end else
3631     IBError(ibxeCannotDelete, [nil]);
3632     finally
3633     FBase.RestoreCursor;
3634     end;
3635     end;
3636    
3637     procedure TIBCustomDataSet.InternalFirst;
3638     begin
3639     FCurrentRecord := -1;
3640     end;
3641    
3642     procedure TIBCustomDataSet.InternalGotoBookmark(Bookmark: Pointer);
3643     begin
3644     FCurrentRecord := PInteger(Bookmark)^;
3645     end;
3646    
3647     procedure TIBCustomDataSet.InternalHandleException;
3648     begin
3649     FBase.HandleException(Self)
3650     end;
3651    
3652     procedure TIBCustomDataSet.InternalInitFieldDefs;
3653     begin
3654     if not InternalPrepared then
3655     begin
3656     InternalPrepare;
3657     exit;
3658     end;
3659     FieldDefsFromQuery(FQSelect);
3660     end;
3661    
3662     procedure TIBCustomDataSet.FieldDefsFromQuery(SourceQuery: TIBSQL);
3663     const
3664     DefaultSQL = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3665     'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME ' + {do not localize}
3666     'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3667     'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3668     'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3669     'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3670     ' (not F.RDB$DEFAULT_VALUE is NULL)) '; {do not localize}
3671     var
3672     FieldType: TFieldType;
3673     FieldSize: Word;
3674 tony 45 charSetID: short;
3675 tony 33 CharSetSize: integer;
3676 tony 39 CharSetName: RawByteString;
3677     FieldCodePage: TSystemCodePage;
3678 tony 33 FieldNullable : Boolean;
3679     i, FieldPosition, FieldPrecision: Integer;
3680     FieldAliasName, DBAliasName: string;
3681 tony 45 aRelationName, FieldName: string;
3682 tony 33 Query : TIBSQL;
3683     FieldIndex: Integer;
3684     FRelationNodes : TRelationNode;
3685 tony 45 aArrayDimensions: integer;
3686     aArrayBounds: TArrayBounds;
3687     ArrayMetaData: IArrayMetaData;
3688 tony 33
3689     function Add_Node(Relation, Field : String) : TRelationNode;
3690     var
3691     FField : TFieldNode;
3692     begin
3693     if FRelationNodes.RelationName = '' then
3694     Result := FRelationNodes
3695     else
3696     begin
3697     Result := TRelationNode.Create;
3698     Result.NextRelation := FRelationNodes;
3699     end;
3700     Result.RelationName := Relation;
3701     FRelationNodes := Result;
3702     Query.Params[0].AsString := Relation;
3703     Query.ExecQuery;
3704     while not Query.Eof do
3705     begin
3706     FField := TFieldNode.Create;
3707     FField.FieldName := Query.Fields[2].AsString;
3708     FField.DEFAULT_VALUE := not Query.Fields[1].IsNull;
3709     FField.COMPUTED_BLR := not Query.Fields[0].IsNull;
3710     FField.NextField := Result.FieldNodes;
3711     Result.FieldNodes := FField;
3712     Query.Next;
3713     end;
3714     Query.Close;
3715     end;
3716    
3717     function Has_COMPUTED_BLR(Relation, Field : String) : Boolean;
3718     var
3719     FRelation : TRelationNode;
3720     FField : TFieldNode;
3721     begin
3722     FRelation := FRelationNodes;
3723     while Assigned(FRelation) and
3724     (FRelation.RelationName <> Relation) do
3725     FRelation := FRelation.NextRelation;
3726     if not Assigned(FRelation) then
3727     FRelation := Add_Node(Relation, Field);
3728     Result := false;
3729     FField := FRelation.FieldNodes;
3730     while Assigned(FField) do
3731     if FField.FieldName = Field then
3732     begin
3733     Result := Ffield.COMPUTED_BLR;
3734     Exit;
3735     end
3736     else
3737     FField := Ffield.NextField;
3738     end;
3739    
3740     function Has_DEFAULT_VALUE(Relation, Field : String) : Boolean;
3741     var
3742     FRelation : TRelationNode;
3743     FField : TFieldNode;
3744     begin
3745     FRelation := FRelationNodes;
3746     while Assigned(FRelation) and
3747     (FRelation.RelationName <> Relation) do
3748     FRelation := FRelation.NextRelation;
3749     if not Assigned(FRelation) then
3750     FRelation := Add_Node(Relation, Field);
3751     Result := false;
3752     FField := FRelation.FieldNodes;
3753     while Assigned(FField) do
3754     if FField.FieldName = Field then
3755     begin
3756     Result := Ffield.DEFAULT_VALUE;
3757     Exit;
3758     end
3759     else
3760     FField := Ffield.NextField;
3761     end;
3762    
3763     Procedure FreeNodes;
3764     var
3765     FRelation : TRelationNode;
3766     FField : TFieldNode;
3767     begin
3768     while Assigned(FRelationNodes) do
3769     begin
3770     While Assigned(FRelationNodes.FieldNodes) do
3771     begin
3772     FField := FRelationNodes.FieldNodes.NextField;
3773     FRelationNodes.FieldNodes.Free;
3774     FRelationNodes.FieldNodes := FField;
3775     end;
3776     FRelation := FRelationNodes.NextRelation;
3777     FRelationNodes.Free;
3778     FRelationNodes := FRelation;
3779     end;
3780     end;
3781    
3782     begin
3783     FRelationNodes := TRelationNode.Create;
3784     FNeedsRefresh := False;
3785 tony 45 if not Database.InternalTransaction.InTransaction then
3786     Database.InternalTransaction.StartTransaction;
3787 tony 33 Query := TIBSQL.Create(self);
3788     try
3789     Query.Database := DataBase;
3790     Query.Transaction := Database.InternalTransaction;
3791     FieldDefs.BeginUpdate;
3792     FieldDefs.Clear;
3793     FieldIndex := 0;
3794 tony 45 if (Length(FMappedFieldPosition) < SourceQuery.MetaData.Count) then
3795     SetLength(FMappedFieldPosition, SourceQuery.MetaData.Count);
3796 tony 33 Query.SQL.Text := DefaultSQL;
3797     Query.Prepare;
3798 tony 45 SetLength(FAliasNameMap, SourceQuery.MetaData.Count);
3799     SetLength(FAliasNameList, SourceQuery.MetaData.Count);
3800     for i := 0 to SourceQuery.MetaData.GetCount - 1 do
3801     with SourceQuery.MetaData[i] do
3802 tony 33 begin
3803     { Get the field name }
3804 tony 45 FieldAliasName := GetName;
3805     DBAliasName := GetAliasname;
3806     aRelationName := getRelationName;
3807     FieldName := getSQLName;
3808 tony 33 FAliasNameList[i] := DBAliasName;
3809     FieldSize := 0;
3810     FieldPrecision := 0;
3811 tony 45 FieldNullable := IsNullable;
3812 tony 35 CharSetSize := 0;
3813     CharSetName := '';
3814 tony 39 FieldCodePage := CP_NONE;
3815 tony 45 aArrayDimensions := 0;
3816     SetLength(aArrayBounds,0);
3817     case SQLType of
3818 tony 33 { All VARCHAR's must be converted to strings before recording
3819     their values }
3820     SQL_VARYING, SQL_TEXT:
3821     begin
3822 tony 60 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
3823     CharSetSize := 1;
3824     CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
3825     Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
3826 tony 45 FieldSize := GetSize div CharSetSize;
3827 tony 43 FieldType := ftString;
3828 tony 33 end;
3829     { All Doubles/Floats should be cast to doubles }
3830     SQL_DOUBLE, SQL_FLOAT:
3831     FieldType := ftFloat;
3832     SQL_SHORT:
3833     begin
3834 tony 45 if (getScale = 0) then
3835 tony 33 FieldType := ftSmallInt
3836     else begin
3837     FieldType := ftBCD;
3838     FieldPrecision := 4;
3839 tony 45 FieldSize := -getScale;
3840 tony 33 end;
3841     end;
3842     SQL_LONG:
3843     begin
3844 tony 45 if (getScale = 0) then
3845 tony 33 FieldType := ftInteger
3846 tony 45 else if (getScale >= (-4)) then
3847 tony 33 begin
3848     FieldType := ftBCD;
3849     FieldPrecision := 9;
3850 tony 45 FieldSize := -getScale;
3851 tony 33 end
3852     else
3853     if Database.SQLDialect = 1 then
3854     FieldType := ftFloat
3855     else
3856     if (FieldCount > i) and (Fields[i] is TFloatField) then
3857     FieldType := ftFloat
3858     else
3859     begin
3860     FieldType := ftFMTBCD;
3861     FieldPrecision := 9;
3862 tony 45 FieldSize := -getScale;
3863 tony 33 end;
3864     end;
3865    
3866     SQL_INT64:
3867     begin
3868 tony 45 if (getScale = 0) then
3869 tony 33 FieldType := ftLargeInt
3870 tony 45 else if (getScale >= (-4)) then
3871 tony 33 begin
3872     FieldType := ftBCD;
3873     FieldPrecision := 18;
3874 tony 45 FieldSize := -getScale;
3875 tony 33 end
3876     else
3877     FieldType := ftFloat
3878     end;
3879     SQL_TIMESTAMP: FieldType := ftDateTime;
3880     SQL_TYPE_TIME: FieldType := ftTime;
3881     SQL_TYPE_DATE: FieldType := ftDate;
3882     SQL_BLOB:
3883     begin
3884     FieldSize := sizeof (TISC_QUAD);
3885 tony 45 if (getSubtype = 1) then
3886 tony 35 begin
3887 tony 60 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
3888     CharSetSize := 1;
3889     CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
3890     Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
3891 tony 43 FieldType := ftMemo;
3892 tony 35 end
3893 tony 33 else
3894     FieldType := ftBlob;
3895     end;
3896     SQL_ARRAY:
3897     begin
3898     FieldSize := sizeof (TISC_QUAD);
3899 tony 45 FieldType := ftArray;
3900     ArrayMetaData := GetArrayMetaData;
3901     if ArrayMetaData <> nil then
3902     begin
3903     aArrayDimensions := ArrayMetaData.GetDimensions;
3904     aArrayBounds := ArrayMetaData.GetBounds;
3905     end;
3906 tony 33 end;
3907     SQL_BOOLEAN:
3908     FieldType:= ftBoolean;
3909     else
3910     FieldType := ftUnknown;
3911     end;
3912     FieldPosition := i + 1;
3913     if (FieldType <> ftUnknown) and (FieldAliasName <> 'IBX_INTERNAL_DBKEY') then {do not localize}
3914     begin
3915     FMappedFieldPosition[FieldIndex] := FieldPosition;
3916     Inc(FieldIndex);
3917 tony 35 with TIBFieldDef.Create(FieldDefs,'',FieldType,0,False,FieldDefs.Count+1) do
3918 tony 33 begin
3919     Name := FieldAliasName;
3920     FAliasNameMap[FieldNo-1] := DBAliasName;
3921     Size := FieldSize;
3922     Precision := FieldPrecision;
3923     Required := not FieldNullable;
3924 tony 45 RelationName := aRelationName;
3925 tony 33 InternalCalcField := False;
3926 tony 35 CharacterSetSize := CharSetSize;
3927     CharacterSetName := CharSetName;
3928 tony 39 CodePage := FieldCodePage;
3929 tony 45 ArrayDimensions := aArrayDimensions;
3930     ArrayBounds := aArrayBounds;
3931 tony 33 if (FieldName <> '') and (RelationName <> '') then
3932     begin
3933     if Has_COMPUTED_BLR(RelationName, FieldName) then
3934     begin
3935     Attributes := [faReadOnly];
3936     InternalCalcField := True;
3937     FNeedsRefresh := True;
3938     end
3939     else
3940     begin
3941     if Has_DEFAULT_VALUE(RelationName, FieldName) then
3942     begin
3943     if not FieldNullable then
3944     Attributes := [faRequired];
3945     end
3946     else
3947     FNeedsRefresh := True;
3948     end;
3949     end;
3950     end;
3951     end;
3952     end;
3953     finally
3954     Query.free;
3955     FreeNodes;
3956     Database.InternalTransaction.Commit;
3957     FieldDefs.EndUpdate;
3958 tony 45 FieldDefs.Updated := true;
3959 tony 33 end;
3960     end;
3961    
3962     procedure TIBCustomDataSet.InternalInitRecord(Buffer: PChar);
3963     begin
3964     CopyRecordBuffer(FModelBuffer, Buffer);
3965     end;
3966    
3967     procedure TIBCustomDataSet.InternalLast;
3968     var
3969     Buffer: PChar;
3970     begin
3971     if (FQSelect.EOF) then
3972     FCurrentRecord := FRecordCount
3973     else begin
3974     Buffer := AllocRecordBuffer;
3975     try
3976 tony 45 while FQSelect.Next do
3977 tony 33 begin
3978     FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
3979     Inc(FRecordCount);
3980     end;
3981     FCurrentRecord := FRecordCount;
3982     finally
3983     FreeRecordBuffer(Buffer);
3984     end;
3985     end;
3986     end;
3987    
3988     procedure TIBCustomDataSet.InternalSetParamsFromCursor;
3989     var
3990     i: Integer;
3991 tony 45 cur_param: ISQLParam;
3992 tony 33 cur_field: TField;
3993     s: TStream;
3994     begin
3995     if FQSelect.SQL.Text = '' then
3996     IBError(ibxeEmptyQuery, [nil]);
3997     if not FInternalPrepared then
3998     InternalPrepare;
3999 tony 45 if (SQLParams.GetCount > 0) and (DataSource <> nil) and (DataSource.DataSet <> nil) then
4000 tony 33 begin
4001 tony 45 for i := 0 to SQLParams.GetCount - 1 do
4002 tony 33 begin
4003     cur_field := DataSource.DataSet.FindField(SQLParams[i].Name);
4004     cur_param := SQLParams[i];
4005     if (cur_field <> nil) then begin
4006     if (cur_field.IsNull) then
4007     cur_param.IsNull := True
4008     else case cur_field.DataType of
4009     ftString:
4010     cur_param.AsString := cur_field.AsString;
4011     ftBoolean:
4012     cur_param.AsBoolean := cur_field.AsBoolean;
4013     ftSmallint, ftWord:
4014     cur_param.AsShort := cur_field.AsInteger;
4015     ftInteger:
4016     cur_param.AsLong := cur_field.AsInteger;
4017     ftLargeInt:
4018     cur_param.AsInt64 := TLargeIntField(cur_field).AsLargeInt;
4019     ftFloat, ftCurrency:
4020     cur_param.AsDouble := cur_field.AsFloat;
4021     ftBCD:
4022     cur_param.AsCurrency := cur_field.AsCurrency;
4023     ftDate:
4024     cur_param.AsDate := cur_field.AsDateTime;
4025     ftTime:
4026     cur_param.AsTime := cur_field.AsDateTime;
4027     ftDateTime:
4028     cur_param.AsDateTime := cur_field.AsDateTime;
4029     ftBlob, ftMemo:
4030     begin
4031     s := nil;
4032     try
4033     s := DataSource.DataSet.
4034     CreateBlobStream(cur_field, bmRead);
4035 tony 45 cur_param.AsBlob := TIBDSBlobStream(s).FBlobStream.Blob;
4036 tony 33 finally
4037     s.free;
4038     end;
4039     end;
4040 tony 45 ftArray:
4041     cur_param.AsArray := TIBArrayField(cur_field).ArrayIntf;
4042 tony 33 else
4043     IBError(ibxeNotSupported, [nil]);
4044     end;
4045     end;
4046     end;
4047     end;
4048     end;
4049    
4050     procedure TIBCustomDataSet.ReQuery;
4051     begin
4052     FQSelect.Close;
4053     ClearBlobCache;
4054     FCurrentRecord := -1;
4055     FRecordCount := 0;
4056     FDeletedRecords := 0;
4057     FBPos := 0;
4058     FOBPos := 0;
4059     FBEnd := 0;
4060     FOBEnd := 0;
4061     FQSelect.Close;
4062     FQSelect.ExecQuery;
4063     FOpen := FQSelect.Open;
4064     First;
4065     end;
4066    
4067     procedure TIBCustomDataSet.InternalOpen;
4068    
4069     function RecordDataLength(n: Integer): Long;
4070     begin
4071     result := SizeOf(TRecordData) + ((n - 1) * SizeOf(TFieldData));
4072     end;
4073    
4074     begin
4075     FBase.SetCursor;
4076     try
4077     ActivateConnection;
4078     ActivateTransaction;
4079     if FQSelect.SQL.Text = '' then
4080     IBError(ibxeEmptyQuery, [nil]);
4081     if not FInternalPrepared then
4082     InternalPrepare;
4083 tony 45 if FQSelect.SQLStatementType = SQLSelect then
4084 tony 33 begin
4085     if DefaultFields then
4086     CreateFields;
4087 tony 45 FArrayFieldCount := 0;
4088 tony 33 BindFields(True);
4089     FCurrentRecord := -1;
4090     FQSelect.ExecQuery;
4091     FOpen := FQSelect.Open;
4092    
4093     { Initialize offsets, buffer sizes, etc...
4094     1. Initially FRecordSize is just the "RecordDataLength".
4095     2. Allocate a "model" buffer and do a dummy fetch
4096     3. After the dummy fetch, FRecordSize will be appropriately
4097     adjusted to reflect the additional "weight" of the field
4098     data.
4099 tony 45 4. Set up the FCalcFieldsOffset, FBlobCacheOffset, FArrayCacheOffset and FRecordBufferSize.
4100 tony 33 5. Now, with the BufferSize available, allocate memory for chunks of records
4101     6. Re-allocate the model buffer, accounting for the new
4102     FRecordBufferSize.
4103     7. Finally, calls to AllocRecordBuffer will work!.
4104     }
4105     {Step 1}
4106 tony 45 FRecordSize := RecordDataLength(FQSelect.FieldCount);
4107 tony 33 {Step 2, 3}
4108 tony 45 GetMem(FFieldColumns,sizeof(TFieldColumns) * (FQSelect.FieldCount));
4109 tony 33 IBAlloc(FModelBuffer, 0, FRecordSize);
4110 tony 45 InitModelBuffer(FQSelect, FModelBuffer);
4111 tony 33 {Step 4}
4112     FCalcFieldsOffset := FRecordSize;
4113     FBlobCacheOffset := FCalcFieldsOffset + CalcFieldsSize;
4114 tony 45 FArrayCacheOffset := (FBlobCacheOffset + (BlobFieldCount * SizeOf(TIBBlobStream)));
4115     FRecordBufferSize := FArrayCacheOffset + (ArrayFieldCount * sizeof(IArray));
4116 tony 33 {Step 5}
4117     if UniDirectional then
4118     FBufferChunkSize := FRecordBufferSize * UniCache
4119     else
4120     FBufferChunkSize := FRecordBufferSize * BufferChunks;
4121     IBAlloc(FBufferCache, FBufferChunkSize, FBufferChunkSize);
4122     if FCachedUpdates or (csReading in ComponentState) then
4123     IBAlloc(FOldBufferCache, FBufferChunkSize, FBufferChunkSize);
4124     FBPos := 0;
4125     FOBPos := 0;
4126     FBEnd := 0;
4127     FOBEnd := 0;
4128     FCacheSize := FBufferChunkSize;
4129     FOldCacheSize := FBufferChunkSize;
4130     {Step 6}
4131 tony 45 IBAlloc(FModelBuffer, RecordDataLength(FQSelect.FieldCount),
4132 tony 33 FRecordBufferSize);
4133     {Step 7}
4134     FOldBuffer := AllocRecordBuffer;
4135     end
4136     else
4137     FQSelect.ExecQuery;
4138     finally
4139     FBase.RestoreCursor;
4140     end;
4141     end;
4142    
4143     procedure TIBCustomDataSet.InternalPost;
4144     var
4145     Qry: TIBSQL;
4146     Buff: PChar;
4147     bInserting: Boolean;
4148     begin
4149     FBase.SetCursor;
4150     try
4151     Buff := GetActiveBuf;
4152     CheckEditState;
4153     AdjustRecordOnInsert(Buff);
4154     if (State = dsInsert) then
4155     begin
4156     bInserting := True;
4157     Qry := FQInsert;
4158     PRecordData(Buff)^.rdUpdateStatus := usInserted;
4159     PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4160     WriteRecordCache(FRecordCount, Buff);
4161     FCurrentRecord := FRecordCount;
4162     end
4163     else begin
4164     bInserting := False;
4165     Qry := FQModify;
4166     if PRecordData(Buff)^.rdCachedUpdateStatus = cusUnmodified then
4167     begin
4168     PRecordData(Buff)^.rdUpdateStatus := usModified;
4169     PRecordData(Buff)^.rdCachedUpdateStatus := cusModified;
4170     end
4171     else if PRecordData(Buff)^.
4172     rdCachedUpdateStatus = cusUninserted then
4173     begin
4174     PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4175     Dec(FDeletedRecords);
4176     end;
4177     end;
4178     if (not CachedUpdates) then
4179     InternalPostRecord(Qry, Buff)
4180     else begin
4181     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4182     FUpdatesPending := True;
4183     end;
4184     if bInserting then
4185     Inc(FRecordCount);
4186     finally
4187     FBase.RestoreCursor;
4188     end;
4189     end;
4190    
4191     procedure TIBCustomDataSet.InternalRefresh;
4192     begin
4193     inherited InternalRefresh;
4194     InternalRefreshRow;
4195     end;
4196    
4197     procedure TIBCustomDataSet.InternalSetToRecord(Buffer: PChar);
4198     begin
4199     InternalGotoBookmark(@(PRecordData(Buffer)^.rdRecordNumber));
4200     end;
4201    
4202     function TIBCustomDataSet.IsCursorOpen: Boolean;
4203     begin
4204     result := FOpen;
4205     end;
4206    
4207     procedure TIBCustomDataSet.Loaded;
4208     begin
4209     if assigned(FQSelect) then
4210     FBaseSQLSelect.assign(FQSelect.SQL);
4211     inherited Loaded;
4212     end;
4213    
4214     procedure TIBCustomDataSet.Post;
4215     var CancelPost: boolean;
4216     begin
4217     CancelPost := false;
4218     if assigned(FOnValidatePost) then
4219     OnValidatePost(self,CancelPost);
4220     if CancelPost then
4221     Cancel
4222     else
4223     inherited Post;
4224     end;
4225    
4226     function TIBCustomDataSet.Locate(const KeyFields: string; const KeyValues: Variant;
4227     Options: TLocateOptions): Boolean;
4228     var
4229     CurBookmark: TBookmark;
4230     begin
4231     DisableControls;
4232     try
4233     CurBookmark := Bookmark;
4234     First;
4235     result := InternalLocate(KeyFields, KeyValues, Options);
4236     if not result then
4237     Bookmark := CurBookmark;
4238     finally
4239     EnableControls;
4240     end;
4241     end;
4242    
4243     function TIBCustomDataSet.Lookup(const KeyFields: string; const KeyValues: Variant;
4244     const ResultFields: string): Variant;
4245     var
4246     fl: TList;
4247     CurBookmark: TBookmark;
4248     begin
4249     DisableControls;
4250     fl := TList.Create;
4251     CurBookmark := Bookmark;
4252     try
4253     First;
4254     if InternalLocate(KeyFields, KeyValues, []) then
4255     begin
4256     if (ResultFields <> '') then
4257     result := FieldValues[ResultFields]
4258     else
4259     result := NULL;
4260     end
4261     else
4262     result := Null;
4263     finally
4264     Bookmark := CurBookmark;
4265     fl.Free;
4266     EnableControls;
4267     end;
4268     end;
4269    
4270     procedure TIBCustomDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
4271     begin
4272     PRecordData(Buffer)^.rdRecordNumber := PInteger(Data)^;
4273     end;
4274    
4275     procedure TIBCustomDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
4276     begin
4277     PRecordData(Buffer)^.rdBookmarkFlag := Value;
4278     end;
4279    
4280     procedure TIBCustomDataSet.SetCachedUpdates(Value: Boolean);
4281     begin
4282     if not Value and FCachedUpdates then
4283     CancelUpdates;
4284     if (not (csReading in ComponentState)) and Value then
4285     CheckDatasetClosed;
4286     FCachedUpdates := Value;
4287     end;
4288    
4289     procedure TIBCustomDataSet.SetDataSource(Value: TDataSource);
4290     begin
4291     if IsLinkedTo(Value) then
4292     IBError(ibxeCircularReference, [nil]);
4293     if FDataLink <> nil then
4294     FDataLink.DataSource := Value;
4295     end;
4296    
4297     procedure TIBCustomDataSet.InternalSetFieldData(Field: TField; Buffer: Pointer);
4298     var
4299     Buff, TmpBuff: PChar;
4300     MappedFieldPos: integer;
4301     begin
4302     Buff := GetActiveBuf;
4303     if Field.FieldNo < 0 then
4304     begin
4305     TmpBuff := Buff + FRecordSize + Field.Offset;
4306     Boolean(TmpBuff[0]) := LongBool(Buffer);
4307     if Boolean(TmpBuff[0]) then
4308     Move(Buffer^, TmpBuff[1], Field.DataSize);
4309     WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4310     end
4311     else begin
4312     CheckEditState;
4313     with PRecordData(Buff)^ do
4314     begin
4315     { If inserting, Adjust record position }
4316     AdjustRecordOnInsert(Buff);
4317     MappedFieldPos := FMappedFieldPosition[Field.FieldNo - 1];
4318     if (MappedFieldPos > 0) and
4319     (MappedFieldPos <= rdFieldCount) then
4320 tony 45 with rdFields[MappedFieldPos], FFieldColumns^[MappedFieldPos] do
4321 tony 33 begin
4322     Field.Validate(Buffer);
4323     if (Buffer = nil) or
4324     (Field is TIBStringField) and (PChar(Buffer)[0] = #0) then
4325 tony 45 fdIsNull := True
4326     else
4327     begin
4328     Move(Buffer^, Buff[fdDataOfs],fdDataSize);
4329     if (fdDataType = SQL_TEXT) or (fdDataType = SQL_VARYING) then
4330     fdDataLength := StrLen(PChar(Buffer));
4331     fdIsNull := False;
4332 tony 33 if rdUpdateStatus = usUnmodified then
4333     begin
4334     if CachedUpdates then
4335     begin
4336     FUpdatesPending := True;
4337     if State = dsInsert then
4338     rdCachedUpdateStatus := cusInserted
4339     else if State = dsEdit then
4340     rdCachedUpdateStatus := cusModified;
4341     end;
4342    
4343     if State = dsInsert then
4344     rdUpdateStatus := usInserted
4345     else
4346     rdUpdateStatus := usModified;
4347     end;
4348     WriteRecordCache(rdRecordNumber, Buff);
4349     SetModified(True);
4350     end;
4351     end;
4352     end;
4353     end;
4354     if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
4355     DataEvent(deFieldChange, PtrInt(Field));
4356     end;
4357    
4358     procedure TIBCustomDataSet.SetRecNo(Value: Integer);
4359     begin
4360     CheckBrowseMode;
4361     if (Value < 1) then
4362     Value := 1
4363     else if Value > FRecordCount then
4364     begin
4365     InternalLast;
4366     Value := Min(FRecordCount, Value);
4367     end;
4368     if (Value <> RecNo) then
4369     begin
4370     DoBeforeScroll;
4371     FCurrentRecord := Value - 1;
4372     Resync([]);
4373     DoAfterScroll;
4374     end;
4375     end;
4376    
4377     procedure TIBCustomDataSet.Disconnect;
4378     begin
4379     Close;
4380     InternalUnPrepare;
4381     end;
4382    
4383     procedure TIBCustomDataSet.SetUpdateMode(const Value: TUpdateMode);
4384     begin
4385     if not CanModify then
4386     IBError(ibxeCannotUpdate, [nil])
4387     else
4388     FUpdateMode := Value;
4389     end;
4390    
4391    
4392     procedure TIBCustomDataSet.SetUpdateObject(Value: TIBDataSetUpdateObject);
4393     begin
4394     if Value <> FUpdateObject then
4395     begin
4396     if Assigned(FUpdateObject) and (FUpdateObject.DataSet = Self) then
4397     FUpdateObject.DataSet := nil;
4398     FUpdateObject := Value;
4399     if Assigned(FUpdateObject) then
4400     begin
4401     if Assigned(FUpdateObject.DataSet) and
4402     (FUpdateObject.DataSet <> Self) then
4403     FUpdateObject.DataSet.UpdateObject := nil;
4404     FUpdateObject.DataSet := Self;
4405     end;
4406     end;
4407     end;
4408    
4409     function TIBCustomDataSet.ConstraintsStored: Boolean;
4410     begin
4411     Result := Constraints.Count > 0;
4412     end;
4413    
4414     procedure TIBCustomDataSet.ClearCalcFields(Buffer: PChar);
4415     begin
4416     FillChar(Buffer[FRecordSize], CalcFieldsSize, 0);
4417     end;
4418    
4419     procedure TIBCustomDataSet.ClearIBLinks;
4420     var i: integer;
4421     begin
4422     for i := FIBLinks.Count - 1 downto 0 do
4423     TIBControlLink(FIBLinks[i]).IBDataSet := nil;
4424     end;
4425    
4426    
4427     procedure TIBCustomDataSet.InternalUnPrepare;
4428     begin
4429     if FInternalPrepared then
4430     begin
4431     CheckDatasetClosed;
4432 tony 45 if FDidActivate then
4433     DeactivateTransaction;
4434 tony 33 FieldDefs.Clear;
4435     FieldDefs.Updated := false;
4436     FInternalPrepared := False;
4437     Setlength(FAliasNameList,0);
4438     end;
4439     end;
4440    
4441     procedure TIBCustomDataSet.InternalExecQuery;
4442     var
4443     DidActivate: Boolean;
4444     begin
4445     DidActivate := False;
4446     FBase.SetCursor;
4447     try
4448     ActivateConnection;
4449     DidActivate := ActivateTransaction;
4450     if FQSelect.SQL.Text = '' then
4451     IBError(ibxeEmptyQuery, [nil]);
4452     if not FInternalPrepared then
4453     InternalPrepare;
4454 tony 45 if FQSelect.SQLStatementType = SQLSelect then
4455 tony 33 begin
4456     IBError(ibxeIsASelectStatement, [nil]);
4457     end
4458     else
4459     FQSelect.ExecQuery;
4460     finally
4461     if DidActivate then
4462     DeactivateTransaction;
4463     FBase.RestoreCursor;
4464     end;
4465     end;
4466    
4467 tony 45 function TIBCustomDataSet.GetSelectStmtIntf: IStatement;
4468 tony 33 begin
4469 tony 45 Result := FQSelect.Statement;
4470 tony 33 end;
4471    
4472     function TIBCustomDataSet.GetParser: TSelectSQLParser;
4473     begin
4474     if not assigned(FParser) then
4475     FParser := CreateParser;
4476     Result := FParser
4477     end;
4478    
4479     procedure TIBCustomDataSet.ResetParser;
4480     begin
4481     if assigned(FParser) then
4482     begin
4483     FParser.Free;
4484     FParser := nil;
4485 tony 35 FQSelect.OnSQLChanged := nil; {Do not react to change}
4486     try
4487     FQSelect.SQL.Assign(FBaseSQLSelect);
4488     finally
4489     FQSelect.OnSQLChanged := SQLChanged;
4490     end;
4491 tony 33 end;
4492     end;
4493    
4494     function TIBCustomDataSet.HasParser: boolean;
4495     begin
4496     Result := not (csDesigning in ComponentState) and (FParser <> nil)
4497     end;
4498    
4499     procedure TIBCustomDataSet.SetGenerateParamNames(AValue: Boolean);
4500     begin
4501     if FGenerateParamNames = AValue then Exit;
4502     FGenerateParamNames := AValue;
4503     Disconnect
4504     end;
4505    
4506     procedure TIBCustomDataSet.InitRecord(Buffer: PChar);
4507     begin
4508     inherited InitRecord(Buffer);
4509     with PRecordData(Buffer)^ do
4510     begin
4511     rdUpdateStatus := TUpdateStatus(usInserted);
4512     rdBookMarkFlag := bfInserted;
4513     rdRecordNumber := -1;
4514     end;
4515     end;
4516    
4517     procedure TIBCustomDataSet.InternalInsert;
4518     begin
4519     CursorPosChanged;
4520     end;
4521    
4522     { TIBDataSet IProviderSupport }
4523    
4524 tony 45 procedure TIBCustomDataSet.PSEndTransaction(Commit: Boolean);
4525 tony 33 begin
4526     if Commit then
4527     Transaction.Commit else
4528     Transaction.Rollback;
4529     end;
4530    
4531     function TIBCustomDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams;
4532     ResultSet: Pointer = nil): Integer;
4533     var
4534     FQuery: TIBQuery;
4535     begin
4536     if Assigned(ResultSet) then
4537     begin
4538     TDataSet(ResultSet^) := TIBQuery.Create(nil);
4539     with TIBQuery(ResultSet^) do
4540     begin
4541     SQL.Text := ASQL;
4542     Params.Assign(AParams);
4543     Open;
4544     Result := RowsAffected;
4545     end;
4546     end
4547     else
4548     begin
4549     FQuery := TIBQuery.Create(nil);
4550     try
4551     FQuery.Database := Database;
4552     FQuery.Transaction := Transaction;
4553     FQuery.GenerateParamNames := True;
4554     FQuery.SQL.Text := ASQL;
4555     FQuery.Params.Assign(AParams);
4556     FQuery.ExecSQL;
4557     Result := FQuery.RowsAffected;
4558     finally
4559     FQuery.Free;
4560     end;
4561     end;
4562     end;
4563    
4564     function TIBCustomDataSet.PSGetQuoteChar: string;
4565     begin
4566     if Database.SQLDialect = 3 then
4567     Result := '"' else
4568     Result := '';
4569     end;
4570    
4571     function TIBCustomDataSet.PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError;
4572     var
4573     PrevErr: Integer;
4574     begin
4575     if Prev <> nil then
4576     PrevErr := Prev.ErrorCode else
4577     PrevErr := 0;
4578     if E is EIBError then
4579     with EIBError(E) do
4580     Result := EUpdateError.Create(E.Message, '', SQLCode, PrevErr, E) else
4581     Result := inherited PSGetUpdateException(E, Prev);
4582     end;
4583    
4584     function TIBCustomDataSet.PSInTransaction: Boolean;
4585     begin
4586     Result := Transaction.InTransaction;
4587     end;
4588    
4589     function TIBCustomDataSet.PSIsSQLBased: Boolean;
4590     begin
4591     Result := True;
4592     end;
4593    
4594     function TIBCustomDataSet.PSIsSQLSupported: Boolean;
4595     begin
4596     Result := True;
4597     end;
4598    
4599     procedure TIBCustomDataSet.PSReset;
4600     begin
4601     inherited PSReset;
4602     if Active then
4603     begin
4604     Close;
4605     Open;
4606     end;
4607     end;
4608    
4609     function TIBCustomDataSet.PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean;
4610     var
4611     UpdateAction: TIBUpdateAction;
4612     SQL: string;
4613     Params: TParams;
4614    
4615     procedure AssignParams(DataSet: TDataSet; Params: TParams);
4616     var
4617     I: Integer;
4618     Old: Boolean;
4619     Param: TParam;
4620     PName: string;
4621     Field: TField;
4622     Value: Variant;
4623     begin
4624     for I := 0 to Params.Count - 1 do
4625     begin
4626     Param := Params[I];
4627     PName := Param.Name;
4628     Old := CompareText(Copy(PName, 1, 4), 'OLD_') = 0; {do not localize}
4629     if Old then System.Delete(PName, 1, 4);
4630     Field := DataSet.FindField(PName);
4631     if not Assigned(Field) then Continue;
4632     if Old then Param.AssignFieldValue(Field, Field.OldValue) else
4633     begin
4634     Value := Field.NewValue;
4635     if VarIsEmpty(Value) then Value := Field.OldValue;
4636     Param.AssignFieldValue(Field, Value);
4637     end;
4638     end;
4639     end;
4640    
4641     begin
4642     Result := False;
4643     if Assigned(OnUpdateRecord) then
4644     begin
4645     UpdateAction := uaFail;
4646     if Assigned(FOnUpdateRecord) then
4647     begin
4648     FOnUpdateRecord(Delta, UpdateKind, UpdateAction);
4649     Result := UpdateAction = uaApplied;
4650     end;
4651     end
4652     else if Assigned(FUpdateObject) then
4653     begin
4654     SQL := FUpdateObject.GetSQL(UpdateKind).Text;
4655     if SQL <> '' then
4656     begin
4657     Params := TParams.Create;
4658     try
4659     Params.ParseSQL(SQL, True);
4660     AssignParams(Delta, Params);
4661     if PSExecuteStatement(SQL, Params) = 0 then
4662     IBError(ibxeNoRecordsAffected, [nil]);
4663     Result := True;
4664     finally
4665     Params.Free;
4666     end;
4667     end;
4668     end;
4669     end;
4670    
4671     procedure TIBCustomDataSet.PSStartTransaction;
4672     begin
4673     ActivateConnection;
4674     Transaction.StartTransaction;
4675     end;
4676    
4677     function TIBCustomDataSet.PSGetTableName: string;
4678     begin
4679     // if not FInternalPrepared then
4680     // InternalPrepare;
4681     { It is possible for the FQSelectSQL to be unprepared
4682     with FInternalPreprepared being true (see DoBeforeTransactionEnd).
4683     So check the Prepared of the SelectSQL instead }
4684     if not FQSelect.Prepared then
4685     FQSelect.Prepare;
4686     Result := FQSelect.UniqueRelationName;
4687 tony 45 end;
4688 tony 33
4689     procedure TIBDataSet.BatchInput(InputObject: TIBBatchInput);
4690     begin
4691     InternalBatchInput(InputObject);
4692     end;
4693    
4694     procedure TIBDataSet.BatchOutput(OutputObject: TIBBatchOutput);
4695     begin
4696     InternalBatchOutput(OutputObject);
4697     end;
4698    
4699     procedure TIBDataSet.ExecSQL;
4700     begin
4701     InternalExecQuery;
4702     end;
4703    
4704     procedure TIBDataSet.Prepare;
4705     begin
4706     InternalPrepare;
4707     end;
4708    
4709     procedure TIBDataSet.UnPrepare;
4710     begin
4711     InternalUnPrepare;
4712     end;
4713    
4714     function TIBDataSet.GetPrepared: Boolean;
4715     begin
4716     Result := InternalPrepared;
4717     end;
4718    
4719     procedure TIBDataSet.InternalOpen;
4720     begin
4721     ActivateConnection;
4722     ActivateTransaction;
4723     InternalSetParamsFromCursor;
4724     Inherited InternalOpen;
4725     end;
4726    
4727     procedure TIBDataSet.SetFiltered(Value: Boolean);
4728     begin
4729     if(Filtered <> Value) then
4730     begin
4731     inherited SetFiltered(value);
4732     if Active then
4733     begin
4734     Close;
4735     Open;
4736     end;
4737     end
4738     else
4739     inherited SetFiltered(value);
4740     end;
4741    
4742     function TIBCustomDataSet.BookmarkValid(Bookmark: TBookmark): Boolean;
4743     begin
4744     Result := false;
4745     if not Assigned(Bookmark) then
4746     exit;
4747     Result := PInteger(Bookmark)^ < FRecordCount;
4748     end;
4749    
4750     function TIBCustomDataSet.GetFieldData(Field: TField;
4751     Buffer: Pointer): Boolean;
4752     {$IFDEF TBCDFIELD_IS_BCD}
4753     var
4754     lTempCurr : System.Currency;
4755     begin
4756     if (Field.DataType = ftBCD) and (Buffer <> nil) then
4757     begin
4758     Result := InternalGetFieldData(Field, @lTempCurr);
4759     if Result then
4760     CurrToBCD(lTempCurr, TBCD(Buffer^), 32, Field.Size);
4761     end
4762     else
4763     {$ELSE}
4764     begin
4765     {$ENDIF}
4766     Result := InternalGetFieldData(Field, Buffer);
4767     end;
4768    
4769     function TIBCustomDataSet.GetFieldData(Field: TField; Buffer: Pointer;
4770     NativeFormat: Boolean): Boolean;
4771     begin
4772     if (Field.DataType = ftBCD) and not NativeFormat then
4773     Result := InternalGetFieldData(Field, Buffer)
4774     else
4775     Result := inherited GetFieldData(Field, Buffer, NativeFormat);
4776     end;
4777    
4778     procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer);
4779     {$IFDEF TDBDFIELD_IS_BCD}
4780     var
4781     lTempCurr : System.Currency;
4782     begin
4783     if (Field.DataType = ftBCD) and (Buffer <> nil) then
4784     begin
4785     BCDToCurr(TBCD(Buffer^), lTempCurr);
4786     InternalSetFieldData(Field, @lTempCurr);
4787     end
4788     else
4789     {$ELSE}
4790     begin
4791     {$ENDIF}
4792     InternalSetFieldData(Field, Buffer);
4793     end;
4794    
4795     procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer;
4796     NativeFormat: Boolean);
4797     begin
4798     if (not NativeFormat) and (Field.DataType = ftBCD) then
4799     InternalSetfieldData(Field, Buffer)
4800     else
4801     inherited SetFieldData(Field, buffer, NativeFormat);
4802     end;
4803    
4804     { TIBDataSetUpdateObject }
4805    
4806     constructor TIBDataSetUpdateObject.Create(AOwner: TComponent);
4807     begin
4808     inherited Create(AOwner);
4809     FRefreshSQL := TStringList.Create;
4810     end;
4811    
4812     destructor TIBDataSetUpdateObject.Destroy;
4813     begin
4814     FRefreshSQL.Free;
4815     inherited Destroy;
4816     end;
4817    
4818     procedure TIBDataSetUpdateObject.SetRefreshSQL(Value: TStrings);
4819     begin
4820     FRefreshSQL.Assign(Value);
4821     end;
4822    
4823     procedure TIBDataSetUpdateObject.InternalSetParams(Query: TIBSQL; buff: PChar);
4824     begin
4825     if not Assigned(DataSet) then Exit;
4826     DataSet.SetInternalSQLParams(Query, buff);
4827     end;
4828    
4829 tony 41 function TIBDSBlobStream.GetSize: Int64;
4830     begin
4831     Result := FBlobStream.BlobSize;
4832     end;
4833    
4834 tony 33 { TIBDSBlobStream }
4835     constructor TIBDSBlobStream.Create(AField: TField; ABlobStream: TIBBlobStream;
4836     Mode: TBlobStreamMode);
4837     begin
4838     FField := AField;
4839     FBlobStream := ABlobStream;
4840     FBlobStream.Seek(0, soFromBeginning);
4841     if (Mode = bmWrite) then
4842 tony 41 begin
4843 tony 33 FBlobStream.Truncate;
4844 tony 41 TIBCustomDataSet(FField.DataSet).RecordModified(True);
4845     TBlobField(FField).Modified := true;
4846     FHasWritten := true;
4847     end;
4848 tony 33 end;
4849    
4850     destructor TIBDSBlobStream.Destroy;
4851     begin
4852     if FHasWritten then
4853     TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
4854     inherited Destroy;
4855     end;
4856    
4857     function TIBDSBlobStream.Read(var Buffer; Count: Longint): Longint;
4858     begin
4859     result := FBlobStream.Read(Buffer, Count);
4860     end;
4861    
4862     function TIBDSBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
4863     begin
4864     result := FBlobStream.Seek(Offset, Origin);
4865     end;
4866    
4867     procedure TIBDSBlobStream.SetSize(NewSize: Longint);
4868     begin
4869     FBlobStream.SetSize(NewSize);
4870     end;
4871    
4872     function TIBDSBlobStream.Write(const Buffer; Count: Longint): Longint;
4873     begin
4874     if not (FField.DataSet.State in [dsEdit, dsInsert]) then
4875     IBError(ibxeNotEditing, [nil]);
4876     TIBCustomDataSet(FField.DataSet).RecordModified(True);
4877     TBlobField(FField).Modified := true;
4878     result := FBlobStream.Write(Buffer, Count);
4879     FHasWritten := true;
4880     { TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
4881     Removed as this caused a seek to beginning of the blob stream thus corrupting
4882     the blob stream. Moved to the destructor i.e. called after blob written}
4883     end;
4884    
4885     { TIBGenerator }
4886    
4887     procedure TIBGenerator.SetIncrement(const AValue: integer);
4888     begin
4889     if AValue < 0 then
4890     raise Exception.Create('A Generator Increment cannot be negative');
4891     FIncrement := AValue
4892     end;
4893    
4894     function TIBGenerator.GetNextValue(ADatabase: TIBDatabase;
4895     ATransaction: TIBTransaction): integer;
4896     begin
4897     with TIBSQL.Create(nil) do
4898     try
4899     Database := ADatabase;
4900     Transaction := ATransaction;
4901     if not assigned(Database) then
4902     IBError(ibxeCannotSetDatabase,[]);
4903     if not assigned(Transaction) then
4904     IBError(ibxeCannotSetTransaction,[]);
4905     with Transaction do
4906     if not InTransaction then StartTransaction;
4907     SQL.Text := Format('Select Gen_ID(%s,%d) as ID From RDB$Database',[FGeneratorName,Increment]);
4908     Prepare;
4909     ExecQuery;
4910     try
4911     Result := FieldByName('ID').AsInteger
4912     finally
4913     Close
4914     end;
4915     finally
4916     Free
4917     end;
4918     end;
4919    
4920     constructor TIBGenerator.Create(Owner: TIBCustomDataSet);
4921     begin
4922     FOwner := Owner;
4923     FIncrement := 1;
4924     end;
4925    
4926    
4927     procedure TIBGenerator.Apply;
4928     begin
4929     if (FGeneratorName <> '') and (FFieldName <> '') and Owner.FieldByName(FFieldName).IsNull then
4930     Owner.FieldByName(FFieldName).AsInteger := GetNextValue(Owner.Database,Owner.Transaction);
4931     end;
4932    
4933 tony 35
4934 tony 33 end.