ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 39
Committed: Tue May 17 08:14:52 2016 UTC (8 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 137289 byte(s)
Log Message:
Committing updates for Release R1-4-1

File Contents

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