ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 143
Committed: Fri Feb 23 12:11:21 2018 UTC (6 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 147700 byte(s)
Log Message:
Fixes Merged

File Contents

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