ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/runtime/nongui/IBCustomDataSet.pas
Revision: 363
Committed: Tue Dec 7 13:30:05 2021 UTC (2 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 168362 byte(s)
Log Message:
add fbintf

File Contents

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