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

File Contents

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