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