ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 66
Committed: Wed Aug 23 08:23:42 2017 UTC (6 years, 7 months ago) by tony
Content type: text/x-pascal
File size: 141350 byte(s)
Log Message:
IBCustomDataset: ensure that TIBStringField uses the field size reported by
   Firebird rather than recomputing it.

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