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

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, LocalDouble: Double;
2078 LocalInt: Integer;
2079 LocalBool: wordBool;
2080 LocalInt64: Int64;
2081 LocalCurrency: Currency;
2082 ColData: ISQLData;
2083 begin
2084 LocalData := nil;
2085 with PRecordData(Buffer)^.rdFields[FieldIndex], FFieldColumns^[FieldIndex] do
2086 begin
2087 QryResults.GetData(ColumnIndex,fdIsNull,fdDataLength,LocalData);
2088 if not fdIsNull then
2089 begin
2090 ColData := QryResults[ColumnIndex];
2091 case fdDataType of {Get Formatted data for column types that need formatting}
2092 SQL_TIMESTAMP:
2093 begin
2094 LocalDate := TimeStampToMSecs(DateTimeToTimeStamp(ColData.AsDateTime));
2095 LocalData := PByte(@LocalDate);
2096 end;
2097 SQL_TYPE_DATE:
2098 begin
2099 LocalInt := DateTimeToTimeStamp(ColData.AsDateTime).Date;
2100 LocalData := PByte(@LocalInt);
2101 end;
2102 SQL_TYPE_TIME:
2103 begin
2104 LocalInt := DateTimeToTimeStamp(ColData.AsDateTime).Time;
2105 LocalData := PByte(@LocalInt);
2106 end;
2107 SQL_SHORT, SQL_LONG:
2108 begin
2109 if (fdDataScale = 0) then
2110 begin
2111 LocalInt := ColData.AsLong;
2112 LocalData := PByte(@LocalInt);
2113 end
2114 else
2115 if (fdDataScale >= (-4)) then
2116 begin
2117 LocalCurrency := ColData.AsCurrency;
2118 LocalData := PByte(@LocalCurrency);
2119 end
2120 else
2121 begin
2122 LocalDouble := ColData.AsDouble;
2123 LocalData := PByte(@LocalDouble);
2124 end;
2125 end;
2126 SQL_INT64:
2127 begin
2128 if (fdDataScale = 0) then
2129 begin
2130 LocalInt64 := ColData.AsInt64;
2131 LocalData := PByte(@LocalInt64);
2132 end
2133 else
2134 if (fdDataScale >= (-4)) then
2135 begin
2136 LocalCurrency := ColData.AsCurrency;
2137 LocalData := PByte(@LocalCurrency);
2138 end
2139 else
2140 begin
2141 LocalDouble := ColData.AsDouble;
2142 LocalData := PByte(@LocalDouble);
2143 end
2144 end;
2145 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2146 begin
2147 LocalDouble := ColData.AsDouble;
2148 LocalData := PByte(@LocalDouble);
2149 end;
2150 SQL_BOOLEAN:
2151 begin
2152 LocalBool := ColData.AsBoolean;
2153 LocalData := PByte(@LocalBool);
2154 end;
2155 end;
2156
2157 if fdDataType = SQL_VARYING then
2158 Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2159 else
2160 Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2161 end
2162 else {Null column}
2163 if fdDataType = SQL_VARYING then
2164 FillChar(Buffer[fdDataOfs],fdDataLength,0)
2165 else
2166 FillChar(Buffer[fdDataOfs],fdDataSize,0);
2167 end;
2168 end;
2169
2170 { Read the record from FQSelect.Current into the record buffer
2171 Then write the buffer to in memory cache }
2172 procedure TIBCustomDataSet.FetchCurrentRecordToBuffer(Qry: TIBSQL;
2173 RecordNumber: Integer; Buffer: PChar);
2174 var
2175 pbd: PBlobDataArray;
2176 pda: PArrayDataArray;
2177 i, j: Integer;
2178 FieldsLoaded: Integer;
2179 p: PRecordData;
2180 begin
2181 if RecordNumber = -1 then
2182 begin
2183 InitModelBuffer(Qry,Buffer);
2184 Exit;
2185 end;
2186 p := PRecordData(Buffer);
2187 { Make sure blob cache is empty }
2188 pbd := PBlobDataArray(Buffer + FBlobCacheOffset);
2189 pda := PArrayDataArray(Buffer + FArrayCacheOffset);
2190 for i := 0 to BlobFieldCount - 1 do
2191 pbd^[i] := nil;
2192 for i := 0 to ArrayFieldCount - 1 do
2193 pda^[i] := nil;
2194
2195 { Get record information }
2196 p^.rdBookmarkFlag := bfCurrent;
2197 p^.rdFieldCount := Qry.FieldCount;
2198 p^.rdRecordNumber := RecordNumber;
2199 p^.rdUpdateStatus := usUnmodified;
2200 p^.rdCachedUpdateStatus := cusUnmodified;
2201 p^.rdSavedOffset := $FFFFFFFF;
2202
2203 { Load up the fields }
2204 FieldsLoaded := FQSelect.MetaData.Count;
2205 j := 1;
2206 for i := 0 to Qry.FieldCount - 1 do
2207 begin
2208 if (Qry = FQSelect) then
2209 j := i + 1
2210 else
2211 begin
2212 if FieldsLoaded = 0 then
2213 break;
2214 j := FQSelect.FieldIndex[Qry[i].Name] + 1;
2215 if j < 1 then
2216 continue
2217 else
2218 Dec(FieldsLoaded);
2219 end;
2220 with FQSelect.MetaData[j - 1] do
2221 if GetAliasname = 'IBX_INTERNAL_DBKEY' then {do not localize}
2222 begin
2223 if (GetSize <= 8) then
2224 p^.rdDBKey := PIBDBKEY(Qry[i].AsPointer)^;
2225 continue;
2226 end;
2227 if j > 0 then
2228 ColumnDataToBuffer(Qry.Current,i,j,Buffer);
2229 end;
2230 WriteRecordCache(RecordNumber, Buffer);
2231 end;
2232
2233 function TIBCustomDataSet.GetActiveBuf: PChar;
2234 begin
2235 case State of
2236 dsBrowse:
2237 if IsEmpty then
2238 result := nil
2239 else
2240 result := ActiveBuffer;
2241 dsEdit, dsInsert:
2242 result := ActiveBuffer;
2243 dsCalcFields:
2244 result := CalcBuffer;
2245 dsFilter:
2246 result := FFilterBuffer;
2247 dsNewValue:
2248 result := ActiveBuffer;
2249 dsOldValue:
2250 if (PRecordData(ActiveBuffer)^.rdRecordNumber =
2251 PRecordData(FOldBuffer)^.rdRecordNumber) then
2252 result := FOldBuffer
2253 else
2254 result := ActiveBuffer;
2255 else if not FOpen then
2256 result := nil
2257 else
2258 result := ActiveBuffer;
2259 end;
2260 end;
2261
2262 function TIBCustomDataSet.CachedUpdateStatus: TCachedUpdateStatus;
2263 begin
2264 if Active then
2265 result := PRecordData(GetActiveBuf)^.rdCachedUpdateStatus
2266 else
2267 result := cusUnmodified;
2268 end;
2269
2270 function TIBCustomDataSet.GetDatabase: TIBDatabase;
2271 begin
2272 result := FBase.Database;
2273 end;
2274
2275 function TIBCustomDataSet.GetDeleteSQL: TStrings;
2276 begin
2277 result := FQDelete.SQL;
2278 end;
2279
2280 function TIBCustomDataSet.GetInsertSQL: TStrings;
2281 begin
2282 result := FQInsert.SQL;
2283 end;
2284
2285 function TIBCustomDataSet.GetSQLParams: ISQLParams;
2286 begin
2287 if not FInternalPrepared then
2288 InternalPrepare;
2289 result := FQSelect.Params;
2290 end;
2291
2292 function TIBCustomDataSet.GetRefreshSQL: TStrings;
2293 begin
2294 result := FQRefresh.SQL;
2295 end;
2296
2297 function TIBCustomDataSet.GetSelectSQL: TStrings;
2298 begin
2299 result := FQSelect.SQL;
2300 end;
2301
2302 function TIBCustomDataSet.GetStatementType: TIBSQLStatementTypes;
2303 begin
2304 result := FQSelect.SQLStatementType;
2305 end;
2306
2307 function TIBCustomDataSet.GetModifySQL: TStrings;
2308 begin
2309 result := FQModify.SQL;
2310 end;
2311
2312 function TIBCustomDataSet.GetTransaction: TIBTransaction;
2313 begin
2314 result := FBase.Transaction;
2315 end;
2316
2317 procedure TIBCustomDataSet.InternalDeleteRecord(Qry: TIBSQL; Buff: Pointer);
2318 begin
2319 if (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukDelete).Text <> '')) then
2320 FUpdateObject.Apply(ukDelete,Buff)
2321 else
2322 begin
2323 SetInternalSQLParams(FQDelete.Params, Buff);
2324 FQDelete.ExecQuery;
2325 end;
2326 with PRecordData(Buff)^ do
2327 begin
2328 rdUpdateStatus := usDeleted;
2329 rdCachedUpdateStatus := cusUnmodified;
2330 end;
2331 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2332 end;
2333
2334 function TIBCustomDataSet.InternalLocate(const KeyFields: string;
2335 const KeyValues: Variant; Options: TLocateOptions): Boolean;
2336 var
2337 keyFieldList: TList;
2338 CurBookmark: TBookmark;
2339 fieldValue: Variant;
2340 lookupValues: array of variant;
2341 i, fieldCount: Integer;
2342 fieldValueAsString: string;
2343 lookupValueAsString: string;
2344 begin
2345 keyFieldList := TList.Create;
2346 try
2347 GetFieldList(keyFieldList, KeyFields);
2348 fieldCount := keyFieldList.Count;
2349 CurBookmark := Bookmark;
2350 result := false;
2351 SetLength(lookupValues, fieldCount);
2352 if not EOF then
2353 begin
2354 for i := 0 to fieldCount - 1 do {expand key values into lookupValues array}
2355 begin
2356 if VarIsArray(KeyValues) then
2357 lookupValues[i] := KeyValues[i]
2358 else
2359 if i > 0 then
2360 lookupValues[i] := NULL
2361 else
2362 lookupValues[0] := KeyValues;
2363
2364 {convert to upper case is case insensitive search}
2365 if (TField(keyFieldList[i]).DataType = ftString) and
2366 not VarIsNull(lookupValues[i]) and (loCaseInsensitive in Options) then
2367 lookupValues[i] := UpperCase(lookupValues[i]);
2368 end;
2369 end;
2370 while not result and not EOF do {search for a matching record}
2371 begin
2372 i := 0;
2373 result := true;
2374 while result and (i < fieldCount) do
2375 {see if all of the key fields matches}
2376 begin
2377 fieldValue := TField(keyFieldList[i]).Value;
2378 result := not (VarIsNull(fieldValue) xor VarIsNull(lookupValues[i]));
2379 if result and not VarIsNull(fieldValue) then
2380 begin
2381 try
2382 if TField(keyFieldList[i]).DataType = ftString then
2383 begin
2384 {strings need special handling because of the locate options that
2385 apply to them}
2386 fieldValueAsString := TField(keyFieldList[i]).AsString;
2387 lookupValueAsString := lookupValues[i];
2388 if (loCaseInsensitive in Options) then
2389 fieldValueAsString := UpperCase(fieldValueAsString);
2390
2391 if (loPartialKey in Options) then
2392 result := result and (Pos(lookupValueAsString, fieldValueAsString) = 1)
2393 else
2394 result := result and (fieldValueAsString = lookupValueAsString);
2395 end
2396 else
2397 result := result and (lookupValues[i] =
2398 VarAsType(fieldValue, VarType(lookupValues[i])));
2399 except on EVariantError do
2400 result := False;
2401 end;
2402 end;
2403 Inc(i);
2404 end;
2405 if not result then
2406 Next;
2407 end;
2408 if not result then
2409 Bookmark := CurBookmark
2410 else
2411 CursorPosChanged;
2412 finally
2413 keyFieldList.Free;
2414 SetLength(lookupValues,0)
2415 end;
2416 end;
2417
2418 procedure TIBCustomDataSet.InternalPostRecord(Qry: TIBSQL; Buff: Pointer);
2419 var
2420 i, j, k, arr: Integer;
2421 pbd: PBlobDataArray;
2422 pda: PArrayDataArray;
2423 begin
2424 pbd := PBlobDataArray(PChar(Buff) + FBlobCacheOffset);
2425 pda := PArrayDataArray(PChar(Buff) + FArrayCacheOffset);
2426 j := 0; arr := 0;
2427 for i := 0 to FieldCount - 1 do
2428 if Fields[i].IsBlob then
2429 begin
2430 k := FMappedFieldPosition[Fields[i].FieldNo -1];
2431 if pbd^[j] <> nil then
2432 begin
2433 pbd^[j].Finalize;
2434 PISC_QUAD(
2435 PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ :=
2436 pbd^[j].BlobID;
2437 PRecordData(Buff)^.rdFields[k].fdIsNull := pbd^[j].Size = 0;
2438 end
2439 else
2440 begin
2441 PRecordData(Buff)^.rdFields[k].fdIsNull := true;
2442 with PISC_QUAD(PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ do
2443 begin
2444 gds_quad_high := 0;
2445 gds_quad_low := 0;
2446 end;
2447 end;
2448 Inc(j);
2449 end
2450 else
2451 if Fields[i] is TIBArrayField then
2452 begin
2453 if pda^[arr] <> nil then
2454 begin
2455 k := FMappedFieldPosition[Fields[i].FieldNo -1];
2456 PISC_QUAD(
2457 PChar(Buff) + FFieldColumns^[k].fdDataOfs)^ := pda^[arr].ArrayIntf.GetArrayID;
2458 PRecordData(Buff)^.rdFields[k].fdIsNull := pda^[arr].ArrayIntf.IsEmpty;
2459 end;
2460 Inc(arr);
2461 end;
2462 if Assigned(FUpdateObject) then
2463 begin
2464 if (Qry = FQDelete) then
2465 FUpdateObject.Apply(ukDelete,Buff)
2466 else if (Qry = FQInsert) then
2467 FUpdateObject.Apply(ukInsert,Buff)
2468 else
2469 FUpdateObject.Apply(ukModify,Buff);
2470 end
2471 else begin
2472 SetInternalSQLParams(Qry.Params, Buff);
2473 Qry.ExecQuery;
2474 end;
2475 if Qry.FieldCount > 0 then {Has RETURNING Clause}
2476 UpdateRecordFromQuery(Qry.Current,Buff);
2477 PRecordData(Buff)^.rdUpdateStatus := usUnmodified;
2478 PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
2479 SetModified(False);
2480 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2481 if (FForcedRefresh or FNeedsRefresh) and CanRefresh then
2482 InternalRefreshRow;
2483 end;
2484
2485 procedure TIBCustomDataSet.InternalRefreshRow;
2486 var
2487 Buff: PChar;
2488 ofs: DWORD;
2489 Qry: TIBSQL;
2490 begin
2491 FBase.SetCursor;
2492 try
2493 Buff := GetActiveBuf;
2494 if CanRefresh then
2495 begin
2496 if Buff <> nil then
2497 begin
2498 if (Assigned(FUpdateObject) and (FUpdateObject.RefreshSQL.Text <> '')) then
2499 begin
2500 Qry := TIBSQL.Create(self);
2501 Qry.Database := Database;
2502 Qry.Transaction := Transaction;
2503 Qry.GoToFirstRecordOnExecute := False;
2504 Qry.SQL.Text := FUpdateObject.RefreshSQL.Text;
2505 end
2506 else
2507 Qry := FQRefresh;
2508 SetInternalSQLParams(Qry.Params, Buff);
2509 Qry.ExecQuery;
2510 try
2511 if (Qry.SQLStatementType = SQLExecProcedure) or Qry.Next then
2512 begin
2513 ofs := PRecordData(Buff)^.rdSavedOffset;
2514 FetchCurrentRecordToBuffer(Qry,
2515 PRecordData(Buff)^.rdRecordNumber,
2516 Buff);
2517 if FCachedUpdates and (ofs <> $FFFFFFFF) then
2518 begin
2519 PRecordData(Buff)^.rdSavedOffset := ofs;
2520 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2521 SaveOldBuffer(Buff);
2522 end;
2523 end;
2524 finally
2525 Qry.Close;
2526 end;
2527 if Qry <> FQRefresh then
2528 Qry.Free;
2529 end
2530 end
2531 else
2532 IBError(ibxeCannotRefresh, [nil]);
2533 finally
2534 FBase.RestoreCursor;
2535 end;
2536 end;
2537
2538 procedure TIBCustomDataSet.InternalRevertRecord(RecordNumber: Integer);
2539 var
2540 NewBuffer, OldBuffer: PRecordData;
2541
2542 begin
2543 NewBuffer := nil;
2544 OldBuffer := nil;
2545 NewBuffer := PRecordData(AllocRecordBuffer);
2546 OldBuffer := PRecordData(AllocRecordBuffer);
2547 try
2548 ReadRecordCache(RecordNumber, PChar(NewBuffer), False);
2549 ReadRecordCache(RecordNumber, PChar(OldBuffer), True);
2550 case NewBuffer^.rdCachedUpdateStatus of
2551 cusInserted:
2552 begin
2553 NewBuffer^.rdCachedUpdateStatus := cusUninserted;
2554 Inc(FDeletedRecords);
2555 end;
2556 cusModified,
2557 cusDeleted:
2558 begin
2559 if (NewBuffer^.rdCachedUpdateStatus = cusDeleted) then
2560 Dec(FDeletedRecords);
2561 CopyRecordBuffer(OldBuffer, NewBuffer);
2562 end;
2563 end;
2564
2565 if State in dsEditModes then
2566 Cancel;
2567
2568 WriteRecordCache(RecordNumber, PChar(NewBuffer));
2569
2570 if (NewBuffer^.rdCachedUpdateStatus = cusUninserted ) then
2571 ReSync([]);
2572 finally
2573 FreeRecordBuffer(PChar(NewBuffer));
2574 FreeRecordBuffer(PChar(OldBuffer));
2575 end;
2576 end;
2577
2578 { A visible record is one that is not truly deleted,
2579 and it is also listed in the FUpdateRecordTypes set }
2580
2581 function TIBCustomDataSet.IsVisible(Buffer: PChar): Boolean;
2582 begin
2583 result := True;
2584 if not (State = dsOldValue) then
2585 result :=
2586 (PRecordData(Buffer)^.rdCachedUpdateStatus in FUpdateRecordTypes) and
2587 (not ((PRecordData(Buffer)^.rdCachedUpdateStatus = cusUnmodified) and
2588 (PRecordData(Buffer)^.rdUpdateStatus = usDeleted)));
2589 end;
2590
2591
2592 function TIBCustomDataSet.LocateNext(const KeyFields: string;
2593 const KeyValues: Variant; Options: TLocateOptions): Boolean;
2594 begin
2595 DisableControls;
2596 try
2597 result := InternalLocate(KeyFields, KeyValues, Options);
2598 finally
2599 EnableControls;
2600 end;
2601 end;
2602
2603 procedure TIBCustomDataSet.InternalPrepare;
2604 begin
2605 if FInternalPrepared then
2606 Exit;
2607 FBase.SetCursor;
2608 try
2609 ActivateConnection;
2610 ActivateTransaction;
2611 FBase.CheckDatabase;
2612 FBase.CheckTransaction;
2613 if HasParser and (FParser.SQLText <> FQSelect.SQL.Text) then
2614 begin
2615 FQSelect.OnSQLChanged := nil; {Do not react to change}
2616 try
2617 FQSelect.SQL.Text := FParser.SQLText;
2618 finally
2619 FQSelect.OnSQLChanged := SQLChanged;
2620 end;
2621 end;
2622 // writeln( FQSelect.SQL.Text);
2623 if FQSelect.SQL.Text <> '' then
2624 begin
2625 if not FQSelect.Prepared then
2626 begin
2627 FQSelect.GenerateParamNames := FGenerateParamNames;
2628 FQSelect.ParamCheck := ParamCheck;
2629 FQSelect.Prepare;
2630 end;
2631 FQDelete.GenerateParamNames := FGenerateParamNames;
2632 if (Trim(FQDelete.SQL.Text) <> '') and (not FQDelete.Prepared) then
2633 FQDelete.Prepare;
2634 FQInsert.GenerateParamNames := FGenerateParamNames;
2635 if (Trim(FQInsert.SQL.Text) <> '') and (not FQInsert.Prepared) then
2636 FQInsert.Prepare;
2637 FQRefresh.GenerateParamNames := FGenerateParamNames;
2638 if (Trim(FQRefresh.SQL.Text) <> '') and (not FQRefresh.Prepared) then
2639 FQRefresh.Prepare;
2640 FQModify.GenerateParamNames := FGenerateParamNames;
2641 if (Trim(FQModify.SQL.Text) <> '') and (not FQModify.Prepared) then
2642 FQModify.Prepare;
2643 FInternalPrepared := True;
2644 InternalInitFieldDefs;
2645 end else
2646 IBError(ibxeEmptyQuery, [nil]);
2647 finally
2648 FBase.RestoreCursor;
2649 end;
2650 end;
2651
2652 procedure TIBCustomDataSet.RecordModified(Value: Boolean);
2653 begin
2654 SetModified(Value);
2655 end;
2656
2657 procedure TIBCustomDataSet.RevertRecord;
2658 var
2659 Buff: PRecordData;
2660 begin
2661 if FCachedUpdates and FUpdatesPending then
2662 begin
2663 Buff := PRecordData(GetActiveBuf);
2664 InternalRevertRecord(Buff^.rdRecordNumber);
2665 ReadRecordCache(Buff^.rdRecordNumber, PChar(Buff), False);
2666 DataEvent(deRecordChange, 0);
2667 end;
2668 end;
2669
2670 procedure TIBCustomDataSet.SaveOldBuffer(Buffer: PChar);
2671 var
2672 OldBuffer: Pointer;
2673 procedure CopyOldBuffer;
2674 begin
2675 CopyRecordBuffer(Buffer, OldBuffer);
2676 if BlobFieldCount > 0 then
2677 FillChar(PChar(OldBuffer)[FBlobCacheOffset],
2678 BlobFieldCount * SizeOf(TIBBlobStream) + ArrayFieldCount * SizeOf(IArray),
2679 0);
2680 end;
2681
2682 begin
2683 if (Buffer <> nil) and (PRecordData(Buffer)^.rdRecordNumber >= 0) then
2684 begin
2685 OldBuffer := AllocRecordBuffer;
2686 try
2687 if (PRecordData(Buffer)^.rdSavedOffset = $FFFFFFFF) then
2688 begin
2689 PRecordData(Buffer)^.rdSavedOffset := AdjustPosition(FOldBufferCache, 0,
2690 FILE_END);
2691 CopyOldBuffer;
2692 WriteCache(FOldBufferCache, 0, FILE_CURRENT, OldBuffer);
2693 WriteCache(FBufferCache, PRecordData(Buffer)^.rdRecordNumber * FRecordBufferSize,
2694 FILE_BEGIN, Buffer);
2695 end
2696 else begin
2697 CopyOldBuffer;
2698 WriteCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
2699 OldBuffer);
2700 end;
2701 finally
2702 FreeRecordBuffer(PChar(OldBuffer));
2703 end;
2704 end;
2705 end;
2706
2707 procedure TIBCustomDataSet.SetBufferChunks(Value: Integer);
2708 begin
2709 if (Value <= 0) then
2710 FBufferChunks := BufferCacheSize
2711 else
2712 FBufferChunks := Value;
2713 end;
2714
2715 procedure TIBCustomDataSet.SetDatabase(Value: TIBDatabase);
2716 begin
2717 if (FBase.Database <> Value) then
2718 begin
2719 CheckDatasetClosed;
2720 InternalUnPrepare;
2721 FBase.Database := Value;
2722 FQDelete.Database := Value;
2723 FQInsert.Database := Value;
2724 FQRefresh.Database := Value;
2725 FQSelect.Database := Value;
2726 FQModify.Database := Value;
2727 FDatabaseInfo.Database := Value;
2728 FGeneratorField.Database := Value;
2729 end;
2730 end;
2731
2732 procedure TIBCustomDataSet.SetDeleteSQL(Value: TStrings);
2733 begin
2734 if FQDelete.SQL.Text <> Value.Text then
2735 begin
2736 Disconnect;
2737 FQDelete.SQL.Assign(Value);
2738 end;
2739 end;
2740
2741 procedure TIBCustomDataSet.SetInsertSQL(Value: TStrings);
2742 begin
2743 if FQInsert.SQL.Text <> Value.Text then
2744 begin
2745 Disconnect;
2746 FQInsert.SQL.Assign(Value);
2747 end;
2748 end;
2749
2750 procedure TIBCustomDataSet.SetInternalSQLParams(Params: ISQLParams; Buffer: Pointer);
2751 var
2752 i, j: Integer;
2753 cr, data: PChar;
2754 fn: string;
2755 st: RawByteString;
2756 OldBuffer: Pointer;
2757 ts: TTimeStamp;
2758 Param: ISQLParam;
2759 begin
2760 if (Buffer = nil) then
2761 IBError(ibxeBufferNotSet, [nil]);
2762 if (not FInternalPrepared) then
2763 InternalPrepare;
2764 OldBuffer := nil;
2765 try
2766 for i := 0 to Params.GetCount - 1 do
2767 begin
2768 Param := Params[i];
2769 fn := Param.Name;
2770 if (Pos('OLD_', fn) = 1) then {mbcs ok}
2771 begin
2772 fn := Copy(fn, 5, Length(fn));
2773 if not Assigned(OldBuffer) then
2774 begin
2775 OldBuffer := AllocRecordBuffer;
2776 ReadRecordCache(PRecordData(Buffer)^.rdRecordNumber, OldBuffer, True);
2777 end;
2778 cr := OldBuffer;
2779 end
2780 else if (Pos('NEW_', fn) = 1) then {mbcs ok}
2781 begin
2782 fn := Copy(fn, 5, Length(fn));
2783 cr := Buffer;
2784 end
2785 else
2786 cr := Buffer;
2787 j := FQSelect.FieldIndex[fn] + 1;
2788 if (j > 0) then
2789 with PRecordData(cr)^,rdFields[j], FFieldColumns^[j] do
2790 begin
2791 if Param.name = 'IBX_INTERNAL_DBKEY' then {do not localize}
2792 begin
2793 PIBDBKey(Param.AsPointer)^ := rdDBKey;
2794 continue;
2795 end;
2796 if fdIsNull then
2797 Param.IsNull := True
2798 else begin
2799 Param.IsNull := False;
2800 data := cr + fdDataOfs;
2801 case fdDataType of
2802 SQL_TEXT, SQL_VARYING:
2803 begin
2804 SetString(st, data, fdDataLength);
2805 SetCodePage(st,fdCodePage,false);
2806 Param.AsString := st;
2807 end;
2808 SQL_FLOAT, SQL_DOUBLE, SQL_D_FLOAT:
2809 Param.AsDouble := PDouble(data)^;
2810 SQL_SHORT, SQL_LONG:
2811 begin
2812 if fdDataScale = 0 then
2813 Param.AsLong := PLong(data)^
2814 else
2815 if fdDataScale >= (-4) then
2816 Param.AsCurrency := PCurrency(data)^
2817 else
2818 Param.AsDouble := PDouble(data)^;
2819 end;
2820 SQL_INT64:
2821 begin
2822 if fdDataScale = 0 then
2823 Param.AsInt64 := PInt64(data)^
2824 else
2825 if fdDataScale >= (-4) then
2826 Param.AsCurrency := PCurrency(data)^
2827 else
2828 Param.AsDouble := PDouble(data)^;
2829 end;
2830 SQL_BLOB, SQL_ARRAY, SQL_QUAD:
2831 Param.AsQuad := PISC_QUAD(data)^;
2832 SQL_TYPE_DATE:
2833 begin
2834 ts.Date := PInt(data)^;
2835 ts.Time := 0;
2836 Param.AsDate := TimeStampToDateTime(ts);
2837 end;
2838 SQL_TYPE_TIME:
2839 begin
2840 ts.Date := 0;
2841 ts.Time := PInt(data)^;
2842 Param.AsTime := TimeStampToDateTime(ts);
2843 end;
2844 SQL_TIMESTAMP:
2845 Param.AsDateTime :=
2846 TimeStampToDateTime(MSecsToTimeStamp(trunc(PDouble(data)^)));
2847 SQL_BOOLEAN:
2848 Param.AsBoolean := PWordBool(data)^;
2849 end;
2850 end;
2851 end;
2852 end;
2853 finally
2854 if (OldBuffer <> nil) then
2855 FreeRecordBuffer(PChar(OldBuffer));
2856 end;
2857 end;
2858
2859 procedure TIBCustomDataSet.SetRefreshSQL(Value: TStrings);
2860 begin
2861 if FQRefresh.SQL.Text <> Value.Text then
2862 begin
2863 Disconnect;
2864 FQRefresh.SQL.Assign(Value);
2865 end;
2866 end;
2867
2868 procedure TIBCustomDataSet.SetSelectSQL(Value: TStrings);
2869 begin
2870 if FQSelect.SQL.Text <> Value.Text then
2871 begin
2872 Disconnect;
2873 FQSelect.SQL.Assign(Value);
2874 end;
2875 end;
2876
2877 procedure TIBCustomDataSet.SetModifySQL(Value: TStrings);
2878 begin
2879 if FQModify.SQL.Text <> Value.Text then
2880 begin
2881 Disconnect;
2882 FQModify.SQL.Assign(Value);
2883 end;
2884 end;
2885
2886 procedure TIBCustomDataSet.SetTransaction(Value: TIBTransaction);
2887 begin
2888 if (FBase.Transaction <> Value) then
2889 begin
2890 CheckDatasetClosed;
2891 FBase.Transaction := Value;
2892 FQDelete.Transaction := Value;
2893 FQInsert.Transaction := Value;
2894 FQRefresh.Transaction := Value;
2895 FQSelect.Transaction := Value;
2896 FQModify.Transaction := Value;
2897 FGeneratorField.Transaction := Value;
2898 end;
2899 end;
2900
2901 procedure TIBCustomDataSet.SetUniDirectional(Value: Boolean);
2902 begin
2903 CheckDatasetClosed;
2904 FUniDirectional := Value;
2905 end;
2906
2907 procedure TIBCustomDataSet.SetUpdateRecordTypes(Value: TIBUpdateRecordTypes);
2908 begin
2909 FUpdateRecordTypes := Value;
2910 if Active then
2911 First;
2912 end;
2913
2914 procedure TIBCustomDataSet.RefreshParams;
2915 var
2916 DataSet: TDataSet;
2917 begin
2918 DisableControls;
2919 try
2920 if FDataLink.DataSource <> nil then
2921 begin
2922 DataSet := FDataLink.DataSource.DataSet;
2923 if DataSet <> nil then
2924 if DataSet.Active and (DataSet.State <> dsSetKey) then
2925 begin
2926 Close;
2927 Open;
2928 end;
2929 end;
2930 finally
2931 EnableControls;
2932 end;
2933 end;
2934
2935 procedure TIBCustomDataSet.RegisterIBLink(Sender: TIBControlLink);
2936 begin
2937 if FIBLinks.IndexOf(Sender) = -1 then
2938 FIBLinks.Add(Sender);
2939 end;
2940
2941
2942 procedure TIBCustomDataSet.SQLChanging(Sender: TObject);
2943 begin
2944 Active := false;
2945 { if FOpen then
2946 InternalClose;}
2947 if FInternalPrepared then
2948 InternalUnPrepare;
2949 FieldDefs.Clear;
2950 FieldDefs.Updated := false;
2951 end;
2952
2953 procedure TIBCustomDataSet.SQLChanged(Sender: TObject);
2954 begin
2955 FBaseSQLSelect.assign(FQSelect.SQL);
2956 end;
2957
2958 { I can "undelete" uninserted records (make them "inserted" again).
2959 I can "undelete" cached deleted (the deletion hasn't yet occurred) }
2960 procedure TIBCustomDataSet.Undelete;
2961 var
2962 Buff: PRecordData;
2963 begin
2964 CheckActive;
2965 Buff := PRecordData(GetActiveBuf);
2966 with Buff^ do
2967 begin
2968 if rdCachedUpdateStatus = cusUninserted then
2969 begin
2970 rdCachedUpdateStatus := cusInserted;
2971 Dec(FDeletedRecords);
2972 end
2973 else if (rdUpdateStatus = usDeleted) and
2974 (rdCachedUpdateStatus = cusDeleted) then
2975 begin
2976 rdCachedUpdateStatus := cusUnmodified;
2977 rdUpdateStatus := usUnmodified;
2978 Dec(FDeletedRecords);
2979 end;
2980 WriteRecordCache(rdRecordNumber, PChar(Buff));
2981 end;
2982 end;
2983
2984 procedure TIBCustomDataSet.UnRegisterIBLink(Sender: TIBControlLink);
2985 begin
2986 FIBLinks.Remove(Sender);
2987 end;
2988
2989 function TIBCustomDataSet.UpdateStatus: TUpdateStatus;
2990 begin
2991 if Active then
2992 if GetActiveBuf <> nil then
2993 result := PRecordData(GetActiveBuf)^.rdUpdateStatus
2994 else
2995 result := usUnmodified
2996 else
2997 result := usUnmodified;
2998 end;
2999
3000 function TIBCustomDataSet.IsSequenced: Boolean;
3001 begin
3002 Result := Assigned( FQSelect ) and FQSelect.EOF;
3003 end;
3004
3005 function TIBCustomDataSet.ParamByName(ParamName: String): ISQLParam;
3006 begin
3007 ActivateConnection;
3008 ActivateTransaction;
3009 if not FInternalPrepared then
3010 InternalPrepare;
3011 Result := Params.ByName(ParamName);
3012 end;
3013
3014 {Beware: the parameter FCache is used as an identifier to determine which
3015 cache is being operated on and is not referenced in the computation.
3016 The result is an adjusted offset into the identified cache, either the
3017 Buffer Cache or the old Buffer Cache.}
3018
3019 function TIBCustomDataSet.AdjustPosition(FCache: PChar; Offset: DWORD;
3020 Origin: Integer): DWORD;
3021 var
3022 OldCacheSize: Integer;
3023 begin
3024 if (FCache = FBufferCache) then
3025 begin
3026 case Origin of
3027 FILE_BEGIN: FBPos := Offset;
3028 FILE_CURRENT: FBPos := FBPos + Offset;
3029 FILE_END: FBPos := DWORD(FBEnd) + Offset;
3030 end;
3031 OldCacheSize := FCacheSize;
3032 while (FBPos >= DWORD(FCacheSize)) do
3033 Inc(FCacheSize, FBufferChunkSize);
3034 if FCacheSize > OldCacheSize then
3035 IBAlloc(FBufferCache, FCacheSize, FCacheSize);
3036 result := FBPos;
3037 end
3038 else begin
3039 case Origin of
3040 FILE_BEGIN: FOBPos := Offset;
3041 FILE_CURRENT: FOBPos := FOBPos + Offset;
3042 FILE_END: FOBPos := DWORD(FOBEnd) + Offset;
3043 end;
3044 OldCacheSize := FOldCacheSize;
3045 while (FBPos >= DWORD(FOldCacheSize)) do
3046 Inc(FOldCacheSize, FBufferChunkSize);
3047 if FOldCacheSize > OldCacheSize then
3048 IBAlloc(FOldBufferCache, FOldCacheSize, FOldCacheSize);
3049 result := FOBPos;
3050 end;
3051 end;
3052
3053 procedure TIBCustomDataSet.ReadCache(FCache: PChar; Offset: DWORD; Origin: Integer;
3054 Buffer: PChar);
3055 var
3056 pCache: PChar;
3057 AdjustedOffset: DWORD;
3058 bOld: Boolean;
3059 begin
3060 bOld := (FCache = FOldBufferCache);
3061 AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
3062 if not bOld then
3063 pCache := FBufferCache + AdjustedOffset
3064 else
3065 pCache := FOldBufferCache + AdjustedOffset;
3066 Move(pCache^, Buffer^, DWORD(FRecordBufferSize));
3067 AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
3068 end;
3069
3070 procedure TIBCustomDataSet.ReadRecordCache(RecordNumber: Integer; Buffer: PChar;
3071 ReadOldBuffer: Boolean);
3072 begin
3073 if FUniDirectional then
3074 RecordNumber := RecordNumber mod UniCache;
3075 if (ReadOldBuffer) then
3076 begin
3077 ReadRecordCache(RecordNumber, Buffer, False);
3078 if FCachedUpdates and
3079 (PRecordData(Buffer)^.rdSavedOffset <> $FFFFFFFF) then
3080 ReadCache(FOldBufferCache, PRecordData(Buffer)^.rdSavedOffset, FILE_BEGIN,
3081 Buffer)
3082 else
3083 if ReadOldBuffer and
3084 (PRecordData(FOldBuffer)^.rdRecordNumber = RecordNumber) then
3085 CopyRecordBuffer( FOldBuffer, Buffer )
3086 end
3087 else
3088 ReadCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
3089 end;
3090
3091 procedure TIBCustomDataSet.WriteCache(FCache: PChar; Offset: DWORD; Origin: Integer;
3092 Buffer: PChar);
3093 var
3094 pCache: PChar;
3095 AdjustedOffset: DWORD;
3096 bOld: Boolean;
3097 dwEnd: DWORD;
3098 begin
3099 bOld := (FCache = FOldBufferCache);
3100 AdjustedOffset := AdjustPosition(FCache, Offset, Origin);
3101 if not bOld then
3102 pCache := FBufferCache + AdjustedOffset
3103 else
3104 pCache := FOldBufferCache + AdjustedOffset;
3105 Move(Buffer^, pCache^, FRecordBufferSize);
3106 dwEnd := AdjustPosition(FCache, FRecordBufferSize, FILE_CURRENT);
3107 if not bOld then
3108 begin
3109 if (dwEnd > FBEnd) then
3110 FBEnd := dwEnd;
3111 end
3112 else begin
3113 if (dwEnd > FOBEnd) then
3114 FOBEnd := dwEnd;
3115 end;
3116 end;
3117
3118 procedure TIBCustomDataSet.WriteRecordCache(RecordNumber: Integer; Buffer: PChar);
3119 begin
3120 if RecordNumber >= 0 then
3121 begin
3122 if FUniDirectional then
3123 RecordNumber := RecordNumber mod UniCache;
3124 WriteCache(FBufferCache, RecordNumber * FRecordBufferSize, FILE_BEGIN, Buffer);
3125 end;
3126 end;
3127
3128 function TIBCustomDataSet.AllocRecordBuffer: PChar;
3129 begin
3130 result := nil;
3131 IBAlloc(result, FRecordBufferSize, FRecordBufferSize);
3132 Move(FModelBuffer^, result^, FRecordBufferSize);
3133 end;
3134
3135 function TIBCustomDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
3136 var
3137 pb: PBlobDataArray;
3138 fs: TIBBlobStream;
3139 Buff: PChar;
3140 bTr, bDB: Boolean;
3141 begin
3142 if (Field = nil) or (Field.DataSet <> self) then
3143 IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3144 Buff := GetActiveBuf;
3145 if Buff = nil then
3146 begin
3147 fs := TIBBlobStream.Create;
3148 fs.Mode := bmReadWrite;
3149 fs.Database := Database;
3150 fs.Transaction := Transaction;
3151 fs.SetField(Field);
3152 FBlobStreamList.Add(Pointer(fs));
3153 result := TIBDSBlobStream.Create(Field, fs, Mode);
3154 exit;
3155 end;
3156 pb := PBlobDataArray(Buff + FBlobCacheOffset);
3157 if pb^[Field.Offset] = nil then
3158 begin
3159 AdjustRecordOnInsert(Buff);
3160 pb^[Field.Offset] := TIBBlobStream.Create;
3161 fs := pb^[Field.Offset];
3162 FBlobStreamList.Add(Pointer(fs));
3163 fs.Mode := bmReadWrite;
3164 fs.Database := Database;
3165 fs.Transaction := Transaction;
3166 fs.SetField(Field);
3167 fs.BlobID :=
3168 PISC_QUAD(@Buff[FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]].fdDataOfs])^;
3169 if (CachedUpdates) then
3170 begin
3171 bTr := not Transaction.InTransaction;
3172 bDB := not Database.Connected;
3173 if bDB then
3174 Database.Open;
3175 if bTr then
3176 Transaction.StartTransaction;
3177 fs.Seek(0, soFromBeginning);
3178 if bTr then
3179 Transaction.Commit;
3180 if bDB then
3181 Database.Close;
3182 end;
3183 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3184 end else
3185 fs := pb^[Field.Offset];
3186 result := TIBDSBlobStream.Create(Field, fs, Mode);
3187 end;
3188
3189 function TIBCustomDataSet.GetArray(Field: TIBArrayField): IArray;
3190 var Buff: PChar;
3191 pda: PArrayDataArray;
3192 bTr, bDB: Boolean;
3193 begin
3194 if (Field = nil) or (Field.DataSet <> self) then
3195 IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3196 Buff := GetActiveBuf;
3197 if Buff = nil then
3198 Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3199 Field.FRelationName,Field.FieldName)
3200 else
3201 begin
3202 pda := PArrayDataArray(Buff + FArrayCacheOffset);
3203 if pda^[Field.FCacheOffset] = nil then
3204 begin
3205 AdjustRecordOnInsert(Buff);
3206 if Field.IsNull then
3207 Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
3208 Field.FRelationName,Field.FieldName)
3209 else
3210 Result := Database.Attachment.OpenArray(Transaction.TransactionIntf,
3211 Field.FRelationName,Field.FieldName,Field.ArrayID);
3212 pda^[Field.FCacheOffset] := TIBArray.Create(Field,Result);
3213 FArrayList.Add(pda^[Field.FCacheOffset]);
3214 if (CachedUpdates) then
3215 begin
3216 bTr := not Transaction.InTransaction;
3217 bDB := not Database.Connected;
3218 if bDB then
3219 Database.Open;
3220 if bTr then
3221 Transaction.StartTransaction;
3222 pda^[Field.FCacheOffset].ArrayIntf.PreLoad;
3223 if bTr then
3224 Transaction.Commit;
3225 if bDB then
3226 Database.Close;
3227 end;
3228 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3229 end
3230 else
3231 Result := pda^[Field.FCacheOffset].ArrayIntf;
3232 end;
3233 end;
3234
3235 procedure TIBCustomDataSet.SetArrayIntf(AnArray: IArray; Field: TIBArrayField);
3236 var Buff: PChar;
3237 pda: PArrayDataArray;
3238 begin
3239 if (Field = nil) or (Field.DataSet <> self) then
3240 IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3241 Buff := GetActiveBuf;
3242 if Buff <> nil then
3243 begin
3244 AdjustRecordOnInsert(Buff);
3245 pda := PArrayDataArray(Buff + FArrayCacheOffset);
3246 pda^[Field.FCacheOffset].FArray := AnArray;
3247 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3248 end;
3249 end;
3250
3251 function TIBCustomDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
3252 const
3253 CMPLess = -1;
3254 CMPEql = 0;
3255 CMPGtr = 1;
3256 RetCodes: array[Boolean, Boolean] of ShortInt = ((2, CMPLess),
3257 (CMPGtr, CMPEql));
3258 begin
3259 result := RetCodes[Bookmark1 = nil, Bookmark2 = nil];
3260
3261 if Result = 2 then
3262 begin
3263 if PInteger(Bookmark1)^ < PInteger(Bookmark2)^ then
3264 Result := CMPLess
3265 else
3266 if PInteger(Bookmark1)^ > PInteger(Bookmark2)^ then
3267 Result := CMPGtr
3268 else
3269 Result := CMPEql;
3270 end;
3271 end;
3272
3273 procedure TIBCustomDataSet.DoBeforeDelete;
3274 var
3275 Buff: PRecordData;
3276 begin
3277 if not CanDelete then
3278 IBError(ibxeCannotDelete, [nil]);
3279 Buff := PRecordData(GetActiveBuf);
3280 if FCachedUpdates and
3281 (Buff^.rdCachedUpdateStatus in [cusUnmodified]) then
3282 SaveOldBuffer(PChar(Buff));
3283 inherited DoBeforeDelete;
3284 end;
3285
3286 procedure TIBCustomDataSet.DoAfterDelete;
3287 begin
3288 inherited DoAfterDelete;
3289 FBase.DoAfterDelete(self);
3290 InternalAutoCommit;
3291 end;
3292
3293 procedure TIBCustomDataSet.DoBeforeEdit;
3294 var
3295 Buff: PRecordData;
3296 begin
3297 Buff := PRecordData(GetActiveBuf);
3298 if not(CanEdit or (FQModify.SQL.Count <> 0) or
3299 (FCachedUpdates and Assigned(FOnUpdateRecord))) then
3300 IBError(ibxeCannotUpdate, [nil]);
3301 if FCachedUpdates and (Buff^.rdCachedUpdateStatus in [cusUnmodified, cusInserted]) then
3302 SaveOldBuffer(PChar(Buff));
3303 CopyRecordBuffer(GetActiveBuf, FOldBuffer);
3304 inherited DoBeforeEdit;
3305 end;
3306
3307 procedure TIBCustomDataSet.DoAfterEdit;
3308 begin
3309 inherited DoAfterEdit;
3310 FBase.DoAfterEdit(self);
3311 end;
3312
3313 procedure TIBCustomDataSet.DoBeforeInsert;
3314 begin
3315 if not CanInsert then
3316 IBError(ibxeCannotInsert, [nil]);
3317 inherited DoBeforeInsert;
3318 end;
3319
3320 procedure TIBCustomDataSet.DoAfterInsert;
3321 begin
3322 if GeneratorField.ApplyOnEvent = gaeOnNewRecord then
3323 GeneratorField.Apply;
3324 inherited DoAfterInsert;
3325 FBase.DoAfterInsert(self);
3326 end;
3327
3328 procedure TIBCustomDataSet.DoBeforeClose;
3329 begin
3330 inherited DoBeforeClose;
3331 if FInTransactionEnd and (FCloseAction = TARollback) then
3332 Exit;
3333 if State in [dsInsert,dsEdit] then
3334 begin
3335 if DataSetCloseAction = dcSaveChanges then
3336 Post;
3337 {Note this can fail with an exception e.g. due to
3338 database validation error. In which case the dataset remains open }
3339 end;
3340 if FCachedUpdates and FUpdatesPending and (DataSetCloseAction = dcSaveChanges) then
3341 ApplyUpdates;
3342 end;
3343
3344 procedure TIBCustomDataSet.DoBeforeOpen;
3345 var i: integer;
3346 begin
3347 if assigned(FParser) then
3348 FParser.Reset;
3349 for i := 0 to FIBLinks.Count - 1 do
3350 TIBControlLink(FIBLinks[i]).UpdateSQL(self);
3351 inherited DoBeforeOpen;
3352 for i := 0 to FIBLinks.Count - 1 do
3353 TIBControlLink(FIBLinks[i]).UpdateParams(self);
3354 end;
3355
3356 procedure TIBCustomDataSet.DoBeforePost;
3357 begin
3358 inherited DoBeforePost;
3359 if (State = dsInsert) and
3360 (GeneratorField.ApplyOnEvent = gaeOnPostRecord) then
3361 GeneratorField.Apply
3362 end;
3363
3364 procedure TIBCustomDataSet.DoAfterPost;
3365 begin
3366 inherited DoAfterPost;
3367 FBase.DoAfterPost(self);
3368 InternalAutoCommit;
3369 end;
3370
3371 procedure TIBCustomDataSet.FetchAll;
3372 var
3373 CurBookmark: TBookmark;
3374 begin
3375 FBase.SetCursor;
3376 try
3377 if FQSelect.EOF or not FQSelect.Open then
3378 exit;
3379 DisableControls;
3380 try
3381 CurBookmark := Bookmark;
3382 Last;
3383 Bookmark := CurBookmark;
3384 finally
3385 EnableControls;
3386 end;
3387 finally
3388 FBase.RestoreCursor;
3389 end;
3390 end;
3391
3392 procedure TIBCustomDataSet.FreeRecordBuffer(var Buffer: PChar);
3393 begin
3394 FreeMem(Buffer);
3395 Buffer := nil;
3396 end;
3397
3398 procedure TIBCustomDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
3399 begin
3400 Move(PRecordData(Buffer)^.rdRecordNumber, Data^, BookmarkSize);
3401 end;
3402
3403 function TIBCustomDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
3404 begin
3405 result := PRecordData(Buffer)^.rdBookmarkFlag;
3406 end;
3407
3408 function TIBCustomDataSet.GetCanModify: Boolean;
3409 begin
3410 result := (FQInsert.SQL.Text <> '') or
3411 (FQModify.SQL.Text <> '') or
3412 (FQDelete.SQL.Text <> '') or
3413 (Assigned(FUpdateObject));
3414 end;
3415
3416 function TIBCustomDataSet.GetCurrentRecord(Buffer: PChar): Boolean;
3417 begin
3418 if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then
3419 begin
3420 UpdateCursorPos;
3421 ReadRecordCache(PRecordData(ActiveBuffer)^.rdRecordNumber, Buffer, False);
3422 result := True;
3423 end
3424 else
3425 result := False;
3426 end;
3427
3428 function TIBCustomDataSet.GetDataSource: TDataSource;
3429 begin
3430 if FDataLink = nil then
3431 result := nil
3432 else
3433 result := FDataLink.DataSource;
3434 end;
3435
3436 function TIBCustomDataSet.GetDBAliasName(FieldNo: integer): string;
3437 begin
3438 Result := FAliasNameMap[FieldNo-1]
3439 end;
3440
3441 function TIBCustomDataSet.GetFieldDefFromAlias(aliasName: string): TFieldDef;
3442 var
3443 i: integer;
3444 begin
3445 Result := nil;
3446 for i := 0 to Length(FAliasNameMap) - 1 do
3447 if FAliasNameMap[i] = aliasName then
3448 begin
3449 Result := FieldDefs[i];
3450 Exit
3451 end;
3452 end;
3453
3454 function TIBCustomDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
3455 begin
3456 Result := DefaultFieldClasses[FieldType];
3457 end;
3458
3459 function TIBCustomDataSet.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean;
3460 begin
3461 result := GetFieldData(FieldByNumber(FieldNo), buffer);
3462 end;
3463
3464 function TIBCustomDataSet.InternalGetFieldData(Field: TField; Buffer: Pointer): Boolean;
3465 var
3466 Buff, Data: PChar;
3467 CurrentRecord: PRecordData;
3468 begin
3469 result := False;
3470 Buff := GetActiveBuf;
3471 if (Buff = nil) or
3472 (not IsVisible(Buff)) or not assigned(Field.DataSet) then
3473 exit;
3474 { The intention here is to stuff the buffer with the data for the
3475 referenced field for the current record }
3476 CurrentRecord := PRecordData(Buff);
3477 if (Field.FieldNo < 0) then
3478 begin
3479 Inc(Buff, FRecordSize + Field.Offset);
3480 result := Boolean(Buff[0]);
3481 if result and (Buffer <> nil) then
3482 Move(Buff[1], Buffer^, Field.DataSize);
3483 end
3484 else
3485 if (FMappedFieldPosition[Field.FieldNo - 1] > 0) and
3486 (FMappedFieldPosition[Field.FieldNo - 1] <= CurrentRecord^.rdFieldCount) then
3487 with CurrentRecord^.rdFields[FMappedFieldPosition[Field.FieldNo - 1]],
3488 FFieldColumns^[FMappedFieldPosition[Field.FieldNo - 1]] do
3489 begin
3490 result := not fdIsNull;
3491 if result and (Buffer <> nil) then
3492 begin
3493 Data := Buff + fdDataOfs;
3494 if (fdDataType = SQL_VARYING) or (fdDataType = SQL_TEXT) then
3495 begin
3496 if fdDataLength < Field.DataSize then
3497 begin
3498 Move(Data^, Buffer^, fdDataLength);
3499 PChar(Buffer)[fdDataLength] := #0;
3500 end
3501 else
3502 IBError(ibxeFieldSizeError,[Field.FieldName])
3503 end
3504 else
3505 Move(Data^, Buffer^, Field.DataSize);
3506 end;
3507 end;
3508 end;
3509
3510 { GetRecNo and SetRecNo both operate off of 1-based indexes as
3511 opposed to 0-based indexes.
3512 This is because we want LastRecordNumber/RecordCount = 1 }
3513
3514 function TIBCustomDataSet.GetRecNo: Integer;
3515 begin
3516 if GetActiveBuf = nil then
3517 result := 0
3518 else
3519 result := PRecordData(GetActiveBuf)^.rdRecordNumber + 1;
3520 end;
3521
3522 function TIBCustomDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
3523 DoCheck: Boolean): TGetResult;
3524 var
3525 Accept: Boolean;
3526 SaveState: TDataSetState;
3527 begin
3528 Result := grOK;
3529 if Filtered and Assigned(OnFilterRecord) then
3530 begin
3531 Accept := False;
3532 SaveState := SetTempState(dsFilter);
3533 while not Accept do
3534 begin
3535 Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3536 if Result <> grOK then
3537 break;
3538 FFilterBuffer := Buffer;
3539 try
3540 Accept := True;
3541 OnFilterRecord(Self, Accept);
3542 if not Accept and (GetMode = gmCurrent) then
3543 GetMode := gmPrior;
3544 except
3545 // FBase.HandleException(Self);
3546 end;
3547 end;
3548 RestoreState(SaveState);
3549 end
3550 else
3551 Result := InternalGetRecord(Buffer, GetMode, DoCheck);
3552 end;
3553
3554 function TIBCustomDataSet.InternalGetRecord(Buffer: PChar; GetMode: TGetMode;
3555 DoCheck: Boolean): TGetResult;
3556 begin
3557 result := grError;
3558 case GetMode of
3559 gmCurrent: begin
3560 if (FCurrentRecord >= 0) then begin
3561 if FCurrentRecord < FRecordCount then
3562 ReadRecordCache(FCurrentRecord, Buffer, False)
3563 else begin
3564 while (not FQSelect.EOF) and FQSelect.Next and
3565 (FCurrentRecord >= FRecordCount) do begin
3566 FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
3567 Inc(FRecordCount);
3568 end;
3569 FCurrentRecord := FRecordCount - 1;
3570 if (FCurrentRecord >= 0) then
3571 ReadRecordCache(FCurrentRecord, Buffer, False);
3572 end;
3573 result := grOk;
3574 end else
3575 result := grBOF;
3576 end;
3577 gmNext: begin
3578 result := grOk;
3579 if FCurrentRecord = FRecordCount then
3580 result := grEOF
3581 else if FCurrentRecord = FRecordCount - 1 then begin
3582 if (not FQSelect.EOF) then begin
3583 FQSelect.Next;
3584 Inc(FCurrentRecord);
3585 end;
3586 if (FQSelect.EOF) then begin
3587 result := grEOF;
3588 end else begin
3589 Inc(FRecordCount);
3590 FetchCurrentRecordToBuffer(FQSelect, FCurrentRecord, Buffer);
3591 end;
3592 end else if (FCurrentRecord < FRecordCount) then begin
3593 Inc(FCurrentRecord);
3594 ReadRecordCache(FCurrentRecord, Buffer, False);
3595 end;
3596 end;
3597 else { gmPrior }
3598 begin
3599 if (FCurrentRecord = 0) then begin
3600 Dec(FCurrentRecord);
3601 result := grBOF;
3602 end else if (FCurrentRecord > 0) and
3603 (FCurrentRecord <= FRecordCount) then begin
3604 Dec(FCurrentRecord);
3605 ReadRecordCache(FCurrentRecord, Buffer, False);
3606 result := grOk;
3607 end else if (FCurrentRecord = -1) then
3608 result := grBOF;
3609 end;
3610 end;
3611 if result = grOk then
3612 result := AdjustCurrentRecord(Buffer, GetMode);
3613 if result = grOk then with PRecordData(Buffer)^ do begin
3614 rdBookmarkFlag := bfCurrent;
3615 GetCalcFields(Buffer);
3616 end else if (result = grEOF) then begin
3617 CopyRecordBuffer(FModelBuffer, Buffer);
3618 PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3619 end else if (result = grBOF) then begin
3620 CopyRecordBuffer(FModelBuffer, Buffer);
3621 PRecordData(Buffer)^.rdBookmarkFlag := bfBOF;
3622 end else if (result = grError) then begin
3623 CopyRecordBuffer(FModelBuffer, Buffer);
3624 PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
3625 end;;
3626 end;
3627
3628 function TIBCustomDataSet.GetRecordCount: Integer;
3629 begin
3630 result := FRecordCount - FDeletedRecords;
3631 end;
3632
3633 function TIBCustomDataSet.GetRecordSize: Word;
3634 begin
3635 result := FRecordBufferSize;
3636 end;
3637
3638 procedure TIBCustomDataSet.InternalAutoCommit;
3639 begin
3640 with Transaction do
3641 if InTransaction and (FAutoCommit = acCommitRetaining) then
3642 begin
3643 if CachedUpdates then ApplyUpdates;
3644 CommitRetaining;
3645 end;
3646 end;
3647
3648 procedure TIBCustomDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
3649 begin
3650 CheckEditState;
3651 begin
3652 { When adding records, we *always* append.
3653 Insertion is just too costly }
3654 AdjustRecordOnInsert(Buffer);
3655 with PRecordData(Buffer)^ do
3656 begin
3657 rdUpdateStatus := usInserted;
3658 rdCachedUpdateStatus := cusInserted;
3659 end;
3660 if not CachedUpdates then
3661 InternalPostRecord(FQInsert, Buffer)
3662 else begin
3663 WriteRecordCache(FCurrentRecord, Buffer);
3664 FUpdatesPending := True;
3665 end;
3666 Inc(FRecordCount);
3667 InternalSetToRecord(Buffer);
3668 end
3669 end;
3670
3671 procedure TIBCustomDataSet.InternalCancel;
3672 var
3673 Buff: PChar;
3674 CurRec: Integer;
3675 pda: PArrayDataArray;
3676 i: integer;
3677 begin
3678 inherited InternalCancel;
3679 Buff := GetActiveBuf;
3680 if Buff <> nil then
3681 begin
3682 pda := PArrayDataArray(Buff + FArrayCacheOffset);
3683 for i := 0 to ArrayFieldCount - 1 do
3684 pda^[i].ArrayIntf.CancelChanges;
3685 CurRec := FCurrentRecord;
3686 AdjustRecordOnInsert(Buff);
3687 if (State = dsEdit) then begin
3688 CopyRecordBuffer(FOldBuffer, Buff);
3689 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3690 end else begin
3691 CopyRecordBuffer(FModelBuffer, Buff);
3692 PRecordData(Buff)^.rdUpdateStatus := usDeleted;
3693 PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
3694 PRecordData(Buff)^.rdBookmarkFlag := bfEOF;
3695 FCurrentRecord := CurRec;
3696 end;
3697 end;
3698 end;
3699
3700
3701 procedure TIBCustomDataSet.InternalClose;
3702 begin
3703 if FDidActivate then
3704 DeactivateTransaction;
3705 FQSelect.Close;
3706 ClearBlobCache;
3707 ClearArrayCache;
3708 FreeRecordBuffer(FModelBuffer);
3709 FreeRecordBuffer(FOldBuffer);
3710 FCurrentRecord := -1;
3711 FOpen := False;
3712 FRecordCount := 0;
3713 FDeletedRecords := 0;
3714 FRecordSize := 0;
3715 FBPos := 0;
3716 FOBPos := 0;
3717 FCacheSize := 0;
3718 FOldCacheSize := 0;
3719 FBEnd := 0;
3720 FOBEnd := 0;
3721 FreeMem(FBufferCache);
3722 FBufferCache := nil;
3723 FreeMem(FFieldColumns);
3724 FFieldColumns := nil;
3725 FreeMem(FOldBufferCache);
3726 FOldBufferCache := nil;
3727 BindFields(False);
3728 ResetParser;
3729 if DefaultFields then DestroyFields;
3730 end;
3731
3732 procedure TIBCustomDataSet.InternalDelete;
3733 var
3734 Buff: PChar;
3735 begin
3736 FBase.SetCursor;
3737 try
3738 Buff := GetActiveBuf;
3739 if CanDelete then
3740 begin
3741 if not CachedUpdates then
3742 InternalDeleteRecord(FQDelete, Buff)
3743 else
3744 begin
3745 with PRecordData(Buff)^ do
3746 begin
3747 if rdCachedUpdateStatus = cusInserted then
3748 rdCachedUpdateStatus := cusUninserted
3749 else begin
3750 rdUpdateStatus := usDeleted;
3751 rdCachedUpdateStatus := cusDeleted;
3752 end;
3753 end;
3754 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
3755 end;
3756 Inc(FDeletedRecords);
3757 FUpdatesPending := True;
3758 end else
3759 IBError(ibxeCannotDelete, [nil]);
3760 finally
3761 FBase.RestoreCursor;
3762 end;
3763 end;
3764
3765 procedure TIBCustomDataSet.InternalFirst;
3766 begin
3767 FCurrentRecord := -1;
3768 end;
3769
3770 procedure TIBCustomDataSet.InternalGotoBookmark(Bookmark: Pointer);
3771 begin
3772 FCurrentRecord := PInteger(Bookmark)^;
3773 end;
3774
3775 procedure TIBCustomDataSet.InternalHandleException;
3776 begin
3777 FBase.HandleException(Self)
3778 end;
3779
3780 procedure TIBCustomDataSet.InternalInitFieldDefs;
3781 begin
3782 if not InternalPrepared then
3783 begin
3784 InternalPrepare;
3785 exit;
3786 end;
3787 FieldDefsFromQuery(FQSelect);
3788 end;
3789
3790 procedure TIBCustomDataSet.FieldDefsFromQuery(SourceQuery: TIBSQL);
3791 const
3792 DefaultSQL = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3793 'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME ' + {do not localize}
3794 'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3795 'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3796 'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3797 'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3798 ' (not F.RDB$DEFAULT_VALUE is NULL)) '; {do not localize}
3799
3800 DefaultSQLODS12 = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3801 'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME, R.RDB$IDENTITY_TYPE ' + {do not localize}
3802 'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3803 'where R.RDB$RELATION_NAME = :RELATION ' + {do not localize}
3804 'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3805 'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3806 ' (not F.RDB$DEFAULT_VALUE is NULL) or ' + {do not localize}
3807 ' ( not R.RDB$IDENTITY_TYPE is NULL))' ; {do not localize}
3808
3809 var
3810 FieldType: TFieldType;
3811 FieldSize: Word;
3812 FieldDataSize: integer;
3813 charSetID: short;
3814 CharSetSize: integer;
3815 CharSetName: RawByteString;
3816 FieldCodePage: TSystemCodePage;
3817 FieldNullable : Boolean;
3818 i, FieldPosition, FieldPrecision: Integer;
3819 FieldAliasName, DBAliasName: string;
3820 aRelationName, FieldName: string;
3821 Query : TIBSQL;
3822 FieldIndex: Integer;
3823 FRelationNodes : TRelationNode;
3824 aArrayDimensions: integer;
3825 aArrayBounds: TArrayBounds;
3826 ArrayMetaData: IArrayMetaData;
3827
3828 function Add_Node(Relation, Field : String) : TRelationNode;
3829 var
3830 FField : TFieldNode;
3831 begin
3832 if FRelationNodes.RelationName = '' then
3833 Result := FRelationNodes
3834 else
3835 begin
3836 Result := TRelationNode.Create;
3837 Result.NextRelation := FRelationNodes;
3838 end;
3839 Result.RelationName := Relation;
3840 FRelationNodes := Result;
3841 Query.Params[0].AsString := Relation;
3842 Query.ExecQuery;
3843 while not Query.Eof do
3844 begin
3845 FField := TFieldNode.Create;
3846 FField.FieldName := Query.Fields[2].AsString;
3847 FField.DEFAULT_VALUE := not Query.Fields[1].IsNull;
3848 FField.COMPUTED_BLR := not Query.Fields[0].IsNull;
3849 FField.IDENTITY_COLUMN := (Query.FieldCount > 3) and not Query.Fields[3].IsNull;
3850 FField.NextField := Result.FieldNodes;
3851 Result.FieldNodes := FField;
3852 Query.Next;
3853 end;
3854 Query.Close;
3855 end;
3856
3857 function Has_COMPUTED_BLR(Relation, Field : String) : Boolean;
3858 var
3859 FRelation : TRelationNode;
3860 FField : TFieldNode;
3861 begin
3862 FRelation := FRelationNodes;
3863 while Assigned(FRelation) and
3864 (FRelation.RelationName <> Relation) do
3865 FRelation := FRelation.NextRelation;
3866 if not Assigned(FRelation) then
3867 FRelation := Add_Node(Relation, Field);
3868 Result := false;
3869 FField := FRelation.FieldNodes;
3870 while Assigned(FField) do
3871 if FField.FieldName = Field then
3872 begin
3873 Result := Ffield.COMPUTED_BLR;
3874 Exit;
3875 end
3876 else
3877 FField := Ffield.NextField;
3878 end;
3879
3880 function Has_DEFAULT_VALUE(Relation, Field : String) : Boolean;
3881 var
3882 FRelation : TRelationNode;
3883 FField : TFieldNode;
3884 begin
3885 FRelation := FRelationNodes;
3886 while Assigned(FRelation) and
3887 (FRelation.RelationName <> Relation) do
3888 FRelation := FRelation.NextRelation;
3889 if not Assigned(FRelation) then
3890 FRelation := Add_Node(Relation, Field);
3891 Result := false;
3892 FField := FRelation.FieldNodes;
3893 while Assigned(FField) do
3894 if FField.FieldName = Field then
3895 begin
3896 Result := Ffield.DEFAULT_VALUE;
3897 Exit;
3898 end
3899 else
3900 FField := Ffield.NextField;
3901 end;
3902
3903 function Is_IDENTITY_COLUMN(Relation, Field : String) : Boolean;
3904 var
3905 FRelation : TRelationNode;
3906 FField : TFieldNode;
3907 begin
3908 FRelation := FRelationNodes;
3909 while Assigned(FRelation) and
3910 (FRelation.RelationName <> Relation) do
3911 FRelation := FRelation.NextRelation;
3912 if not Assigned(FRelation) then
3913 FRelation := Add_Node(Relation, Field);
3914 Result := false;
3915 FField := FRelation.FieldNodes;
3916 while Assigned(FField) do
3917 if FField.FieldName = Field then
3918 begin
3919 Result := Ffield.IDENTITY_COLUMN;
3920 Exit;
3921 end
3922 else
3923 FField := Ffield.NextField;
3924 end;
3925
3926 Procedure FreeNodes;
3927 var
3928 FRelation : TRelationNode;
3929 FField : TFieldNode;
3930 begin
3931 while Assigned(FRelationNodes) do
3932 begin
3933 While Assigned(FRelationNodes.FieldNodes) do
3934 begin
3935 FField := FRelationNodes.FieldNodes.NextField;
3936 FRelationNodes.FieldNodes.Free;
3937 FRelationNodes.FieldNodes := FField;
3938 end;
3939 FRelation := FRelationNodes.NextRelation;
3940 FRelationNodes.Free;
3941 FRelationNodes := FRelation;
3942 end;
3943 end;
3944
3945 begin
3946 FRelationNodes := TRelationNode.Create;
3947 FNeedsRefresh := False;
3948 if not Database.InternalTransaction.InTransaction then
3949 Database.InternalTransaction.StartTransaction;
3950 Query := TIBSQL.Create(self);
3951 try
3952 Query.Database := DataBase;
3953 Query.Transaction := Database.InternalTransaction;
3954 FieldDefs.BeginUpdate;
3955 FieldDefs.Clear;
3956 FieldIndex := 0;
3957 if (Length(FMappedFieldPosition) < SourceQuery.MetaData.Count) then
3958 SetLength(FMappedFieldPosition, SourceQuery.MetaData.Count);
3959 if FDatabaseInfo.ODSMajorVersion >= 12 then
3960 Query.SQL.Text := DefaultSQLODS12
3961 else
3962 Query.SQL.Text := DefaultSQL;
3963 Query.Prepare;
3964 SetLength(FAliasNameMap, SourceQuery.MetaData.Count);
3965 SetLength(FAliasNameList, SourceQuery.MetaData.Count);
3966 for i := 0 to SourceQuery.MetaData.GetCount - 1 do
3967 with SourceQuery.MetaData[i] do
3968 begin
3969 { Get the field name }
3970 FieldAliasName := GetName;
3971 DBAliasName := GetAliasname;
3972 aRelationName := getRelationName;
3973 FieldName := getSQLName;
3974 FAliasNameList[i] := DBAliasName;
3975 FieldSize := 0;
3976 FieldDataSize := GetSize;
3977 FieldPrecision := 0;
3978 FieldNullable := IsNullable;
3979 CharSetSize := 0;
3980 CharSetName := '';
3981 FieldCodePage := CP_NONE;
3982 aArrayDimensions := 0;
3983 SetLength(aArrayBounds,0);
3984 case SQLType of
3985 { All VARCHAR's must be converted to strings before recording
3986 their values }
3987 SQL_VARYING, SQL_TEXT:
3988 begin
3989 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
3990 CharSetSize := 1;
3991 CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
3992 Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
3993 FieldSize := FieldDataSize div CharSetSize;
3994 FieldType := ftString;
3995 end;
3996 { All Doubles/Floats should be cast to doubles }
3997 SQL_DOUBLE, SQL_FLOAT:
3998 FieldType := ftFloat;
3999 SQL_SHORT:
4000 begin
4001 if (getScale = 0) then
4002 FieldType := ftSmallInt
4003 else begin
4004 FieldType := ftBCD;
4005 FieldPrecision := 4;
4006 FieldSize := -getScale;
4007 end;
4008 end;
4009 SQL_LONG:
4010 begin
4011 if (getScale = 0) then
4012 FieldType := ftInteger
4013 else if (getScale >= (-4)) then
4014 begin
4015 FieldType := ftBCD;
4016 FieldPrecision := 9;
4017 FieldSize := -getScale;
4018 end
4019 else
4020 if Database.SQLDialect = 1 then
4021 FieldType := ftFloat
4022 else
4023 if (FieldCount > i) and (Fields[i] is TFloatField) then
4024 FieldType := ftFloat
4025 else
4026 begin
4027 FieldType := ftFMTBCD;
4028 FieldPrecision := 9;
4029 FieldSize := -getScale;
4030 end;
4031 end;
4032
4033 SQL_INT64:
4034 begin
4035 if (getScale = 0) then
4036 FieldType := ftLargeInt
4037 else if (getScale >= (-4)) then
4038 begin
4039 FieldType := ftBCD;
4040 FieldPrecision := 18;
4041 FieldSize := -getScale;
4042 end
4043 else
4044 FieldType := ftFloat;
4045 end;
4046 SQL_TIMESTAMP: FieldType := ftDateTime;
4047 SQL_TYPE_TIME: FieldType := ftTime;
4048 SQL_TYPE_DATE: FieldType := ftDate;
4049 SQL_BLOB:
4050 begin
4051 FieldSize := sizeof (TISC_QUAD);
4052 if (getSubtype = 1) then
4053 begin
4054 if not Database.Attachment.CharSetWidth(getCharSetID,CharSetSize) then
4055 CharSetSize := 1;
4056 CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
4057 Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
4058 FieldType := ftMemo;
4059 end
4060 else
4061 FieldType := ftBlob;
4062 end;
4063 SQL_ARRAY:
4064 begin
4065 FieldSize := sizeof (TISC_QUAD);
4066 FieldType := ftArray;
4067 ArrayMetaData := GetArrayMetaData;
4068 if ArrayMetaData <> nil then
4069 begin
4070 aArrayDimensions := ArrayMetaData.GetDimensions;
4071 aArrayBounds := ArrayMetaData.GetBounds;
4072 end;
4073 end;
4074 SQL_BOOLEAN:
4075 FieldType:= ftBoolean;
4076 else
4077 FieldType := ftUnknown;
4078 end;
4079 FieldPosition := i + 1;
4080 if (FieldType <> ftUnknown) and (FieldAliasName <> 'IBX_INTERNAL_DBKEY') then {do not localize}
4081 begin
4082 FMappedFieldPosition[FieldIndex] := FieldPosition;
4083 Inc(FieldIndex);
4084 with TIBFieldDef.Create(FieldDefs,'',FieldType,0,False,FieldDefs.Count+1) do
4085 begin
4086 Name := FieldAliasName;
4087 FAliasNameMap[FieldNo-1] := DBAliasName;
4088 Size := FieldSize;
4089 DataSize := FieldDataSize;
4090 Precision := FieldPrecision;
4091 Required := not FieldNullable;
4092 RelationName := aRelationName;
4093 InternalCalcField := False;
4094 CharacterSetSize := CharSetSize;
4095 CharacterSetName := CharSetName;
4096 CodePage := FieldCodePage;
4097 ArrayDimensions := aArrayDimensions;
4098 ArrayBounds := aArrayBounds;
4099 if (FieldName <> '') and (RelationName <> '') then
4100 begin
4101 IdentityColumn := Is_IDENTITY_COLUMN(RelationName, FieldName);
4102 if Has_COMPUTED_BLR(RelationName, FieldName) then
4103 begin
4104 Attributes := [faReadOnly];
4105 InternalCalcField := True;
4106 FNeedsRefresh := True;
4107 end
4108 else
4109 begin
4110 if Has_DEFAULT_VALUE(RelationName, FieldName) then
4111 begin
4112 if not FieldNullable then
4113 Attributes := [faRequired];
4114 end
4115 else
4116 FNeedsRefresh := True;
4117 end;
4118 end;
4119 end;
4120 end;
4121 end;
4122 finally
4123 Query.free;
4124 FreeNodes;
4125 Database.InternalTransaction.Commit;
4126 FieldDefs.EndUpdate;
4127 FieldDefs.Updated := true;
4128 end;
4129 end;
4130
4131 procedure TIBCustomDataSet.InternalInitRecord(Buffer: PChar);
4132 begin
4133 CopyRecordBuffer(FModelBuffer, Buffer);
4134 end;
4135
4136 procedure TIBCustomDataSet.InternalLast;
4137 var
4138 Buffer: PChar;
4139 begin
4140 if (FQSelect.EOF) then
4141 FCurrentRecord := FRecordCount
4142 else begin
4143 Buffer := AllocRecordBuffer;
4144 try
4145 while FQSelect.Next do
4146 begin
4147 FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
4148 Inc(FRecordCount);
4149 end;
4150 FCurrentRecord := FRecordCount;
4151 finally
4152 FreeRecordBuffer(Buffer);
4153 end;
4154 end;
4155 end;
4156
4157 procedure TIBCustomDataSet.InternalSetParamsFromCursor;
4158 var
4159 i: Integer;
4160 cur_param: ISQLParam;
4161 cur_field: TField;
4162 s: TStream;
4163 begin
4164 if FQSelect.SQL.Text = '' then
4165 IBError(ibxeEmptyQuery, [nil]);
4166 if not FInternalPrepared then
4167 InternalPrepare;
4168 if (SQLParams.GetCount > 0) and (DataSource <> nil) and (DataSource.DataSet <> nil) then
4169 begin
4170 for i := 0 to SQLParams.GetCount - 1 do
4171 begin
4172 cur_field := DataSource.DataSet.FindField(SQLParams[i].Name);
4173 cur_param := SQLParams[i];
4174 if (cur_field <> nil) then begin
4175 if (cur_field.IsNull) then
4176 cur_param.IsNull := True
4177 else case cur_field.DataType of
4178 ftString:
4179 cur_param.AsString := cur_field.AsString;
4180 ftBoolean:
4181 cur_param.AsBoolean := cur_field.AsBoolean;
4182 ftSmallint, ftWord:
4183 cur_param.AsShort := cur_field.AsInteger;
4184 ftInteger:
4185 cur_param.AsLong := cur_field.AsInteger;
4186 ftLargeInt:
4187 cur_param.AsInt64 := TLargeIntField(cur_field).AsLargeInt;
4188 ftFloat, ftCurrency:
4189 cur_param.AsDouble := cur_field.AsFloat;
4190 ftBCD:
4191 cur_param.AsCurrency := cur_field.AsCurrency;
4192 ftDate:
4193 cur_param.AsDate := cur_field.AsDateTime;
4194 ftTime:
4195 cur_param.AsTime := cur_field.AsDateTime;
4196 ftDateTime:
4197 cur_param.AsDateTime := cur_field.AsDateTime;
4198 ftBlob, ftMemo:
4199 begin
4200 s := nil;
4201 try
4202 s := DataSource.DataSet.
4203 CreateBlobStream(cur_field, bmRead);
4204 cur_param.AsBlob := TIBDSBlobStream(s).FBlobStream.Blob;
4205 finally
4206 s.free;
4207 end;
4208 end;
4209 ftArray:
4210 cur_param.AsArray := TIBArrayField(cur_field).ArrayIntf;
4211 else
4212 IBError(ibxeNotSupported, [nil]);
4213 end;
4214 end;
4215 end;
4216 end;
4217 end;
4218
4219 procedure TIBCustomDataSet.ReQuery;
4220 begin
4221 FQSelect.Close;
4222 ClearBlobCache;
4223 FCurrentRecord := -1;
4224 FRecordCount := 0;
4225 FDeletedRecords := 0;
4226 FBPos := 0;
4227 FOBPos := 0;
4228 FBEnd := 0;
4229 FOBEnd := 0;
4230 FQSelect.Close;
4231 FQSelect.ExecQuery;
4232 FOpen := FQSelect.Open;
4233 First;
4234 end;
4235
4236 procedure TIBCustomDataSet.InternalOpen;
4237
4238 function RecordDataLength(n: Integer): Long;
4239 begin
4240 result := SizeOf(TRecordData) + ((n - 1) * SizeOf(TFieldData));
4241 end;
4242
4243 begin
4244 FBase.SetCursor;
4245 try
4246 ActivateConnection;
4247 ActivateTransaction;
4248 if FQSelect.SQL.Text = '' then
4249 IBError(ibxeEmptyQuery, [nil]);
4250 if not FInternalPrepared then
4251 InternalPrepare;
4252 if FQSelect.SQLStatementType = SQLSelect then
4253 begin
4254 if DefaultFields then
4255 CreateFields;
4256 FArrayFieldCount := 0;
4257 BindFields(True);
4258 FCurrentRecord := -1;
4259 FQSelect.ExecQuery;
4260 FOpen := FQSelect.Open;
4261
4262 { Initialize offsets, buffer sizes, etc...
4263 1. Initially FRecordSize is just the "RecordDataLength".
4264 2. Allocate a "model" buffer and do a dummy fetch
4265 3. After the dummy fetch, FRecordSize will be appropriately
4266 adjusted to reflect the additional "weight" of the field
4267 data.
4268 4. Set up the FCalcFieldsOffset, FBlobCacheOffset, FArrayCacheOffset and FRecordBufferSize.
4269 5. Now, with the BufferSize available, allocate memory for chunks of records
4270 6. Re-allocate the model buffer, accounting for the new
4271 FRecordBufferSize.
4272 7. Finally, calls to AllocRecordBuffer will work!.
4273 }
4274 {Step 1}
4275 FRecordSize := RecordDataLength(FQSelect.FieldCount);
4276 {Step 2, 3}
4277 GetMem(FFieldColumns,sizeof(TFieldColumns) * (FQSelect.FieldCount));
4278 IBAlloc(FModelBuffer, 0, FRecordSize);
4279 InitModelBuffer(FQSelect, FModelBuffer);
4280 {Step 4}
4281 FCalcFieldsOffset := FRecordSize;
4282 FBlobCacheOffset := FCalcFieldsOffset + CalcFieldsSize;
4283 FArrayCacheOffset := (FBlobCacheOffset + (BlobFieldCount * SizeOf(TIBBlobStream)));
4284 FRecordBufferSize := FArrayCacheOffset + (ArrayFieldCount * sizeof(IArray));
4285 {Step 5}
4286 if UniDirectional then
4287 FBufferChunkSize := FRecordBufferSize * UniCache
4288 else
4289 FBufferChunkSize := FRecordBufferSize * BufferChunks;
4290 IBAlloc(FBufferCache, FBufferChunkSize, FBufferChunkSize);
4291 if FCachedUpdates or (csReading in ComponentState) then
4292 IBAlloc(FOldBufferCache, FBufferChunkSize, FBufferChunkSize);
4293 FBPos := 0;
4294 FOBPos := 0;
4295 FBEnd := 0;
4296 FOBEnd := 0;
4297 FCacheSize := FBufferChunkSize;
4298 FOldCacheSize := FBufferChunkSize;
4299 {Step 6}
4300 IBAlloc(FModelBuffer, RecordDataLength(FQSelect.FieldCount),
4301 FRecordBufferSize);
4302 {Step 7}
4303 FOldBuffer := AllocRecordBuffer;
4304 end
4305 else
4306 FQSelect.ExecQuery;
4307 finally
4308 FBase.RestoreCursor;
4309 end;
4310 end;
4311
4312 procedure TIBCustomDataSet.InternalPost;
4313 var
4314 Qry: TIBSQL;
4315 Buff: PChar;
4316 bInserting: Boolean;
4317 begin
4318 FBase.SetCursor;
4319 try
4320 Buff := GetActiveBuf;
4321 CheckEditState;
4322 AdjustRecordOnInsert(Buff);
4323 if (State = dsInsert) then
4324 begin
4325 bInserting := True;
4326 Qry := FQInsert;
4327 PRecordData(Buff)^.rdUpdateStatus := usInserted;
4328 PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4329 WriteRecordCache(FRecordCount, Buff);
4330 FCurrentRecord := FRecordCount;
4331 end
4332 else begin
4333 bInserting := False;
4334 Qry := FQModify;
4335 if PRecordData(Buff)^.rdCachedUpdateStatus = cusUnmodified then
4336 begin
4337 PRecordData(Buff)^.rdUpdateStatus := usModified;
4338 PRecordData(Buff)^.rdCachedUpdateStatus := cusModified;
4339 end
4340 else if PRecordData(Buff)^.
4341 rdCachedUpdateStatus = cusUninserted then
4342 begin
4343 PRecordData(Buff)^.rdCachedUpdateStatus := cusInserted;
4344 Dec(FDeletedRecords);
4345 end;
4346 end;
4347 if (not CachedUpdates) then
4348 InternalPostRecord(Qry, Buff)
4349 else begin
4350 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4351 FUpdatesPending := True;
4352 end;
4353 if bInserting then
4354 Inc(FRecordCount);
4355 finally
4356 FBase.RestoreCursor;
4357 end;
4358 end;
4359
4360 procedure TIBCustomDataSet.InternalRefresh;
4361 begin
4362 inherited InternalRefresh;
4363 InternalRefreshRow;
4364 end;
4365
4366 procedure TIBCustomDataSet.InternalSetToRecord(Buffer: PChar);
4367 begin
4368 InternalGotoBookmark(@(PRecordData(Buffer)^.rdRecordNumber));
4369 end;
4370
4371 function TIBCustomDataSet.IsCursorOpen: Boolean;
4372 begin
4373 result := FOpen;
4374 end;
4375
4376 procedure TIBCustomDataSet.Loaded;
4377 begin
4378 if assigned(FQSelect) then
4379 FBaseSQLSelect.assign(FQSelect.SQL);
4380 inherited Loaded;
4381 end;
4382
4383 procedure TIBCustomDataSet.Post;
4384 var CancelPost: boolean;
4385 begin
4386 CancelPost := false;
4387 if assigned(FOnValidatePost) then
4388 OnValidatePost(self,CancelPost);
4389 if CancelPost then
4390 Cancel
4391 else
4392 inherited Post;
4393 end;
4394
4395 function TIBCustomDataSet.Locate(const KeyFields: string; const KeyValues: Variant;
4396 Options: TLocateOptions): Boolean;
4397 var
4398 CurBookmark: TBookmark;
4399 begin
4400 DisableControls;
4401 try
4402 CurBookmark := Bookmark;
4403 First;
4404 result := InternalLocate(KeyFields, KeyValues, Options);
4405 if not result then
4406 Bookmark := CurBookmark;
4407 finally
4408 EnableControls;
4409 end;
4410 end;
4411
4412 function TIBCustomDataSet.Lookup(const KeyFields: string; const KeyValues: Variant;
4413 const ResultFields: string): Variant;
4414 var
4415 fl: TList;
4416 CurBookmark: TBookmark;
4417 begin
4418 DisableControls;
4419 fl := TList.Create;
4420 CurBookmark := Bookmark;
4421 try
4422 First;
4423 if InternalLocate(KeyFields, KeyValues, []) then
4424 begin
4425 if (ResultFields <> '') then
4426 result := FieldValues[ResultFields]
4427 else
4428 result := NULL;
4429 end
4430 else
4431 result := Null;
4432 finally
4433 Bookmark := CurBookmark;
4434 fl.Free;
4435 EnableControls;
4436 end;
4437 end;
4438
4439 procedure TIBCustomDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
4440 begin
4441 PRecordData(Buffer)^.rdRecordNumber := PInteger(Data)^;
4442 end;
4443
4444 procedure TIBCustomDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
4445 begin
4446 PRecordData(Buffer)^.rdBookmarkFlag := Value;
4447 end;
4448
4449 procedure TIBCustomDataSet.SetCachedUpdates(Value: Boolean);
4450 begin
4451 if not Value and FCachedUpdates then
4452 CancelUpdates;
4453 if (not (csReading in ComponentState)) and Value then
4454 CheckDatasetClosed;
4455 FCachedUpdates := Value;
4456 end;
4457
4458 procedure TIBCustomDataSet.SetDataSource(Value: TDataSource);
4459 begin
4460 if IsLinkedTo(Value) then
4461 IBError(ibxeCircularReference, [nil]);
4462 if FDataLink <> nil then
4463 FDataLink.DataSource := Value;
4464 end;
4465
4466 procedure TIBCustomDataSet.InternalSetFieldData(Field: TField; Buffer: Pointer);
4467 var
4468 Buff, TmpBuff: PChar;
4469 MappedFieldPos: integer;
4470 begin
4471 Buff := GetActiveBuf;
4472 if Field.FieldNo < 0 then
4473 begin
4474 TmpBuff := Buff + FRecordSize + Field.Offset;
4475 Boolean(TmpBuff[0]) := LongBool(Buffer);
4476 if Boolean(TmpBuff[0]) then
4477 Move(Buffer^, TmpBuff[1], Field.DataSize);
4478 WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
4479 end
4480 else begin
4481 CheckEditState;
4482 with PRecordData(Buff)^ do
4483 begin
4484 { If inserting, Adjust record position }
4485 AdjustRecordOnInsert(Buff);
4486 MappedFieldPos := FMappedFieldPosition[Field.FieldNo - 1];
4487 if (MappedFieldPos > 0) and
4488 (MappedFieldPos <= rdFieldCount) then
4489 with rdFields[MappedFieldPos], FFieldColumns^[MappedFieldPos] do
4490 begin
4491 Field.Validate(Buffer);
4492 if (Buffer = nil) or
4493 (Field is TIBStringField) and (PChar(Buffer)[0] = #0) then
4494 fdIsNull := True
4495 else
4496 begin
4497 Move(Buffer^, Buff[fdDataOfs],fdDataSize);
4498 if (fdDataType = SQL_TEXT) or (fdDataType = SQL_VARYING) then
4499 fdDataLength := StrLen(PChar(Buffer));
4500 fdIsNull := False;
4501 if rdUpdateStatus = usUnmodified then
4502 begin
4503 if CachedUpdates then
4504 begin
4505 FUpdatesPending := True;
4506 if State = dsInsert then
4507 rdCachedUpdateStatus := cusInserted
4508 else if State = dsEdit then
4509 rdCachedUpdateStatus := cusModified;
4510 end;
4511
4512 if State = dsInsert then
4513 rdUpdateStatus := usInserted
4514 else
4515 rdUpdateStatus := usModified;
4516 end;
4517 WriteRecordCache(rdRecordNumber, Buff);
4518 SetModified(True);
4519 end;
4520 end;
4521 end;
4522 end;
4523 if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
4524 DataEvent(deFieldChange, PtrInt(Field));
4525 end;
4526
4527 procedure TIBCustomDataSet.SetRecNo(Value: Integer);
4528 begin
4529 CheckBrowseMode;
4530 if (Value < 1) then
4531 Value := 1
4532 else if Value > FRecordCount then
4533 begin
4534 InternalLast;
4535 Value := Min(FRecordCount, Value);
4536 end;
4537 if (Value <> RecNo) then
4538 begin
4539 DoBeforeScroll;
4540 FCurrentRecord := Value - 1;
4541 Resync([]);
4542 DoAfterScroll;
4543 end;
4544 end;
4545
4546 procedure TIBCustomDataSet.Disconnect;
4547 begin
4548 Close;
4549 InternalUnPrepare;
4550 end;
4551
4552 procedure TIBCustomDataSet.SetUpdateMode(const Value: TUpdateMode);
4553 begin
4554 if not CanModify then
4555 IBError(ibxeCannotUpdate, [nil])
4556 else
4557 FUpdateMode := Value;
4558 end;
4559
4560
4561 procedure TIBCustomDataSet.SetUpdateObject(Value: TIBDataSetUpdateObject);
4562 begin
4563 if Value <> FUpdateObject then
4564 begin
4565 if Assigned(FUpdateObject) and (FUpdateObject.DataSet = Self) then
4566 FUpdateObject.DataSet := nil;
4567 FUpdateObject := Value;
4568 if Assigned(FUpdateObject) then
4569 begin
4570 if Assigned(FUpdateObject.DataSet) and
4571 (FUpdateObject.DataSet <> Self) then
4572 FUpdateObject.DataSet.UpdateObject := nil;
4573 FUpdateObject.DataSet := Self;
4574 end;
4575 end;
4576 end;
4577
4578 function TIBCustomDataSet.ConstraintsStored: Boolean;
4579 begin
4580 Result := Constraints.Count > 0;
4581 end;
4582
4583 procedure TIBCustomDataSet.ClearCalcFields(Buffer: PChar);
4584 begin
4585 FillChar(Buffer[FRecordSize], CalcFieldsSize, 0);
4586 end;
4587
4588 procedure TIBCustomDataSet.ClearIBLinks;
4589 var i: integer;
4590 begin
4591 for i := FIBLinks.Count - 1 downto 0 do
4592 TIBControlLink(FIBLinks[i]).IBDataSet := nil;
4593 end;
4594
4595
4596 procedure TIBCustomDataSet.InternalUnPrepare;
4597 begin
4598 if FInternalPrepared then
4599 begin
4600 CheckDatasetClosed;
4601 if FDidActivate then
4602 DeactivateTransaction;
4603 FieldDefs.Clear;
4604 FieldDefs.Updated := false;
4605 FInternalPrepared := False;
4606 Setlength(FAliasNameList,0);
4607 end;
4608 end;
4609
4610 procedure TIBCustomDataSet.InternalExecQuery;
4611 var
4612 DidActivate: Boolean;
4613 begin
4614 DidActivate := False;
4615 FBase.SetCursor;
4616 try
4617 ActivateConnection;
4618 DidActivate := ActivateTransaction;
4619 if FQSelect.SQL.Text = '' then
4620 IBError(ibxeEmptyQuery, [nil]);
4621 if not FInternalPrepared then
4622 InternalPrepare;
4623 if FQSelect.SQLStatementType = SQLSelect then
4624 begin
4625 IBError(ibxeIsASelectStatement, [nil]);
4626 end
4627 else
4628 FQSelect.ExecQuery;
4629 finally
4630 if DidActivate then
4631 DeactivateTransaction;
4632 FBase.RestoreCursor;
4633 end;
4634 end;
4635
4636 function TIBCustomDataSet.GetSelectStmtIntf: IStatement;
4637 begin
4638 Result := FQSelect.Statement;
4639 end;
4640
4641 function TIBCustomDataSet.GetParser: TSelectSQLParser;
4642 begin
4643 if not assigned(FParser) then
4644 FParser := CreateParser;
4645 Result := FParser
4646 end;
4647
4648 procedure TIBCustomDataSet.ResetParser;
4649 begin
4650 if assigned(FParser) then
4651 begin
4652 FParser.Free;
4653 FParser := nil;
4654 FQSelect.OnSQLChanged := nil; {Do not react to change}
4655 try
4656 FQSelect.SQL.Assign(FBaseSQLSelect);
4657 finally
4658 FQSelect.OnSQLChanged := SQLChanged;
4659 end;
4660 end;
4661 end;
4662
4663 function TIBCustomDataSet.HasParser: boolean;
4664 begin
4665 Result := not (csDesigning in ComponentState) and (FParser <> nil)
4666 end;
4667
4668 procedure TIBCustomDataSet.SetGenerateParamNames(AValue: Boolean);
4669 begin
4670 if FGenerateParamNames = AValue then Exit;
4671 FGenerateParamNames := AValue;
4672 Disconnect
4673 end;
4674
4675 procedure TIBCustomDataSet.InitRecord(Buffer: PChar);
4676 begin
4677 inherited InitRecord(Buffer);
4678 with PRecordData(Buffer)^ do
4679 begin
4680 rdUpdateStatus := TUpdateStatus(usInserted);
4681 rdBookMarkFlag := bfInserted;
4682 rdRecordNumber := -1;
4683 end;
4684 end;
4685
4686 procedure TIBCustomDataSet.InternalInsert;
4687 begin
4688 CursorPosChanged;
4689 end;
4690
4691 { TIBDataSet IProviderSupport }
4692
4693 procedure TIBCustomDataSet.PSEndTransaction(Commit: Boolean);
4694 begin
4695 if Commit then
4696 Transaction.Commit else
4697 Transaction.Rollback;
4698 end;
4699
4700 function TIBCustomDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams;
4701 ResultSet: Pointer = nil): Integer;
4702 var
4703 FQuery: TIBQuery;
4704 begin
4705 if Assigned(ResultSet) then
4706 begin
4707 TDataSet(ResultSet^) := TIBQuery.Create(nil);
4708 with TIBQuery(ResultSet^) do
4709 begin
4710 SQL.Text := ASQL;
4711 Params.Assign(AParams);
4712 Open;
4713 Result := RowsAffected;
4714 end;
4715 end
4716 else
4717 begin
4718 FQuery := TIBQuery.Create(nil);
4719 try
4720 FQuery.Database := Database;
4721 FQuery.Transaction := Transaction;
4722 FQuery.GenerateParamNames := True;
4723 FQuery.SQL.Text := ASQL;
4724 FQuery.Params.Assign(AParams);
4725 FQuery.ExecSQL;
4726 Result := FQuery.RowsAffected;
4727 finally
4728 FQuery.Free;
4729 end;
4730 end;
4731 end;
4732
4733 function TIBCustomDataSet.PSGetQuoteChar: string;
4734 begin
4735 if Database.SQLDialect = 3 then
4736 Result := '"' else
4737 Result := '';
4738 end;
4739
4740 function TIBCustomDataSet.PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError;
4741 var
4742 PrevErr: Integer;
4743 begin
4744 if Prev <> nil then
4745 PrevErr := Prev.ErrorCode else
4746 PrevErr := 0;
4747 if E is EIBError then
4748 with EIBError(E) do
4749 Result := EUpdateError.Create(E.Message, '', SQLCode, PrevErr, E) else
4750 Result := inherited PSGetUpdateException(E, Prev);
4751 end;
4752
4753 function TIBCustomDataSet.PSInTransaction: Boolean;
4754 begin
4755 Result := Transaction.InTransaction;
4756 end;
4757
4758 function TIBCustomDataSet.PSIsSQLBased: Boolean;
4759 begin
4760 Result := True;
4761 end;
4762
4763 function TIBCustomDataSet.PSIsSQLSupported: Boolean;
4764 begin
4765 Result := True;
4766 end;
4767
4768 procedure TIBCustomDataSet.PSReset;
4769 begin
4770 inherited PSReset;
4771 if Active then
4772 begin
4773 Close;
4774 Open;
4775 end;
4776 end;
4777
4778 function TIBCustomDataSet.PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean;
4779 var
4780 UpdateAction: TIBUpdateAction;
4781 SQL: string;
4782 Params: TParams;
4783
4784 procedure AssignParams(DataSet: TDataSet; Params: TParams);
4785 var
4786 I: Integer;
4787 Old: Boolean;
4788 Param: TParam;
4789 PName: string;
4790 Field: TField;
4791 Value: Variant;
4792 begin
4793 for I := 0 to Params.Count - 1 do
4794 begin
4795 Param := Params[I];
4796 PName := Param.Name;
4797 Old := CompareText(Copy(PName, 1, 4), 'OLD_') = 0; {do not localize}
4798 if Old then System.Delete(PName, 1, 4);
4799 Field := DataSet.FindField(PName);
4800 if not Assigned(Field) then Continue;
4801 if Old then Param.AssignFieldValue(Field, Field.OldValue) else
4802 begin
4803 Value := Field.NewValue;
4804 if VarIsEmpty(Value) then Value := Field.OldValue;
4805 Param.AssignFieldValue(Field, Value);
4806 end;
4807 end;
4808 end;
4809
4810 begin
4811 Result := False;
4812 if Assigned(OnUpdateRecord) then
4813 begin
4814 UpdateAction := uaFail;
4815 if Assigned(FOnUpdateRecord) then
4816 begin
4817 FOnUpdateRecord(Delta, UpdateKind, UpdateAction);
4818 Result := UpdateAction = uaApplied;
4819 end;
4820 end
4821 else if Assigned(FUpdateObject) then
4822 begin
4823 SQL := FUpdateObject.GetSQL(UpdateKind).Text;
4824 if SQL <> '' then
4825 begin
4826 Params := TParams.Create;
4827 try
4828 Params.ParseSQL(SQL, True);
4829 AssignParams(Delta, Params);
4830 if PSExecuteStatement(SQL, Params) = 0 then
4831 IBError(ibxeNoRecordsAffected, [nil]);
4832 Result := True;
4833 finally
4834 Params.Free;
4835 end;
4836 end;
4837 end;
4838 end;
4839
4840 procedure TIBCustomDataSet.PSStartTransaction;
4841 begin
4842 ActivateConnection;
4843 Transaction.StartTransaction;
4844 end;
4845
4846 function TIBCustomDataSet.PsGetTableName: string;
4847 begin
4848 // if not FInternalPrepared then
4849 // InternalPrepare;
4850 { It is possible for the FQSelectSQL to be unprepared
4851 with FInternalPreprepared being true (see DoBeforeTransactionEnd).
4852 So check the Prepared of the SelectSQL instead }
4853 if not FQSelect.Prepared then
4854 FQSelect.Prepare;
4855 Result := FQSelect.UniqueRelationName;
4856 end;
4857
4858 procedure TIBDataSet.BatchInput(InputObject: TIBBatchInput);
4859 begin
4860 InternalBatchInput(InputObject);
4861 end;
4862
4863 procedure TIBDataSet.BatchOutput(OutputObject: TIBBatchOutput);
4864 begin
4865 InternalBatchOutput(OutputObject);
4866 end;
4867
4868 procedure TIBDataSet.ExecSQL;
4869 begin
4870 InternalExecQuery;
4871 end;
4872
4873 procedure TIBDataSet.Prepare;
4874 begin
4875 InternalPrepare;
4876 end;
4877
4878 procedure TIBDataSet.UnPrepare;
4879 begin
4880 InternalUnPrepare;
4881 end;
4882
4883 function TIBDataSet.GetPrepared: Boolean;
4884 begin
4885 Result := InternalPrepared;
4886 end;
4887
4888 procedure TIBDataSet.InternalOpen;
4889 begin
4890 ActivateConnection;
4891 ActivateTransaction;
4892 InternalSetParamsFromCursor;
4893 Inherited InternalOpen;
4894 end;
4895
4896 procedure TIBDataSet.SetFiltered(Value: Boolean);
4897 begin
4898 if(Filtered <> Value) then
4899 begin
4900 inherited SetFiltered(value);
4901 if Active then
4902 begin
4903 Close;
4904 Open;
4905 end;
4906 end
4907 else
4908 inherited SetFiltered(value);
4909 end;
4910
4911 function TIBCustomDataSet.BookmarkValid(Bookmark: TBookmark): Boolean;
4912 begin
4913 Result := false;
4914 if not Assigned(Bookmark) then
4915 exit;
4916 Result := PInteger(Bookmark)^ < FRecordCount;
4917 end;
4918
4919 function TIBCustomDataSet.GetFieldData(Field: TField;
4920 Buffer: Pointer): Boolean;
4921 {$IFDEF TBCDFIELD_IS_BCD}
4922 var
4923 lTempCurr : System.Currency;
4924 begin
4925 if (Field.DataType = ftBCD) and (Buffer <> nil) then
4926 begin
4927 Result := InternalGetFieldData(Field, @lTempCurr);
4928 if Result then
4929 CurrToBCD(lTempCurr, TBCD(Buffer^), 32, Field.Size);
4930 end
4931 else
4932 {$ELSE}
4933 begin
4934 {$ENDIF}
4935 Result := InternalGetFieldData(Field, Buffer);
4936 end;
4937
4938 function TIBCustomDataSet.GetFieldData(Field: TField; Buffer: Pointer;
4939 NativeFormat: Boolean): Boolean;
4940 begin
4941 if (Field.DataType = ftBCD) and not NativeFormat then
4942 Result := InternalGetFieldData(Field, Buffer)
4943 else
4944 Result := inherited GetFieldData(Field, Buffer, NativeFormat);
4945 end;
4946
4947 procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer);
4948 {$IFDEF TDBDFIELD_IS_BCD}
4949 var
4950 lTempCurr : System.Currency;
4951 begin
4952 if (Field.DataType = ftBCD) and (Buffer <> nil) then
4953 begin
4954 BCDToCurr(TBCD(Buffer^), lTempCurr);
4955 InternalSetFieldData(Field, @lTempCurr);
4956 end
4957 else
4958 {$ELSE}
4959 begin
4960 {$ENDIF}
4961 InternalSetFieldData(Field, Buffer);
4962 end;
4963
4964 procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer;
4965 NativeFormat: Boolean);
4966 begin
4967 if (not NativeFormat) and (Field.DataType = ftBCD) then
4968 InternalSetfieldData(Field, Buffer)
4969 else
4970 inherited SetFieldData(Field, buffer, NativeFormat);
4971 end;
4972
4973 { TIBDataSetUpdateObject }
4974
4975 constructor TIBDataSetUpdateObject.Create(AOwner: TComponent);
4976 begin
4977 inherited Create(AOwner);
4978 FRefreshSQL := TStringList.Create;
4979 end;
4980
4981 destructor TIBDataSetUpdateObject.Destroy;
4982 begin
4983 FRefreshSQL.Free;
4984 inherited Destroy;
4985 end;
4986
4987 procedure TIBDataSetUpdateObject.SetRefreshSQL(value: TStrings);
4988 begin
4989 FRefreshSQL.Assign(Value);
4990 end;
4991
4992 procedure TIBDataSetUpdateObject.InternalSetParams(Params: ISQLParams;
4993 buff: PChar);
4994 begin
4995 if not Assigned(DataSet) then Exit;
4996 DataSet.SetInternalSQLParams(Params, buff);
4997 end;
4998
4999 procedure TIBDataSetUpdateObject.InternalSetParams(Query: TIBSQL; buff: PChar);
5000 begin
5001 InternalSetParams(Query.Params,buff);
5002 end;
5003
5004 procedure TIBDataSetUpdateObject.UpdateRecordFromQuery(QryResults: IResults;
5005 Buffer: PChar);
5006 begin
5007 if not Assigned(DataSet) then Exit;
5008 DataSet.UpdateRecordFromQuery(QryResults, Buffer);
5009 end;
5010
5011 function TIBDSBlobStream.GetSize: Int64;
5012 begin
5013 Result := FBlobStream.BlobSize;
5014 end;
5015
5016 { TIBDSBlobStream }
5017 constructor TIBDSBlobStream.Create(AField: TField; ABlobStream: TIBBlobStream;
5018 Mode: TBlobStreamMode);
5019 begin
5020 FField := AField;
5021 FBlobStream := ABlobStream;
5022 FBlobStream.Seek(0, soFromBeginning);
5023 if (Mode = bmWrite) then
5024 begin
5025 FBlobStream.Truncate;
5026 TIBCustomDataSet(FField.DataSet).RecordModified(True);
5027 TBlobField(FField).Modified := true;
5028 FHasWritten := true;
5029 end;
5030 end;
5031
5032 destructor TIBDSBlobStream.Destroy;
5033 begin
5034 if FHasWritten then
5035 TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
5036 inherited Destroy;
5037 end;
5038
5039 function TIBDSBlobStream.Read(var Buffer; Count: Longint): Longint;
5040 begin
5041 result := FBlobStream.Read(Buffer, Count);
5042 end;
5043
5044 function TIBDSBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
5045 begin
5046 result := FBlobStream.Seek(Offset, Origin);
5047 end;
5048
5049 procedure TIBDSBlobStream.SetSize(NewSize: Longint);
5050 begin
5051 FBlobStream.SetSize(NewSize);
5052 end;
5053
5054 function TIBDSBlobStream.Write(const Buffer; Count: Longint): Longint;
5055 begin
5056 if not (FField.DataSet.State in [dsEdit, dsInsert]) then
5057 IBError(ibxeNotEditing, [nil]);
5058 TIBCustomDataSet(FField.DataSet).RecordModified(True);
5059 TBlobField(FField).Modified := true;
5060 result := FBlobStream.Write(Buffer, Count);
5061 FHasWritten := true;
5062 { TIBCustomDataSet(FField.DataSet).DataEvent(deFieldChange, PtrInt(FField));
5063 Removed as this caused a seek to beginning of the blob stream thus corrupting
5064 the blob stream. Moved to the destructor i.e. called after blob written}
5065 end;
5066
5067 { TIBGenerator }
5068
5069 procedure TIBGenerator.SetIncrement(const AValue: integer);
5070 begin
5071 if FIncrement = AValue then Exit;
5072 if AValue < 0 then
5073 IBError(ibxeNegativeGenerator,[]);
5074 FIncrement := AValue;
5075 SetQuerySQL;
5076 end;
5077
5078 procedure TIBGenerator.SetTransaction(AValue: TIBTransaction);
5079 begin
5080 FQuery.Transaction := AValue;
5081 end;
5082
5083 procedure TIBGenerator.SetQuerySQL;
5084 begin
5085 FQuery.SQL.Text := Format('Select Gen_ID(%s,%d) From RDB$Database',[FGeneratorName,Increment]);
5086 end;
5087
5088 function TIBGenerator.GetDatabase: TIBDatabase;
5089 begin
5090 Result := FQuery.Database;
5091 end;
5092
5093 function TIBGenerator.GetTransaction: TIBTransaction;
5094 begin
5095 Result := FQuery.Transaction;
5096 end;
5097
5098 procedure TIBGenerator.SetDatabase(AValue: TIBDatabase);
5099 begin
5100 FQuery.Database := AValue;
5101 end;
5102
5103 procedure TIBGenerator.SetGeneratorName(AValue: string);
5104 begin
5105 if FGeneratorName = AValue then Exit;
5106 FGeneratorName := AValue;
5107 SetQuerySQL;
5108 end;
5109
5110 function TIBGenerator.GetNextValue: integer;
5111 begin
5112 with FQuery do
5113 begin
5114 Transaction.Active := true;
5115 ExecQuery;
5116 try
5117 Result := Fields[0].AsInteger
5118 finally
5119 Close
5120 end;
5121 end;
5122 end;
5123
5124 constructor TIBGenerator.Create(Owner: TIBCustomDataSet);
5125 begin
5126 FOwner := Owner;
5127 FIncrement := 1;
5128 FQuery := TIBSQL.Create(nil);
5129 end;
5130
5131 destructor TIBGenerator.Destroy;
5132 begin
5133 if assigned(FQuery) then FQuery.Free;
5134 inherited Destroy;
5135 end;
5136
5137
5138 procedure TIBGenerator.Apply;
5139 begin
5140 if assigned(Database) and assigned(Transaction) and
5141 (FGeneratorName <> '') and (FFieldName <> '') and Owner.FieldByName(FFieldName).IsNull then
5142 Owner.FieldByName(FFieldName).AsInteger := GetNextValue;
5143 end;
5144
5145
5146 end.