ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 45
Committed: Tue Dec 6 10:33:46 2016 UTC (7 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 140716 byte(s)
Log Message:
Committing updates for Release R2-0-0

File Contents

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