ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 106
Committed: Thu Jan 18 14:37:35 2018 UTC (6 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 146605 byte(s)
Log Message:
Updates Merged

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