ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 23
Committed: Fri Mar 13 10:26:52 2015 UTC (9 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 127086 byte(s)
Log Message:
Committing updates for Release R1-2-1

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