ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/runtime/nongui/IBCustomDataSet.pas
Revision: 315
Committed: Thu Feb 25 11:56:36 2021 UTC (3 years, 1 month ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/runtime/nongui/IBCustomDataSet.pas
File size: 167837 byte(s)
Log Message:
Updated for IBX 4 release

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