ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 19
Committed: Mon Jul 7 13:00:15 2014 UTC (9 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 122567 byte(s)
Log Message:
Committing updates for Release R1-1-0

File Contents

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