ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 37
Committed: Mon Feb 15 14:44:25 2016 UTC (8 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 134179 byte(s)
Log Message:
Committing updates for Release R1-4-0

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