ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 107
Committed: Thu Jan 18 14:37:40 2018 UTC (6 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 146840 byte(s)
Log Message:
Fixes merged

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