ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
Revision: 43
Committed: Thu Sep 22 17:10:15 2016 UTC (7 years, 7 months ago) by tony
Content type: text/x-pascal
File size: 134829 byte(s)
Log Message:
Committing updates for Release R1-4-3

File Contents

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