ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBBufferedCursors.pas
Revision: 427
Committed: Tue Nov 28 17:00:03 2023 UTC (11 months, 2 weeks ago) by tony
Content type: text/x-pascal
File size: 111699 byte(s)
Log Message:
TIBCustomDataset.Requery: The display buffer pool is now also reset avoiding
   issues with left over pointers to invalid internal buffers.

File Contents

# Content
1 (*
2 * IBX For Lazarus (Firebird Express)
3 *
4 * The contents of this file are subject to the Initial Developer's
5 * Public License Version 1.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy
7 * of the License here:
8 *
9 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
10 *
11 * Software distributed under the License is distributed on an "AS
12 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13 * implied. See the License for the specific language governing rights
14 * and limitations under the License.
15 *
16 * The Initial Developer of the Original Code is Tony Whyman.
17 *
18 * The Original Code is (C) 2023 Tony Whyman, MWA Software
19 * (http://www.mwasoftware.co.uk).
20 *
21 * All Rights Reserved.
22 *
23 * Contributor(s): ______________________________________.
24 *
25 *)
26 unit IBBufferedCursors;
27
28 {$mode Delphi}
29 {$codepage UTF8}
30
31 { $define PrintBuf}
32
33 interface
34
35 uses
36 Classes, SysUtils, DB, IB, IBBlob, IBExternals, FmtBCD;
37
38 type
39 { The TIBSimpleBufferPool provides basic buffer management for IBX. The pool consists
40 of one or more memory blocks organised as a bi-directional linked list. The blocks do
41 not have to be the same size and the first is typically a small block with
42 subsequent blocks larger in size. This allows for efficient memory allocation
43 for small datasets whilst efficiently extending the pool for larger datasets.
44
45 Each block comprises a "start header", one or more fixed size "buffers" and a trailing end
46 header. Each type is distinguished by the first byte value. Additionally, the
47 first byte of the first buffer in a block is separately identified. This
48 allows for a block to be parsed in either direction with the end header terminating
49 forward parsing while the first buffer identifier terminates parsing in the
50 backwards direction.
51
52 The end header consists of no more than a pointer back to the start header, while
53 the start header includes the previous and next block pointers, the number of buffers
54 in the block and the number of buffers in use.
55
56 Each buffer is allocated a sequential record number starting from one. A simple
57 TList based index allows for random access to the block containing a buffer
58 identifies by record number with the requested buffer then located as an
59 offset from the start header.
60 }
61
62 PIBRecordNumber = ^TIBRecordNumber;
63
64 TIBRecordNumber = cardinal;
65
66 { TIBSimpleBufferPool }
67
68 TIBSimpleBufferPool = class
69 private type
70 THeaderTypes = (htEmptySlot=0, htStart, htEnd, htFirstBuffer, htBuffer);
71 PStartHeader = ^TStartHeader;
72 TStartHeader = record
73 HeaderType: THeaderTypes;
74 PreviousBlock: PByte;
75 NextBlock: PByte;
76 MaxBuffers: integer;
77 BuffersInUse: integer;
78 FirstRecNo: TIBRecordNumber; {1- based}
79 end;
80
81 PEndHeader = ^TEndHeader;
82 TEndHeader = record
83 HeaderType: THeaderTypes;
84 StartHeader: PByte;
85 end;
86
87 PBufferHeader = ^TBufferHeader;
88 TBufferHeader = record
89 HeaderType: THeaderTypes;
90 RecNo: TIBRecordNumber;
91 end;
92
93 strict private
94 FFirstBlock: PByte;
95 FLastBlock: PByte;
96 FBufferSize: integer; {user buffer size i.e. not including header}
97 FBuffersPerBlock: integer;
98 FFirstBlockBuffers: integer;
99 FBufferIndex: TList;
100 FLastBuffer: PByte;
101 FCurrent: PByte;
102 FName: string;
103 FRecordCount: TIBRecordNumber;
104 function AllocBlock(buffers: integer): PByte;
105 procedure CheckBuffersAvailable;
106 procedure InternalCheckValidBuffer(P:PByte); inline;
107 protected
108 procedure CheckValidBuffer(P:PByte); virtual;
109 function AddBuffer: PByte; virtual;
110 procedure Clear; virtual;
111 function GetFirst: PByte; virtual;
112 function GetLast: PByte; virtual;
113 function GetBuffer(RecNo: TIBRecordNumber): PByte; virtual;
114 function GetNextBuffer(aBuffer: PByte): PByte; virtual;
115 function GetPriorBuffer(aBuffer: PByte): PByte; virtual;
116 public
117 constructor Create(aName: string; bufSize, aBuffersPerBlock, firstBlockBuffers: integer);
118 destructor Destroy; override;
119 function Append: PByte;
120 function GetRecNo(aBuffer: PByte): TIBRecordNumber; virtual;
121 function GetRecordCount: TIBRecordNumber;
122 function Empty: boolean;
123 property BuffersPerBlock: integer read FBuffersPerBlock write FBuffersPerBlock;
124 property Name: string read FName;
125 property RecordCount: TIBRecordNumber read FRecordCount;
126 property BufferSize: integer read FBufferSize;
127 end;
128
129 PIBDBKey = ^TIBDBKey;
130 TIBDBKey = record
131 DBKey: array[0..7] of Byte;
132 end;
133
134
135 {
136 The TIBBufferPool builds on TIBSimpleBufferPool and adds the ability to
137 insert buffers before and after a given buffer and to mark a buffer as
138 deleted.
139
140 In order to avoid large memory to memory copies, a previousBuffer pointer
141 is added to each buffer header. InsertBefore is thus simply achieved by
142 adding a buffer to the pool and inserting it into the backwards chain.
143 GetPriorBuffer is then amended to follow the previous pointer.
144
145 InsertAfter is implemented similarly. However, those appended to the pool
146 have to be identified as appended instead of simply inserted. This is because
147 GetNextBuffer is implemented using the inherited method while skipping
148 inserted and deleted buffers. That is the inherited method is called repeatedly
149 until an appended buffer is returned or EOF is reached.
150
151 Deletion is implemetned by marking a buffer as deleted and adjusting the previous
152 pointer of the next buffer in sequence
153 }
154 { TIBBufferPool }
155
156 TIBBufferPool = class(TIBSimpleBufferPool)
157 private type
158 TRecordStatus = (rsAppended,rsInserted,rsInsertDeleted,rsAppendDeleted);
159 PRecordData = ^TRecordData;
160 TRecordData = record
161 rdStatus: TRecordStatus;
162 rdPreviousBuffer: PByte;
163 end;
164 strict private
165 FFirstRecord: PByte;
166 FLastRecord: PByte;
167 FInsertedRecords: integer;
168 FDeletedRecords: integer;
169 function InternalGetNextBuffer(aBuffer: PByte; IncludeDeleted: boolean): PByte;
170 public
171 constructor Create(aName: string; bufSize, aBuffersPerBlock, firstBlockBuffers: integer);
172 procedure Clear; override;
173 function GetFirst: PByte; override;
174 function GetLast: PByte; override;
175 function GetBuffer(RecNo: TIBRecordNumber): PByte; override;
176 function GetNextBuffer(aBuffer: PByte): PByte; override; overload;
177 function GetNextBuffer(aBuffer: PByte; IncludeDeleted: boolean): PByte; overload;
178 function GetPriorBuffer(aBuffer: PByte): PByte; override;
179 function GetRecNo(aBuffer: PByte): TIBRecordNumber; override;
180 function InsertBefore(aBuffer: PByte): PByte; virtual;
181 function InsertAfter(aBuffer: PByte): PByte; virtual;
182 function LocatePreviousBuffer(aBuffer: PByte): PByte;
183 function Append: PByte;
184 function Delete(aBuffer: PByte): PByte;
185 procedure UnDelete(aBuffer: PByte);
186 function GetUpdateStatus(aBuffer: PByte): TUpdateStatus;
187 function GetRecordStatus(aBuffer: PByte): TRecordStatus;
188 property InsertedRecords: integer read FInsertedRecords;
189 property DeletedRecords: integer read FDeletedRecords;
190 {$ifdef PrintBuf}
191 public
192 procedure PrintBufferList;
193 {$endif}
194 end;
195
196 {
197 TIIBOldBufferPool is used to support cached updates. When a record is updated
198 a new buffer is allocated from this pool. The caller may then copy the
199 record data into this buffer. The buffer header includes the cached update
200 status and a pointer back to the buffer containing the updated data.
201
202 Insertions and Deletions do not need a data copy. This only applies to updated
203 records.
204
205 Forward and backwards iterators are provided. The Forward iterator may be used
206 to apply updates while the backwards iterator may be used to cancel updates. In
207 each case the update status is used to determine the actual and the pointer to
208 the data buffer is used to access the updated data.
209 }
210
211 TCachedUpdateStatus = (
212 cusUnmodified=0, cusModified, cusInserted,
213 cusDeleted, cusUninserted
214 );
215
216 TIBUpdateRecordTypes = set of TCachedUpdateStatus;
217
218 { TIBOldBufferPool }
219
220 TIBOldBufferPool = class(TIBSimpleBufferPool)
221 private type
222 PRecordData = ^TRecordData;
223 TRecordData = record
224 rdStatus: TCachedUpdateStatus;
225 rdRecordNumber: TIBRecordNumber;
226 rdDataBuffer: PByte;
227 end;
228
229 private
230 FModifiedRecords: integer;
231
232 protected
233 procedure CheckValidBuffer(P:PByte); override;
234
235 public type
236 TIterator = procedure(status: TCachedUpdateStatus; DataBuffer, OldBuffer: PByte) of object;
237
238 public
239 constructor Create(aName: string; bufSize, aBuffersPerBlock, firstBlockBuffers: integer);
240 function Append(RecNo: TIBRecordNumber; DataBuffer: PByte): PByte;
241 procedure Clear; override;
242 function FindOldBufferFor(RecNo: TIBRecordNumber): PByte;
243 function GetBuffer(RecNo: TIBRecordNumber): PByte; override;
244 function GetRecNo(aBuffer: PByte): TIBRecordNumber; override;
245 function GetStatus(aBuffer: PByte): TCachedUpdateStatus; overload;
246 function GetStatus(RecNo: TIBRecordNumber): TCachedUpdateStatus; overload;
247 procedure SetStatus(aBuffer: PByte; status: TCachedUpdateStatus);
248 procedure SetDataBuffer(aBuffer: PByte; aDataBuffer: PByte);
249 procedure ForwardIterator(iterator: TIterator);
250 procedure BackwardsIterator(iterator: TIterator);
251 property ModifiedRecords: integer read FModifiedRecords;
252 end;
253
254 PIBBufferedDateTimeWithTimeZone = ^TIBBufferedDateTimeWithTimeZone;
255 TIBBufferedDateTimeWithTimeZone = packed record
256 Timestamp: TDateTime;
257 dstOffset: smallint;
258 TimeZoneID: ISC_USHORT;
259 end;
260
261 { TIBDSBlobStream }
262
263 TIBDSBlobStream = class(TStream)
264 private
265 FHasWritten: boolean;
266 procedure FieldChanged;
267 protected
268 FField: TField;
269 FBlobStream: TIBBlobStream;
270 function GetSize: Int64; override;
271 public
272 constructor Create(AField: TField; ABlobStream: TIBBlobStream;
273 Mode: TBlobStreamMode);
274 destructor Destroy; override;
275 function Read(var Buffer; Count: Longint): Longint; override;
276 function Seek(Offset: Longint; Origin: Word): Longint; override;
277 procedure SetSize(NewSize: Longint); override;
278 function Write(const Buffer; Count: Longint): Longint; override;
279 property BlobStream: TIBBlobStream read FBlobStream;
280 end;
281
282 TUpdatesIterator = procedure(status: TCachedUpdateStatus; aBufID: TRecordBuffer;
283 var RecordSkipped: boolean) of object;
284
285 TOnValuesReturned = procedure(qryResults: IResults) of object;
286
287 TRegisteredQueryTypes = (rqInsert,rqModify,rqDelete,rqRefresh);
288
289 { IIBCursor }
290
291 IIBCursor = interface
292 ['{909621f7-e7fe-4b39-a8c5-f25c40a71c12}']
293 function AllocRecordBuffer: TRecordBuffer;
294 procedure FreeRecordBuffer(var Buffer: TRecordBuffer);
295 procedure ClearRecordBuffer(Buffer: TRecordBuffer);
296 function CreateBlobStream(aBufID: TRecordBuffer; Field: TField; Mode: TBlobStreamMode): TStream;
297 function GetArray(aBufID: TRecordBuffer; Field: TField): IArray;
298 procedure SetArrayIntf(aBufID: TRecordBuffer; AnArray: IArray; Field: TField);
299 function GetRecDBkey(aBufID: TRecordBuffer): TIBDBKey;
300 function GetFieldData(aBufID: TRecordBuffer; field: TField; outBuffer: PByte): boolean;
301 procedure SetFieldData(aBufID: TRecordBuffer; field: TField; inBuffer: PByte);
302 procedure SetSQLParams(aBufID: TRecordBuffer; params: ISQLParams);
303 procedure UpdateRecordFromQuery(aBufID: TRecordBuffer; QryResults: IResults);
304 function NeedRefresh(aBufID: TRecordBuffer): boolean;
305 function GetBookmarkFlag(aBufID: TRecordBuffer): TBookmarkFlag;
306 procedure SetBookmarkFlag(aBufID: TRecordBuffer; aBookmarkFlag: TBookmarkFlag);
307 procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
308 procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
309 function GetBookmarkSize: integer;
310 function GetRecord(aBufID: TRecordBuffer; GetMode: TGetMode;
311 DoCheck: Boolean): TGetResult;
312 procedure GotoFirst;
313 procedure GotoLast;
314 function GotoRecordNumber(RecNo: TIBRecordNumber): boolean;
315 function GetRecNo(aBufID: TRecordBuffer): TIBRecordNumber;
316 function GetCurrentRecNo: TIBRecordNumber;
317 function GetRecordCount: TIBRecordNumber;
318 function GetRecordSize: word;
319 procedure SetCurrentRecord(aBufID: TRecordBuffer);
320 procedure EditBuffer(aBufID: TRecordBuffer);
321 procedure CancelChanges(aBufID: TRecordBuffer);
322 procedure EditingDone(aBufID: TRecordBuffer; UpdateStatus: TCachedUpdateStatus);
323 procedure InsertBefore(aBufID: TRecordBuffer);
324 procedure Append(aBufID: TRecordBuffer);
325 procedure Delete(aBufID: TRecordBuffer);
326 procedure UnDelete(aBufID: TRecordBuffer);
327 function GetCachedUpdateStatus(aBufID: TRecordBuffer): TCachedUpdateStatus;
328 function GetUpdateStatus(aBufID: TRecordBuffer): TUpdateStatus;
329
330 function GetInsertedRecords: integer;
331 function GetDeletedRecords: integer;
332 procedure ApplyUpdates(iterator: TUpdatesIterator);
333 procedure CancelUpdates;
334 function UpdatesPending: boolean;
335 function GetCachedUpdatesEnabled:boolean;
336 procedure SwapDataBuffer(buf1, buf2: TRecordBuffer);
337 function GetAliasName(FieldNo: integer): AnsiString;
338 procedure InitRecord(aBufID: TRecordBuffer);
339 function AtBOF: boolean;
340 function AtEOF: boolean;
341 procedure ClearCalcFields(aBufID: TRecordBuffer);
342 procedure SetCursor(aCursor: IResultSet);
343 procedure RegisterQuery(qryType : TRegisteredQueryTypes; qry : IStatement;
344 OnValuesReturnedProc : TOnValuesReturned);
345 procedure ExecRegisteredQuery(qryType : TRegisteredQueryTypes; aBufID: TRecordBuffer;
346 var SelectCount, InsertCount, UpdateCount, DeleteCount: integer);
347
348 function HasRegisteredQuery(qryType : TRegisteredQueryTypes): boolean;
349 end;
350
351 {
352 TIBSelectCursor provides common functions for uni-directional, bi-directional
353 and bi-directional with cached updates cursors.
354
355 The IB Cursor classes support a common interface that is used to satisfy the
356 TDataset abstract methods used in buffer management including AllocRecordBuffer
357 and GetRecord.
358
359 The opaque pointer to a buffer returned to TDataset is a pointer to an internal
360 data structure including a pointer to the actual buffer and a pointer to a separate
361 buffer for cached updates. This approach is used to avoid in memory copies every
362 time the dataset is scolled. It also avoids the cached rows buffers having to
363 include space for the calculated fields.
364 }
365
366 TIBSelectCursor = class(TInterfacedObject)
367 private type
368 { TIBArray }
369
370 TIBArray = class {Wrapper class to support array cache and event handling}
371 private
372 FArray: IArray;
373 FRecNo: integer;
374 FField: TField;
375 procedure EventHandler(Sender: IArray; Reason: TArrayEventReason);
376 public
377 constructor Create(aField: TField; anArray: IArray);
378 destructor Destroy; override;
379 property ArrayIntf: IArray read FArray;
380 end;
381
382 PIBArray = ^TIBArray;
383 PIBBlobStream = ^TIBBlobStream;
384
385 TColumnMetadata = record
386 fdSQLColIndex: Integer; {Corresponding element index in ISQLData}
387 fdDataType: Short;
388 fdDataScale: Short;
389 fdNullable: Boolean;
390 fdDataSize: Short;
391 fdDataOfs: Integer;
392 fdCodePage: TSystemCodePage;
393 fdRefreshOnInsert: boolean;
394 fdRefreshOnUpdate: boolean;
395 fdObjOffset: Integer; {used for Blob and Array columns}
396 fdAliasName: AnsiString;
397 end;
398
399 PDisplayBuffer = ^TDisplaybuffer;
400 TDisplayBuffer = record
401 dbBookmarkFlag: TBookmarkFlag;
402 dbBookmarkData: array [1..sizeof(TIBRecordNumber)] of Byte;
403 dbBuffer: PByte;
404 dbCalcFields: PByte;
405 end;
406
407 PRecordHeader = ^TRecordHeader;
408 TRecordHeader = record
409 rhUpdateStatus: TUpdateStatus;
410 end;
411
412 TColumnMetadataArray = array of TColumnMetadata;
413
414 TRegisteredQuery = record
415 stmt: IStatement;
416 ParamMap: array of integer;
417 UseOldValue: array of boolean;
418 ColMap: array of integer; {return values}
419 OnValuesReturned: TOnValuesReturned;
420 end;
421
422 strict private
423 FRecordBufferSize: Integer; {Calculated size in bytes for each record buffer}
424 FRecordCount: Integer; {No. of records held in buffer pool. Total in dataset with Cursor.IsEof}
425 FColumnMetaData: TColumnMetadataArray; {Metadata extracted from cursor + per column info.
426 Note: only includes columns required i.e. there is
427 a corresponding field for the column or is the DBKey}
428 FColumnCount: integer; {size of metadata array}
429 FCalcFieldsSize: integer; {size in bytes of calculated fields buffer}
430 FBlobFieldCount: Longint; {Number of blob fields in each record}
431 FBlobStreamList: TList; {Keeps track of TIBBlobStream objects created}
432 FArrayList: TList; {Keeps track of TIBArry objects created}
433 FArrayFieldCount: integer; {Number of array fields in each record}
434 FDBKeyFieldColumn: integer; {FColumnMetadata index of DBKey with alias sDBKeyAIias}
435 FFieldNo2ColumnMap: array of integer; {TField.FieldNo to FColumnMetadata index map}
436 FNullColBitmapOffset: integer; {start of NullColumn Bitmap in each record buffer}
437 FRefreshRequiredBitmapOffset: integer; {start of Refresh Requried Bitmap in each record buffer}
438 FRefreshRequiredSize: integer; {number of bytes in RefreshRequired bitmap}
439 FCursor: IResultSet; {The Cursor}
440 FDefaultTZDate: TDateTime; {Default Time Zone time}
441 FName: string; {Local cursor name - set by creator}
442 FRegisteredQueries: array[TRegisteredQueryTypes] of TRegisteredQuery; {cached query info}
443 FDataset: TDataSet;
444
445 function GetSQLParams : ISQLParams;
446 procedure SetupBufferStructure(metadata: IMetadata; aFields: TFields; aMaxFieldNo: integer);
447 procedure ClearBlobCache;
448 procedure ClearArrayCache;
449 procedure CopyCursorDataToBuffer(QryResults: IResults; QryIndex, ColIndex: integer;
450 destBuff: PByte);
451 function InternalGetIsNull(Buff: PByte; ColIndex: integer): boolean;
452 procedure InternalSetIsNull(Buff: PByte; ColIndex: integer; IsNull: boolean);
453 procedure SaveBlobsAndArrays(Buff: PByte);
454 function NormaliseParamName(aName: AnsiString; var UseOldValue: boolean): AnsiString;
455 procedure ClearRegisteredQueries;
456 procedure SetParamValue(Buff: PByte; colIndex: integer; Param: ISQLParam);
457 protected
458 FCurrentRecord: PByte;
459 FCurrentRecordStatus: (csBOF, csRowBuffer, csEOF);
460 FSaveBufferSize: integer;
461 FEditState: (esBrowse, esEdit, esInsert);
462 function CalcRecordHdrSize: integer; virtual;
463 procedure ClearRowCache(aBuffer: PByte);
464 function ColIndexByName(aName: AnsiString; caseSensitive: boolean=false): integer;
465 procedure FetchCurrentRecord(destBuffer: PByte);
466 procedure FieldChanged(aBuffer: PByte; aField: TField); virtual;
467 function GetBuffer(aBufID: TRecordBuffer): PByte; inline;
468 procedure SetBuffer(aBufID: TRecordBuffer; aBuffer: PByte); inline;
469 function GetCalcFields(aBufID: TRecordBuffer): PByte; inline;
470 function GetOldBufferFor(aBuffer: PByte): PByte; virtual; abstract;
471 function FieldNo2ColumnIndex(aField: TField): integer; inline;
472 function InternalAllocRecordBuffer: PByte; virtual; abstract;
473 procedure InternalFreeRecordBuffer(aBuffer: PByte); virtual; abstract;
474 function InternalGetRecNo(aBuffer: PByte): TIBRecordNumber; virtual; abstract;
475 procedure InternalDelete(aBufID: TRecordBuffer); virtual; abstract;
476 procedure InternalUnDelete(aBuffer: PByte); virtual; abstract;
477 function InternalGetUpdateStatus(aBuffer: PByte): TUpdateStatus; inline;
478 procedure InternalSetUpdateStatus(aBuffer: PByte; status: TUpdateStatus); inline;
479 procedure SetRefreshRequired(Buff: PByte; ColIndex: integer; RefreshRequired: boolean);
480 procedure SetUpdateStatus(aBufID: TRecordBuffer; status: TUpdateStatus);
481 procedure Reset; virtual;
482 function FetchNext: boolean;
483 protected
484 property Buffers[index: TRecordBuffer]:PByte read GetBuffer;
485 property Cursor: IResultSet read FCursor;
486 property ColumnMetaData: TColumnMetadataArray read FColumnMetaData;
487 public
488 constructor Create(aDataset: TDataset; aName: string; aCursor: IResultSet; aFields: TFields;
489 aMaxFieldNo: integer; aCalcFieldsSize: integer; aDefaultTZDate: TDateTime);
490 destructor Destroy; override;
491
492 {TDataset Interface}
493 function AllocRecordBuffer: TRecordBuffer;
494 procedure FreeRecordBuffer(var Buffer: TRecordBuffer);
495 procedure ClearRecordBuffer(Buffer: TRecordBuffer);
496 procedure SetCurrentRecord(aBufID: TRecordBuffer);
497 function CreateBlobStream(aBufID: TRecordBuffer; Field: TField; Mode: TBlobStreamMode): TStream; virtual;
498 function GetArray(aBufID: TRecordBuffer; Field: TField): IArray;
499 procedure SetArrayIntf(aBufID: TRecordBuffer; AnArray: IArray; Field: TField);
500 function GetRecDBkey(aBufID: TRecordBuffer): TIBDBKey;
501 function GetFieldData(aBufID: TRecordBuffer; field: TField; outBuffer: PByte): boolean;
502 procedure SetFieldData(aBufID: TRecordBuffer; field: TField; inBuffer: PByte);
503 procedure SetSQLParams(aBufID: TRecordBuffer; params: ISQLParams);
504 procedure UpdateRecordFromQuery(aBufID: TRecordBuffer; QryResults: IResults);
505 function NeedRefresh(aBufID: TRecordBuffer): boolean;
506 function GetBookmarkFlag(aBufID: TRecordBuffer): TBookmarkFlag;
507 procedure SetBookmarkFlag(aBufID: TRecordBuffer; aBookmarkFlag: TBookmarkFlag);
508 procedure SetBookmarkData(aBufID: TRecordBuffer; RecNo: TIBRecordNumber); overload;
509 procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
510 procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); overload;
511 function GetBookmarkSize: integer;
512 function GetRecordSize: word;
513 function GetCurrentRecNo: TIBRecordNumber;
514 procedure SwapDataBuffer(buf1, buf2: TRecordBuffer);
515 function GetAliasName(FieldNo: integer): AnsiString;
516 procedure InitRecord(aBufID: TRecordBuffer); virtual;
517 function AtBOF: boolean;
518 function AtEOF: boolean;
519 function CursorAtEOF: boolean;
520 procedure Delete(aBufID: TRecordBuffer);
521 procedure UnDelete(aBufID: TRecordBuffer);
522 function GetUpdateStatus(aBufID: TRecordBuffer): TUpdateStatus;
523 procedure ClearCalcFields(aBufID: TRecordBuffer);
524 procedure SetCursor(aCursor: IResultSet);
525 procedure RegisterQuery(qryType : TRegisteredQueryTypes; qry : IStatement;
526 OnValuesReturnedProc : TOnValuesReturned);
527 procedure ExecRegisteredQuery(qryType : TRegisteredQueryTypes; aBufID: TRecordBuffer;
528 var SelectCount, InsertCount, UpdateCount, DeleteCount: integer);
529 function HasRegisteredQuery(qryType : TRegisteredQueryTypes): boolean;
530 public
531 property RecordBufferSize: integer read FRecordBufferSize;
532 property ColumnCount: integer read FColumnCount;
533 property RecordCount: integer read FRecordCount;
534 property CalcFieldsSize: integer read FCalcFieldsSize;
535 property BlobFieldCount: integer read FBlobFieldCount;
536 property ArrayFieldCount: integer read FArrayFieldCount;
537 property Name: string read FName;
538 property Dataset: TDataset read FDataset;
539 property Params: ISQLParams read GetSQLParams;
540 end;
541
542 { TIBEditableCursor adds the means the save a copy of the current buffer during
543 a row edit and to restore it later. This may either be a single save buffer
544 or, if cached updates are enabled, a save buffer allocated from the old buffer pool.}
545
546 TIBEditableCursor = class(TIBSelectCursor)
547 private
548 const OldBuffersPerBlock = 1024;
549 type
550 PLocalHeader = ^TLocalHeader;
551 TLocalHeader = record
552 lhOldBuffer: PByte;
553 end;
554 strict private
555 FApplyUpdates: TUpdatesIterator;
556 FSaveBuffer: PByte;
557 FOldBufferCache: TIBOldBufferPool;
558 FCachedUpdatesEnabled: boolean;
559 FLocalHdrOffset: integer;
560 procedure ApplyUpdatesIterator(status: TCachedUpdateStatus; DataBuffer, OldBuffer: PByte);
561 protected
562 FSavedRecNo: TIBRecordNumber; {record number of saved buffer if any}
563 function CalcRecordHdrSize: integer override;
564 procedure CancelBlobAndArrayChanges(aBuffer: PByte);
565 procedure CopyBuffers(src, dest: PByte); inline;
566 procedure DoCancelUpdates; virtual; abstract;
567 procedure DoOnInserted(aBuffer: PByte);
568 procedure FieldChanged(aBuffer: PByte; aField: TField); override;
569 function GetOldBufferFor(aBuffer: PByte): PByte; override;
570 procedure InitCachedUpdates; virtual;
571 procedure ReleaseSaveBuffer(aBufID: TRecordBuffer; UpdateStatus: TCachedUpdateStatus);
572 property OldBufferCache: TIBOldBufferPool read FOldBufferCache;
573 procedure InternalDelete(aBufID: TRecordBuffer); override;
574 procedure InternalUnDelete(aBuffer: PByte); override;
575 procedure InternalSetOldBuffer(aBuffer, OldBuffer: PByte); inline;
576 procedure Reset; override;
577 public
578 constructor Create(aDataset: TDataset; aName: string; aCursor: IResultSet; aFields: TFields;
579 aMaxFieldNo: integer; aCalcFieldsSize: integer; aDefaultTZDate: TDateTime; CachedUpdates: boolean);
580 destructor Destroy; override;
581 procedure EditingDone(aBufID: TRecordBuffer; UpdateStatus: TCachedUpdateStatus); virtual;
582 procedure EditBuffer(aBufID: TRecordBuffer);
583 function GetRecNo(aBufID: TRecordBuffer): TIBRecordNumber; virtual; abstract;
584 procedure CancelChanges(aBufID: TRecordBuffer);
585 procedure ApplyUpdates(iterator: TUpdatesIterator);
586 procedure CancelUpdates;
587 function UpdatesPending: boolean;
588 function GetCachedUpdatesEnabled:boolean;
589 function GetCachedUpdateStatus(aBufID: TRecordBuffer): TCachedUpdateStatus;
590 end;
591
592 {
593 TIBUniDirectionalCursor provides a simple forwards only cursor with edit support.
594
595 }
596
597 TIBUniDirectionalCursor = class(TIBEditableCursor, IIBCursor)
598 private const
599 DataBuffersPerBlock = 1024;
600 private type
601 PUniDirRecordHdr = ^TUniDirRecordHdr;
602 TUniDirRecordHdr = record
603 rdRecNo: TIBRecordNumber;
604 end;
605
606 private
607 FRecordCount: TIBRecordNumber;
608 FInserting: boolean;
609 FInsertedRecords: integer;
610 FDeletedRecords: integer;
611 FDataBufferCache: TIBSimpleBufferPool;
612 FUniDirHdrOffset: integer;
613 procedure InternalSetRecNo(aBuffer: PByte; recno: TIBRecordNumber); inline;
614 protected
615 function CalcRecordHdrSize: integer; override;
616 procedure DoCancelUpdates; override;
617 function InternalAllocRecordBuffer: PByte; override;
618 procedure InternalFreeRecordBuffer(aBuffer: PByte); override;
619 function InternalGetRecNo(aBuffer : PByte) : TIBRecordNumber; override;
620 procedure InitCachedUpdates; override;
621 procedure InternalDelete(aBufID: TRecordBuffer); override;
622 procedure InternalUnDelete(aBuffer: PByte); override;
623 procedure Reset; override;
624 public
625 destructor Destroy; override;
626 function GetRecord(aBufID: TRecordBuffer; GetMode: TGetMode;
627 DoCheck: Boolean): TGetResult;
628 function GetRecNo(aBufID: TRecordBuffer): TIBRecordNumber; override;
629 function GetRecordCount: TIBRecordNumber;
630 procedure GotoFirst;
631 procedure GotoLast;
632 function GotoRecordNumber(RecNo: TIBRecordNumber): boolean;
633 procedure EditingDone(aBufID: TRecordBuffer; UpdateStatus: TCachedUpdateStatus); override;
634 procedure InsertBefore(aBufID: TRecordBuffer);
635 procedure Append(aBufID: TRecordBuffer);
636 function GetInsertedRecords: integer;
637 function GetDeletedRecords: integer;
638 procedure InitRecord(aBufID: TRecordBuffer); override;
639 end;
640
641 {TIBBiDirectionalCursor provides a buffered dataset that can be scrolled in either direction}
642
643 TIBBiDirectionalCursor = class(TIBEditableCursor,IIBCursor)
644 private
645 FBufferPool: TIBBufferPool;
646 procedure CancelUpdatesIterator(status: TCachedUpdateStatus; DataBuffer, OldBuffer: PByte);
647 function NewBuffer: PByte; inline;
648 protected
649 procedure DoCancelUpdates; override;
650 function InternalAllocRecordBuffer: PByte; override;
651 procedure InternalFreeRecordBuffer(aBuffer: PByte); override;
652 function InternalGetRecNo(aBuffer: PByte): TIBRecordNumber; override;
653 procedure InternalDelete(aBufID: TRecordBuffer); override;
654 procedure InternalUnDelete(aBuffer: PByte); override;
655 procedure Reset; override;
656 public
657 constructor Create(aDataset: TDataset; aName: string; aCursor: IResultSet; aFields: TFields;
658 aMaxFieldNo: integer; aCalcFieldsSize: integer;
659 aDefaultTZDate: TDateTime; CachedUpdates: boolean;
660 aBuffersPerBlock, aFirstBlockBuffers: integer);
661 destructor Destroy; override;
662 function GetRecord(aBufID: TRecordBuffer; GetMode: TGetMode;
663 DoCheck: Boolean): TGetResult;
664 function GetRecNo(aBufID: TRecordBuffer): TIBRecordNumber; override;
665 procedure GotoFirst;
666 procedure GotoLast;
667 function GotoRecordNumber(RecNo: TIBRecordNumber): boolean;
668 function GetRecordCount: TIBRecordNumber;
669 procedure InsertBefore(aBufID: TRecordBuffer);
670 procedure Append(aBufID: TRecordBuffer);
671 function GetInsertedRecords: integer;
672 function GetDeletedRecords: integer;
673 procedure InitRecord(aBufID: TRecordBuffer); override;
674 end;
675
676 implementation
677
678 uses IBMessages, IBCustomDataSet, IBInternals, IBSQLMonitor;
679
680 type
681 THackedField = class(TField); {Used to access to protected method TField.DataChange}
682
683 { TIBEditableCursor }
684
685 procedure TIBEditableCursor.ApplyUpdatesIterator(status: TCachedUpdateStatus;
686 DataBuffer, OldBuffer: PByte);
687 var DisplayBuffer: TDisplayBuffer;
688 RecordSkipped: boolean;
689 curOldBuffer: PByte;
690 begin
691 FSavedRecNo := InternalGetRecNo(DataBuffer);
692 DisplayBuffer.dbBookmarkFlag := bfCurrent;
693 DisplayBuffer.dbBuffer := DataBuffer;
694 DisplayBuffer.dbCalcFields := nil;
695 curOldBuffer := GetOldBufferFor(DataBuffer);
696 InternalSetOldBuffer(DataBuffer,OldBuffer); {May be different if more than one edit}
697 try
698 RecordSkipped := false;
699 FApplyUpdates(status,TRecordBuffer(@DisplayBuffer),RecordSkipped);
700 if not RecordSkipped then
701 FOldBufferCache.SetStatus(OldBuffer,cusUnModified);
702 finally
703 FSavedRecNo := 0;
704 InternalSetOldBuffer(DataBuffer,curOldBuffer);
705 end;
706 end;
707
708 function TIBEditableCursor.CalcRecordHdrSize: integer;
709 begin
710 Result := inherited CalcRecordHdrSize;
711 if FCachedUpdatesEnabled then
712 begin
713 FLocalHdrOffset := Result;
714 Inc(Result,sizeof(TLocalHeader));
715 end;
716 end;
717
718 procedure TIBEditableCursor.CancelBlobAndArrayChanges(aBuffer: PByte);
719 var i: integer;
720 pda: PIBArray;
721 begin
722 for i := 0 to ColumnCount - 1 do
723 with ColumnMetaData[i] do
724 case fdDataType of
725 SQL_BLOB:
726 PIBBlobStream(aBuffer + fdObjOffset)^ := nil;
727 SQL_ARRAY:
728 begin
729 pda := PIBArray(aBuffer + fdObjOffset);
730 if pda^ <> nil then
731 begin
732 pda^.ArrayIntf.CancelChanges;
733 pda^ := nil;
734 end;
735 end;
736 end;
737 end;
738
739 procedure TIBEditableCursor.CopyBuffers(src, dest: PByte);
740 begin
741 Move(src^,dest^,FSaveBufferSize);
742 end;
743
744 procedure TIBEditableCursor.DoOnInserted(aBuffer: PByte);
745 var OldBuffer: PByte;
746 i: integer;
747 begin
748 if FCachedUpdatesEnabled then
749 begin
750 OldBuffer := FOldBufferCache.Append(InternalGetRecNo(aBuffer),aBuffer);
751 FOldBufferCache.SetStatus(OldBuffer,cusInserted);
752 end;
753 FEditState := esInsert;
754 for i := 0 to ColumnCount - 1 do
755 with ColumnMetaData[i] do
756 if fdRefreshOnInsert then
757 SetRefreshRequired(aBuffer,i,true);
758 end;
759
760 procedure TIBEditableCursor.FieldChanged(aBuffer : PByte; aField : TField);
761 var I: integer;
762 begin
763 inherited FieldChanged(aBuffer, aField);
764
765 if InternalGetUpdateStatus(aBuffer) = usUnModified then
766 InternalSetUpdateStatus(aBuffer,usModified);
767 end;
768
769 function TIBEditableCursor.GetOldBufferFor(aBuffer: PByte): PByte;
770 begin
771 if InternalGetRecNo(aBuffer) = FSavedRecNo then
772 if FCachedUpdatesEnabled then
773 Result := PLocalHeader(aBuffer + FLocalHdrOffset)^.lhOldBuffer
774 else
775 Result := FSaveBuffer
776 else
777 Result := nil;
778 end;
779
780 procedure TIBEditableCursor.InitCachedUpdates;
781 begin
782 if FOldBufferCache = nil then
783 FOldBufferCache := TIBOldBufferPool.Create(Name + ': Old Buffer Cache', FSaveBufferSize,
784 OldBuffersPerBlock, OldBuffersPerBlock);
785 end;
786
787 constructor TIBEditableCursor.Create(aDataset: TDataset; aName: string;
788 aCursor: IResultSet; aFields: TFields; aMaxFieldNo: integer;
789 aCalcFieldsSize: integer; aDefaultTZDate: TDateTime; CachedUpdates: boolean
790 );
791 begin
792 FCachedUpdatesEnabled := CachedUpdates;
793 inherited Create(aDataset, aName, aCursor,aFields, aMaxFieldNo, aCalcFieldsSize, aDefaultTZDate);
794 if FCachedUpdatesEnabled then
795 InitCachedUpdates
796 else
797 begin
798 FSaveBuffer := GetMem(FSaveBufferSize);
799 if FSaveBuffer = nil then
800 OutOfMemoryError;
801 end;
802 FEditState := esBrowse;
803 end;
804
805 destructor TIBEditableCursor.Destroy;
806 begin
807 if FSaveBuffer <> nil then
808 FreeMem(FSaveBuffer);
809 if FOldBufferCache <> nil then
810 FOldBufferCache.Free;
811 inherited Destroy;
812 end;
813
814 procedure TIBEditableCursor.EditingDone(aBufID: TRecordBuffer;
815 UpdateStatus: TCachedUpdateStatus);
816 begin
817 if FSavedRecNo <> 0 then
818 ReleaseSaveBuffer(aBufID, UpdateStatus);
819 FEditState := esBrowse;
820 end;
821
822 procedure TIBEditableCursor.EditBuffer(aBufID: TRecordBuffer);
823 var Buff: PByte;
824 OldBuffer: PByte;
825 i: integer;
826 begin
827 Buff := GetBuffer(aBufID);
828 if Buff = nil then
829 IBError(ibxeBufferNotSet, [nil]);
830
831 if FSavedRecNo <> 0 then
832 IBError(ibxeSaveBufferNotReleased,[InternalGetRecNo(Buff),FSavedRecNo]);
833
834 if FCachedUpdatesEnabled then
835 OldBuffer := FOldBufferCache.Append(InternalGetRecNo(Buff),Buff)
836 else
837 OldBuffer := FSaveBuffer;
838
839 InternalSetOldBuffer(Buff,OldBuffer);
840 CopyBuffers(Buff,OldBuffer);
841 FSavedRecNo := InternalGetRecNo(Buff);
842 FEditState := esEdit;
843 for i := 0 to ColumnCount - 1 do
844 with ColumnMetaData[i] do
845 if fdRefreshOnUpdate then
846 SetRefreshRequired(Buff,i,true);
847 end;
848
849 procedure TIBEditableCursor.CancelChanges(aBufID: TRecordBuffer);
850 var Buff: PByte;
851 OldBuffer: PByte;
852 begin
853 Buff := GetBuffer(aBufID);
854 if Buff = nil then
855 IBError(ibxeBufferNotSet, [nil]);
856
857 case FEditState of
858 esInsert:
859 InternalDelete(aBufID);
860
861 esEdit:
862 begin
863 if InternalGetRecNo(Buff) <> FSavedRecNo then
864 IBError(ibxeUnableToRestore,[InternalGetRecNo(Buff),FSavedRecNo]);
865
866 CancelBlobAndArrayChanges(Buff);
867 OldBuffer := GetOldBufferFor(Buff);
868 CopyBuffers(OldBuffer,Buff);
869 if FCachedUpdatesEnabled then
870 FOldBufferCache.SetStatus(OldBuffer,cusUnModified);
871 InternalSetOldBuffer(Buff,nil);
872 FSavedRecNo := 0;
873 end;
874 end;
875 FEditState := esBrowse;
876 end;
877
878 procedure TIBEditableCursor.ReleaseSaveBuffer(aBufID: TRecordBuffer;
879 UpdateStatus: TCachedUpdateStatus);
880 var Buff: PByte;
881 begin
882 Buff := GetBuffer(aBufID);
883 if Buff = nil then
884 IBError(ibxeBufferNotSet, [nil]);
885
886 if FSavedRecNo <> InternalGetRecNo(Buff) then
887 IBError(ibxeUnableToReleaseSaveBuffer,[InternalGetRecNo(Buff),FSavedRecNo]);
888
889 if FCachedUpdatesEnabled then
890 FOldBufferCache.SetStatus(GetOldBufferFor(Buff),UpdateStatus);
891 InternalSetOldBuffer(Buff,nil);
892 FSavedRecNo := 0;
893 end;
894
895 procedure TIBEditableCursor.InternalDelete(aBufID: TRecordBuffer);
896 begin
897 SetUpdateStatus(aBufID,usDeleted);
898 if FCachedUpdatesEnabled then
899 begin
900 if FSavedRecNo = 0 then
901 EditBuffer(aBufID);
902 if GetUpdateStatus(aBufID) = usInserted then
903 EditingDone(aBufID,cusUninserted)
904 else
905 EditingDone(aBufID,cusDeleted);
906 end;
907 end;
908
909 procedure TIBEditableCursor.InternalUnDelete(aBuffer: PByte);
910 var OldBuffer: PByte;
911 begin
912 if not FCachedUpdatesEnabled then
913 IBError(ibxeCannotUnDelete,[]);
914
915 OldBuffer := GetOldBufferFor(aBuffer);
916
917 case FOldBufferCache.GetStatus(OldBuffer) of
918 cusUninserted:
919 FOldBufferCache.SetStatus(OldBuffer,cusInserted);
920
921 cusDeleted:
922 if InternalGetUpdateStatus(aBuffer) = usDeleted then
923 begin
924 FOldBufferCache.SetStatus(OldBuffer,cusUnmodified);
925 InternalSetUpdateStatus(aBuffer,usUnmodified);
926 end;
927 end;
928 end;
929
930 procedure TIBEditableCursor.InternalSetOldBuffer(aBuffer, OldBuffer: PByte);
931 begin
932 if FCachedUpdatesEnabled then
933 PLocalHeader(aBuffer + FLocalHdrOffset)^.lhOldBuffer := OldBuffer;
934 end;
935
936 procedure TIBEditableCursor.Reset;
937 begin
938 inherited Reset;
939 if FOldBufferCache <> nil then
940 FOldBufferCache.Clear;
941 FSavedRecNo := 0;
942 end;
943
944 procedure TIBEditableCursor.ApplyUpdates(iterator: TUpdatesIterator);
945 begin
946 if FSavedRecNo <> 0 then
947 IBError(ibxeSaveBufferNotReleased,[0,FSavedRecNo]);
948
949 if not FCachedUpdatesEnabled or not UpdatesPending then Exit;
950
951 FCachedUpdatesEnabled := false;
952 try
953 FApplyUpdates := iterator;
954 FSavedRecNo := 0;
955 FOldBufferCache.ForwardIterator(ApplyUpdatesIterator);
956 if FOldBufferCache.ModifiedRecords = 0 then
957 FOldBufferCache.Clear;
958 finally
959 FCachedUpdatesEnabled := true;
960 end;
961 end;
962
963 procedure TIBEditableCursor.CancelUpdates;
964 begin
965 if FSavedRecNo <> 0 then
966 IBError(ibxeSaveBufferNotReleased,[0,FSavedRecNo]);
967
968 if not FCachedUpdatesEnabled or not UpdatesPending then Exit;
969
970 FCachedUpdatesEnabled := false;
971 try
972 FSavedRecNo := 0;
973 DoCancelUpdates;
974 FOldBufferCache.Clear;
975 finally
976 FCachedUpdatesEnabled := true;
977 end;
978 end;
979
980 function TIBEditableCursor.UpdatesPending: boolean;
981 begin
982 Result := FOldBufferCache.RecordCount > 0;
983 end;
984
985 function TIBEditableCursor.GetCachedUpdatesEnabled: boolean;
986 begin
987 Result := FCachedUpdatesEnabled;
988 end;
989
990 function TIBEditableCursor.GetCachedUpdateStatus(aBufID: TRecordBuffer
991 ): TCachedUpdateStatus;
992 begin
993 if GetCachedUpdatesEnabled then
994 Result := FOldBufferCache.GetStatus(GetRecNo(aBufID))
995 else
996 Result := cusUnModified;
997 end;
998
999 { TIBBiDirectionalCursor }
1000
1001 procedure TIBBiDirectionalCursor.CancelUpdatesIterator(
1002 status: TCachedUpdateStatus; DataBuffer, OldBuffer: PByte);
1003 var curOldBuffer: PByte;
1004 begin
1005 curOldBuffer := GetOldBufferFor(DataBuffer);
1006 InternalSetOldBuffer(DataBuffer,OldBuffer);
1007 FSavedRecNo := InternalGetRecNo(DataBuffer);
1008 try
1009 case status of
1010 cusInserted:
1011 FBufferPool.Delete(DataBuffer);
1012
1013 cusDeleted:
1014 FBufferPool.UnDelete(DataBuffer);
1015
1016 cusModified:
1017 CopyBuffers(OldBuffer,DataBuffer);
1018 end;
1019 finally
1020 InternalSetOldBuffer(DataBuffer,curOldBuffer);
1021 FSavedRecNo := 0;
1022 end;
1023 end;
1024
1025 function TIBBiDirectionalCursor.NewBuffer: PByte;
1026 begin
1027 Result := FBufferPool.Append;
1028 end;
1029
1030 procedure TIBBiDirectionalCursor.DoCancelUpdates;
1031 begin
1032 OldBufferCache.BackwardsIterator(CancelUpdatesIterator);
1033 GotoLast;
1034 end;
1035
1036 function TIBBiDirectionalCursor.InternalGetRecNo(aBuffer: PByte): TIBRecordNumber;
1037 begin
1038 Result := FBufferPool.GetRecNo(aBuffer);
1039 end;
1040
1041 procedure TIBBiDirectionalCursor.InternalDelete(aBufID: TRecordBuffer);
1042 begin
1043 inherited InternalDelete(aBufID);
1044 SetCurrentRecord(aBufID);
1045 case FCurrentRecordStatus of
1046 csBOF:
1047 IBError(ibxeDeleteAtBOF,[]);
1048 csEOF:
1049 IBError(ibxeDeleteBeyondEOF,[]);
1050 csRowBuffer:
1051 begin
1052 {$ifdef PrintBuf}
1053 writeln('Delete RecNo ',GetRecNo(aBufID));
1054 {$endif}
1055 FCurrentRecord := FBufferPool.Delete(FCurrentRecord);
1056 {$ifdef PrintBuf}
1057 FBufferPool.PrintBufferList;
1058 {$endif}
1059 if FCurrentRecord = nil then
1060 FCurrentRecordStatus := csBOF;
1061 end;
1062 end;
1063 end;
1064
1065 procedure TIBBiDirectionalCursor.InternalUnDelete(aBuffer: PByte);
1066 begin
1067 inherited InternalUnDelete(aBuffer);
1068 FBufferPool.UnDelete(aBuffer);
1069 end;
1070
1071 procedure TIBBiDirectionalCursor.Reset;
1072 begin
1073 inherited Reset;
1074 if FBufferPool <> nil then
1075 FBufferPool.Clear;
1076 end;
1077
1078 function TIBBiDirectionalCursor.InternalAllocRecordBuffer: PByte;
1079 begin
1080 Result := nil; {see GetRecord}
1081 end;
1082
1083 procedure TIBBiDirectionalCursor.InternalFreeRecordBuffer(aBuffer: PByte);
1084 begin
1085 // Do nothing
1086 end;
1087
1088 constructor TIBBiDirectionalCursor.Create(aDataset: TDataset; aName: string;
1089 aCursor: IResultSet; aFields: TFields; aMaxFieldNo: integer;
1090 aCalcFieldsSize: integer; aDefaultTZDate: TDateTime;
1091 CachedUpdates: boolean; aBuffersPerBlock, aFirstBlockBuffers: integer);
1092 begin
1093 inherited Create(aDataset,aName,aCursor,aFields, aMaxFieldNo, aCalcFieldsSize, aDefaultTZDate,CachedUpdates);
1094 FBufferPool := TIBBufferPool.Create(aName+ ': BiDirectional record cache',
1095 RecordBufferSize,aBuffersPerBlock, aFirstBlockBuffers);
1096 end;
1097
1098 destructor TIBBiDirectionalCursor.Destroy;
1099 begin
1100 if FBufferPool <> nil then
1101 FBufferPool.Free;
1102 inherited Destroy;
1103 end;
1104
1105 function TIBBiDirectionalCursor.GetRecord(aBufID: TRecordBuffer;
1106 GetMode: TGetMode; DoCheck: Boolean): TGetResult;
1107
1108 function ReadNext: TGetResult;
1109 begin
1110 if not Cursor.IsEof and FetchNext then
1111 begin
1112 FCurrentRecord := NewBuffer;
1113 FetchCurrentRecord(FCurrentRecord);
1114 FCurrentRecordStatus := csRowBuffer;
1115 Result := grOK;
1116 end
1117 else
1118 begin
1119 FCurrentRecordStatus := csEOF;
1120 Result := grEOF;
1121 end;
1122 end;
1123
1124 begin
1125 Result := grError;
1126 case GetMode of
1127 gmPrior:
1128 case FCurrentRecordStatus of
1129 csBOF:
1130 Result := grBOF;
1131 csRowBuffer:
1132 begin
1133 FCurrentRecord := FBufferPool.GetPriorBuffer(FCurrentRecord);
1134 if FCurrentRecord = nil then
1135 begin
1136 FCurrentRecordStatus := csBOF;
1137 Result := grBOF
1138 end
1139 else
1140 Result := grOK;
1141 end;
1142 csEOF:
1143 begin
1144 FCurrentRecord := FBufferPool.GetLast;
1145 if FCurrentRecord = nil then
1146 Result := grEOF
1147 else
1148 begin
1149 FCurrentRecordStatus := csRowBuffer;
1150 Result := grOK;
1151 end;
1152 end;
1153 end;
1154
1155 gmCurrent:
1156 case FCurrentRecordStatus of
1157 csBOF:
1158 Result := grBOF;
1159 csRowBuffer:
1160 Result := grOK;
1161 csEOF:
1162 Result := grEOF;
1163 end;
1164
1165 gmNext:
1166 case FCurrentRecordStatus of
1167 csBOF:
1168 begin
1169 FCurrentRecord := FBufferPool.GetFirst;
1170 if (FCurrentRecord = nil) then
1171 Result := ReadNext
1172 else
1173 begin
1174 FCurrentRecordStatus := csRowBuffer;
1175 Result := grOK;
1176 end;
1177 end;
1178 csRowBuffer:
1179 begin
1180 FCurrentRecord := FBufferPool.GetNextBuffer(FCurrentRecord);
1181 if (FCurrentRecord = nil) then
1182 Result := ReadNext
1183 else
1184 Result := grOK;
1185 end;
1186 csEOF:
1187 Result := grEOF;
1188 end;
1189 end;
1190
1191 case Result of
1192 grOK:
1193 begin
1194 SetBuffer(aBufID,FCurrentRecord);
1195 SetBookmarkFlag(aBufID,bfCurrent);
1196 end;
1197 grBOF:
1198 begin
1199 SetBuffer(aBufID,nil);
1200 SetBookmarkFlag(aBufID,bfBOF);
1201 end;
1202 grEOF:
1203 begin
1204 SetBuffer(aBufID,nil);
1205 SetBookmarkFlag(aBufID,bfEOF);
1206 end;
1207 end;
1208 { if FCurrentRecord <> nil then
1209 writeln('Get Record request ',GetMode,' Returns Rec No ',InternalGetRecNo(FCurrentRecord),' Status = ',
1210 FBufferPool.GetRecordStatus(FCurrentRecord))
1211 else
1212 writeln('Get Record request ',GetMode,' Returns ',Result); }
1213 SetBookmarkData(aBufID,InternalGetRecNo(FCurrentRecord));
1214 end;
1215
1216 function TIBBiDirectionalCursor.GetRecNo(aBufID: TRecordBuffer): TIBRecordNumber;
1217 var Buff: PByte;
1218 begin
1219 Buff := GetBuffer(aBufID);
1220 if Buff = nil then
1221 Result := 0
1222 else
1223 Result := InternalGetRecNo(Buff);
1224 end;
1225
1226 procedure TIBBiDirectionalCursor.GotoFirst;
1227 begin
1228 FCurrentRecord := nil;
1229 FCurrentRecordStatus := csBOF;
1230 end;
1231
1232 procedure TIBBiDirectionalCursor.GotoLast;
1233 begin
1234 FCurrentRecord := FBufferPool.GetLast;
1235 if (FCurrentRecord <> nil) or not Cursor.IsEOF then
1236 begin
1237 FCurrentRecordStatus := csRowBuffer;
1238 if not Cursor.IsEof then
1239 while FetchNext do
1240 begin
1241 FCurrentRecord := NewBuffer;
1242 FetchCurrentRecord(FCurrentRecord);
1243 end;
1244 end;
1245 FCurrentRecord := nil;
1246 FCurrentRecordStatus := csEOF;
1247 end;
1248
1249 function TIBBiDirectionalCursor.GotoRecordNumber(RecNo: TIBRecordNumber): boolean;
1250 begin
1251 if FBufferPool.GetRecordCount >= RecNo then
1252 FCurrentRecord := FBufferPool.GetBuffer(RecNo)
1253 else
1254 begin
1255 FCurrentRecord := FBufferPool.GetLast;
1256 if not Cursor.IsEOF then
1257 while (FBufferPool.GetRecNo(FCurrentRecord) < RecNo) and FetchNext do
1258 begin
1259 FCurrentRecord := NewBuffer;
1260 FetchCurrentRecord(FCurrentRecord);
1261 end;
1262 end;
1263 Result := (FBufferPool.GetRecNo(FCurrentRecord) = RecNo);
1264 end;
1265
1266 function TIBBiDirectionalCursor.GetRecordCount: TIBRecordNumber;
1267 begin
1268 Result := FBufferPool.GetRecordCount - FBufferPool.DeletedRecords;
1269 end;
1270
1271
1272 procedure TIBBiDirectionalCursor.InsertBefore(aBufID: TRecordBuffer);
1273 var RecNo: TIBRecordNumber;
1274 begin
1275 GetBookmarkData(aBufID,@RecNo);
1276 if RecNo = 0 then
1277 GotoFirst
1278 else
1279 GotoRecordNumber(RecNo);
1280 {$ifdef PrintBuf}
1281 writeln('Insert Before Record = ',RecNo);
1282 {$endif}
1283 case FCurrentRecordStatus of
1284 csBOF:
1285 FCurrentRecord := FBufferPool.InsertBefore(FBufferPool.GetFirst);
1286 csRowBuffer:
1287 FCurrentRecord := FBufferPool.InsertBefore(FCurrentRecord);
1288 csEOF:
1289 FCurrentRecord := NewBuffer;
1290 end;
1291 InternalSetUpdateStatus(FCurrentRecord,usInserted);
1292 DoOnInserted(FCurrentRecord);
1293 SetBuffer(aBufID, FCurrentRecord);
1294 FCurrentRecordStatus := csRowBuffer;
1295 {$ifdef PrintBuf}
1296 FBufferPool.PrintBufferList;
1297 {$endif}
1298 end;
1299
1300 procedure TIBBiDirectionalCursor.Append(aBufID: TRecordBuffer);
1301 begin
1302 GotoLast;
1303 {$ifdef PrintBuf}
1304 writeln('Append After Record = ',GetRecNo(aBufID));
1305 {$endif}
1306 if FCurrentRecordStatus = csEOF then
1307 FCurrentRecord := FBufferPool.Append
1308 else
1309 FCurrentRecord := FBufferPool.InsertAfter(FCurrentRecord);
1310 InternalSetUpdateStatus(FCurrentRecord,usInserted);
1311 DoOnInserted(FCurrentRecord);
1312 SetBuffer(aBufID,FCurrentRecord);
1313 FCurrentRecordStatus := csRowBuffer;
1314 {$ifdef PrintBuf}
1315 FBufferPool.PrintBufferList;
1316 {$endif}
1317 end;
1318
1319 function TIBBiDirectionalCursor.GetInsertedRecords: integer;
1320 begin
1321 Result := FBufferPool.InsertedRecords;
1322 end;
1323
1324 function TIBBiDirectionalCursor.GetDeletedRecords: integer;
1325 begin
1326 Result := FBufferPool.DeletedRecords;
1327 end;
1328
1329 procedure TIBBiDirectionalCursor.InitRecord(aBufID: TRecordBuffer);
1330 begin
1331 inherited InitRecord(aBufID);
1332 if aBufID <> nil then
1333 PDisplayBuffer(aBufID)^.dbBuffer := nil;
1334 end;
1335
1336 { TIBUniDirectionalCursor }
1337
1338 function TIBUniDirectionalCursor.InternalGetRecNo(aBuffer: PByte) : TIBRecordNumber;
1339 begin
1340 if aBuffer = nil then
1341 Result := 0
1342 else
1343 Result := PUniDirRecordHdr(aBuffer+ FUniDirHdrOffset)^.rdRecNo;
1344 end;
1345
1346 procedure TIBUniDirectionalCursor.InitCachedUpdates;
1347 begin
1348 inherited InitCachedUpdates;
1349 if FDataBufferCache = nil then
1350 FDataBufferCache := TIBSimpleBufferPool.Create(Name + ': Data Buffer Cacne',
1351 FSaveBufferSize,
1352 DataBuffersPerBlock,DataBuffersPerBlock);
1353 end;
1354
1355 destructor TIBUniDirectionalCursor.Destroy;
1356 begin
1357 if FDataBufferCache <> nil then
1358 FDataBufferCache.Free;
1359 inherited Destroy;
1360 end;
1361
1362 procedure TIBUniDirectionalCursor.InternalSetRecNo(aBuffer: PByte;
1363 recno: TIBRecordNumber);
1364 begin
1365 PUniDirRecordHdr(aBuffer + FUniDirHdrOffset)^.rdRecNo := recno;
1366 end;
1367
1368 function TIBUniDirectionalCursor.CalcRecordHdrSize: integer;
1369 begin
1370 Result := inherited CalcRecordHdrSize;
1371 FUniDirHdrOffset := Result;
1372 Inc(Result,sizeof(TUniDirRecordHdr));
1373 end;
1374
1375 procedure TIBUniDirectionalCursor.DoCancelUpdates;
1376 begin
1377 if FDataBufferCache <> nil then
1378 FDataBufferCache.Clear;
1379 end;
1380
1381 function TIBUniDirectionalCursor.InternalAllocRecordBuffer: PByte;
1382 begin
1383 Result := GetMem(RecordBufferSize);
1384 if Result = nil then
1385 OutOfMemoryError;
1386 FillChar(Result^,RecordBufferSize,0);
1387 end;
1388
1389 procedure TIBUniDirectionalCursor.InternalFreeRecordBuffer(aBuffer: PByte);
1390 begin
1391 if aBuffer <> nil then
1392 FreeMem(aBuffer);
1393 end;
1394
1395 function TIBUniDirectionalCursor.GetRecord(aBufID: TRecordBuffer;
1396 GetMode: TGetMode; DoCheck: Boolean): TGetResult;
1397 var Buff: PByte;
1398
1399 function GetNext: TGetResult;
1400 begin
1401 if Cursor.IsEOF or not FetchNext then
1402 Result := grEOF
1403 else
1404 begin
1405 FetchCurrentRecord(Buff);
1406 Inc(FRecordCount);
1407 InternalSetRecNo(Buff,FRecordCount);
1408 FCurrentRecord := Buff;
1409 FCurrentRecordStatus := csRowBuffer;
1410 Result := grOK;
1411 end;
1412 end;
1413
1414 begin
1415 Result := grError;
1416 Buff := GetBuffer(aBufID);
1417 if Buff = nil then Exit;
1418
1419 ClearRowCache(Buff);
1420 case GetMode of
1421 gmCurrent:
1422 begin
1423 case FCurrentRecordStatus of
1424 csBOF: ; {do nothing - returns grError}
1425 csEOF:
1426 Result := grEOF;
1427 csRowBuffer:
1428 begin
1429 { if InternalGetDeleted(FCurrentRecord) then
1430 Result := GetNext
1431 else}
1432 if Buff <> FCurrentRecord then
1433 begin
1434 FetchCurrentRecord(Buff);
1435 InternalSetRecNo(Buff,FRecordCount);
1436 FCurrentRecord := Buff;
1437 end;
1438 Result := grOK;
1439 end;
1440 end;
1441 end;
1442
1443 gmNext:
1444 begin
1445 case FCurrentRecordStatus of
1446 csEOF:
1447 Result := grEOF;
1448 csBOF, csRowBuffer:
1449 Result := GetNext;
1450 end;
1451 end;
1452 end;
1453
1454 case Result of
1455 grOK:
1456 SetBookmarkFlag(aBufID,bfCurrent);
1457 grBOF:
1458 SetBookmarkFlag(aBufID,bfBOF);
1459 grEOF:
1460 SetBookmarkFlag(aBufID,bfEOF);
1461 end;
1462
1463 SetBookmarkData(aBufID,InternalGetRecNo(FCurrentRecord));
1464 end;
1465
1466 function TIBUniDirectionalCursor.GetRecNo(aBufID : TRecordBuffer) : TIBRecordNumber;
1467 var Buff: PByte;
1468 begin
1469 Result := 0;
1470 Buff := GetBuffer(aBufID);
1471 if Buff = nil then
1472 IBError(ibxeBufferNotSet, [nil]);
1473 Result := InternalGetRecNo(Buff);
1474 end ;
1475
1476 function TIBUniDirectionalCursor.GetRecordCount: TIBRecordNumber;
1477 begin
1478 Result := FRecordCount - FDeletedRecords;
1479 end;
1480
1481 procedure TIBUniDirectionalCursor.GotoFirst;
1482 begin
1483 if FCurrentRecordStatus <> csBOF then
1484 IBError(ibxeDataSetUniDirectional,[]);
1485 end;
1486
1487 procedure TIBUniDirectionalCursor.GotoLast;
1488 begin
1489 if not Cursor.IsEOF then
1490 while FetchNext do
1491 Inc(FRecordCount);
1492 FCurrentRecord := nil;
1493 FCurrentRecordStatus := csRowBuffer;
1494 end;
1495
1496 function TIBUniDirectionalCursor.GotoRecordNumber(RecNo: TIBRecordNumber
1497 ): boolean;
1498 begin
1499 if not Cursor.IsEOF then
1500 begin
1501 while (FRecordCount < RecNo) and FetchNext do
1502 Inc(FRecordCount);
1503 FCurrentRecord := nil;
1504 FCurrentRecordStatus := csRowBuffer;
1505 end;
1506 Result := FRecordCount = RecNo;
1507 end;
1508
1509 procedure TIBUniDirectionalCursor.EditingDone(aBufID: TRecordBuffer;
1510 UpdateStatus: TCachedUpdateStatus);
1511 var Buff: PByte;
1512 DataBuffer: PByte;
1513 OldBuffer: PByte;
1514 begin
1515 if GetCachedUpdatesEnabled and (FSavedRecNo <> 0) then
1516 begin
1517 Buff := GetBuffer(aBufID);
1518 if Buff = nil then
1519 IBError(ibxeBufferNotSet, [nil]);
1520
1521 OldBuffer := GetOldBufferFor(Buff);
1522 DataBuffer := FDataBufferCache.Append;
1523 CopyBuffers(Buff,DataBuffer); {cache current data buffer}
1524 OldBufferCache.SetDataBuffer(OldBuffer,DataBuffer);
1525 end;
1526 inherited EditingDone(aBufID, UpdateStatus);
1527 FInserting := false;
1528 end;
1529
1530 procedure TIBUniDirectionalCursor.InsertBefore(aBufID: TRecordBuffer);
1531 var Buff: PByte;
1532 begin
1533 SetCurrentRecord(aBufID);
1534
1535 Buff := GetBuffer(aBufID);
1536 if Buff = nil then
1537 IBError(ibxeBufferNotSet, [nil]);
1538 Inc(FRecordCount);
1539 InternalSetRecNo(Buff,FRecordCount);
1540 FCurrentRecord := Buff;
1541 FCurrentRecordStatus := csRowBuffer;
1542 FInserting := true;
1543 Inc(FInsertedRecords);
1544 InternalSetUpdateStatus(FCurrentRecord,usInserted);
1545 DoOnInserted(FCurrentRecord);
1546 end;
1547
1548 procedure TIBUniDirectionalCursor.Append(aBufID: TRecordBuffer);
1549 var Buff: PByte;
1550 begin
1551 Buff := GetBuffer(aBufID);
1552 if Buff = nil then
1553 IBError(ibxeBufferNotSet, [nil]);
1554
1555 GotoLast;
1556 Inc(FRecordCount);
1557 InternalSetRecNo(Buff,FRecordCount);
1558 FCurrentRecord := Buff;
1559 FCurrentRecordStatus := csRowBuffer;
1560 FInserting := true;
1561 Inc(FInsertedRecords);
1562 InternalSetUpdateStatus(FCurrentRecord,usInserted);
1563 DoOnInserted(FCurrentRecord);
1564 end;
1565
1566 procedure TIBUniDirectionalCursor.InternalDelete(aBufID: TRecordBuffer);
1567 begin
1568 inherited InternalDelete(aBufID);
1569 SetCurrentRecord(aBufID);
1570
1571 GetRecord(aBufID,gmNext,false);
1572 Inc(FDeletedRecords);
1573 end;
1574
1575 procedure TIBUniDirectionalCursor.InternalUnDelete(aBuffer: PByte);
1576 begin
1577 inherited InternalUnDelete(aBuffer);
1578 Dec(FDeletedRecords);
1579 end;
1580
1581 procedure TIBUniDirectionalCursor.Reset;
1582 begin
1583 inherited Reset;
1584 FRecordCount := 0;
1585 FInserting := false;
1586 FInsertedRecords := 0;
1587 FDeletedRecords := 0;
1588 if FDataBufferCache <> nil then
1589 FDataBufferCache.Clear;
1590 end;
1591
1592 function TIBUniDirectionalCursor.GetInsertedRecords: integer;
1593 begin
1594 Result := FInsertedRecords
1595 end;
1596
1597 function TIBUniDirectionalCursor.GetDeletedRecords: integer;
1598 begin
1599 Result := FDeletedRecords;
1600 end;
1601
1602 procedure TIBUniDirectionalCursor.InitRecord(aBufID: TRecordBuffer);
1603 var Buff: PByte;
1604 begin
1605 inherited InitRecord(aBufID);
1606 Buff := GetBuffer(aBufID);
1607 if Buff = nil then
1608 IBError(ibxeBufferNotSet, [nil]);
1609 ClearRowCache(Buff);
1610 Fillchar(Buff^,RecordBufferSize,0);
1611 InternalSetUpdateStatus(Buff,usInserted);
1612 end;
1613
1614
1615 { TIBSelectCursor.TIBArray }
1616
1617 procedure TIBSelectCursor.TIBArray.EventHandler(Sender: IArray;
1618 Reason: TArrayEventReason);
1619 begin
1620 case Reason of
1621 arChanging:
1622 if FRecNo <> FField.Dataset.RecNo then
1623 IBError(ibxeNotCurrentArray,[nil]);
1624
1625 arChanged:
1626 THackedField(FField).DataChanged;
1627 end;
1628 end;
1629
1630 constructor TIBSelectCursor.TIBArray.Create(aField: TField; anArray: IArray);
1631 begin
1632 inherited Create;
1633 FField := aField;
1634 FArray := anArray;
1635 FRecNo := FField.Dataset.RecNo;
1636 FArray.AddEventHandler(EventHandler);
1637 end;
1638
1639 destructor TIBSelectCursor.TIBArray.Destroy;
1640 begin
1641 FArray.RemoveEventHandler(EventHandler);
1642 inherited Destroy;
1643 end;
1644
1645 { TIBSelectCursor }
1646
1647 {
1648 A record buffer is structured into
1649
1650 1. Space for each field's column data determined from the metadate datasize.
1651 Note: for VarChar the column data is sizeof(short) longer to allow for a length
1652 indicator.
1653 2. Buffer Header for per record local data
1654 3. a little endian bitmap giving each column's null status. 0 => null, 1 => not null
1655 4. Another bitmap to keep track of each colmn's refresh required status.
1656 5. Additional space for the blob and array caches,
1657
1658 The Column Metadata and Field No. mapping is also established. The interesting
1659 metadata is copied from the IStatement into the FColumnMetadata dynamic array, and which
1660 also includes the SQL Column Index. This latter mapping is necessary because an
1661 IStatement column is ignored if it is not reference from the list of fields, or
1662 it's aliasname is not the IBX specical field sDBkeyAlias (used by TIBTable).
1663
1664 Likewise a mapping between the Field.FieldNo (a 1 -based sequence nu. and the
1665 corresponding index into the 0 -based FColumnMetadata dynamic array. This is
1666 not just because of the different base, but because the use of IBX special field can
1667 result in and FColumnMetadata that is not linked to a TField.
1668 }
1669
1670 procedure TIBSelectCursor.SetupBufferStructure(metadata: IMetadata;
1671 aFields: TFields; aMaxFieldNo: integer);
1672 var i{,j}: integer;
1673 colMetadata: IColumnMetaData;
1674 field: TField;
1675 ColUsed: boolean;
1676 ColMetaDataIndex: integer;
1677 RecordSize: integer;
1678 begin
1679 FArrayFieldCount := 0;
1680 FBlobFieldCount := 0;
1681 FDBKeyFieldColumn := -1;
1682 ColMetaDataIndex := 0;
1683 SetLength(FFieldNo2ColumnMap,aMaxFieldNo+1); {Note: FieldNos are 1-based - index 0 is not used}
1684
1685 { Initialize offsets, buffer sizes, etc... }
1686 FColumnCount := metadata.Count;
1687 SetLength(FColumnMetadata,FColumnCount);
1688 RecordSize := CalcRecordHdrSize;
1689
1690 {Now determine how much space needs to be reserved for each column and column metadata}
1691 for i := 0 to FColumnCount - 1 do
1692 with FColumnMetadata[ColMetaDataIndex] do
1693 begin
1694 fdSQLColIndex := i;
1695 colMetadata := metadata[i];
1696 ColUsed := false;
1697
1698 if colMetadata.GetAliasName = sDBkeyAlias then {special case for TIBTable support}
1699 begin
1700 FDBKeyFieldColumn := ColMetaDataIndex;
1701 ColUsed := true;
1702 end;
1703
1704 field := aFields.FindField(colMetadata.GetAliasName);
1705 if field <> nil then
1706 begin
1707 { if (field.FieldNo < 0) or (field.FieldNo >= Length(FFieldNo2ColumnMap)) then
1708 begin
1709 writeln('Map -size = ',Length(FFieldNo2ColumnMap),' Field Count = ',aFields.Count);
1710 for j := 1 to aFields.Count do
1711 writeln('Field: ',aFields[j].FieldName,', No = ',aFields[j].FieldNo);
1712 end;}
1713 FFieldNo2ColumnMap[field.FieldNo] := ColMetaDataIndex;
1714 Colused := true;
1715 fdRefreshOnInsert := (pfRefreshOnInsert in field.ProviderFlags) or
1716 (TIBFieldDef(field.FieldDef).IdentityColumn) or
1717 field.FieldDef.InternalCalcField;
1718 fdRefreshOnUpdate := (pfRefreshOnUpdate in field.ProviderFlags) or
1719 field.FieldDef.InternalCalcField;
1720 end;
1721
1722 if not Colused then continue;
1723
1724 fdDataType := colMetadata.GetSQLType;
1725 if fdDataType = SQL_BLOB then
1726 fdDataScale := 0
1727 else
1728 fdDataScale := colMetadata.getScale;
1729 fdNullable := colMetadata.getIsNullable;
1730 fdDataSize := colMetadata.GetSize;
1731 fdCodePage := CP_NONE;
1732 fdAliasName := colMetadata.GetAliasName;
1733
1734 case fdDataType of
1735 SQL_TIMESTAMP,
1736 SQL_TYPE_DATE,
1737 SQL_TYPE_TIME:
1738 fdDataSize := SizeOf(TDateTime);
1739 SQL_TIMESTAMP_TZ,
1740 SQL_TIMESTAMP_TZ_EX,
1741 SQL_TIME_TZ,
1742 SQL_TIME_TZ_EX:
1743 fdDataSize := SizeOf(TIBBufferedDateTimeWithTimeZone);
1744 SQL_SHORT:
1745 begin
1746 if (fdDataScale = 0) then
1747 fdDataSize := SizeOf(short)
1748 else
1749 if (fdDataScale >= (-4)) then
1750 fdDataSize := SizeOf(Currency)
1751 else
1752 fdDataSize := SizeOf(Double);
1753 end;
1754 SQL_LONG:
1755 begin
1756 if (fdDataScale = 0) then
1757 fdDataSize := SizeOf(Integer)
1758 else
1759 if (fdDataScale >= (-4)) then
1760 fdDataSize := SizeOf(Currency)
1761 else
1762 fdDataSize := SizeOf(Double);
1763 end;
1764 SQL_INT64:
1765 begin
1766 if (fdDataScale = 0) then
1767 fdDataSize := SizeOf(Int64)
1768 else
1769 if (fdDataScale >= (-4)) then
1770 fdDataSize := SizeOf(Currency)
1771 else
1772 fdDataSize := SizeOf(Double);
1773 end;
1774 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1775 fdDataSize := SizeOf(Double);
1776 SQL_BOOLEAN:
1777 fdDataSize := SizeOf(wordBool);
1778 SQL_VARYING,
1779 SQL_TEXT:
1780 fdCodePage := colMetadata.getCodePage;
1781 SQL_BLOB:
1782 begin
1783 Inc(FBlobFieldCount);
1784 fdCodePage := colMetadata.getCodePage;
1785 end;
1786 SQL_DEC16,
1787 SQL_DEC34,
1788 SQL_DEC_FIXED,
1789 SQL_INT128:
1790 fdDataSize := sizeof(tBCD);
1791 SQL_Array:
1792 Inc(FArrayFieldCount);
1793 end;
1794 fdDataOfs := RecordSize;
1795 if fdDataType = SQL_VARYING then
1796 Inc(RecordSize, fdDataSize + sizeof(short))
1797 else
1798 Inc(RecordSize, fdDataSize);
1799 Inc(ColMetaDataIndex);
1800 end;
1801
1802 FColumnCount := ColMetaDataIndex; {set to number of columns in use}
1803
1804 {Reserve space for null column bitmap}
1805 FNullColBitmapOffset := RecordSize;
1806 Inc(RecordSize, ((FColumnCount - 1) div 8) + 1);
1807
1808 {Reserve space for Refresh Required bit map}
1809 FRefreshRequiredBitmapOffset := RecordSize;
1810 FRefreshRequiredSize := ((FColumnCount - 1) div 8) + 1;
1811 Inc(RecordSize, FRefreshRequiredSize);
1812 FSaveBufferSize := RecordSize;
1813
1814 {Reserve space for Blob Objects}
1815 if FBlobFieldCount > 0 then
1816 for i := 0 to FColumnCount - 1 do
1817 with FColumnMetadata[i] do
1818 begin
1819 if fdDataType = SQL_BLOB then
1820 begin
1821 fdObjOffset := RecordSize;
1822 Inc(RecordSize,sizeof(TIBBlobStream));
1823 end;
1824 end;
1825
1826 {Reserve space for array objects}
1827 if FArrayFieldCount > 0 then
1828 for i := 0 to FColumnCount - 1 do
1829 with FColumnMetadata[i] do
1830 begin
1831 if fdDataType = SQL_ARRAY then
1832 begin
1833 fdObjOffset := RecordSize;
1834 Inc(RecordSize,sizeof(TIBArray));
1835 end;
1836 end;
1837
1838 {FRecordBufferSize is how much space needs to be reserved}
1839 FRecordBufferSize := RecordSize;
1840 end;
1841
1842 function TIBSelectCursor.GetSQLParams : ISQLParams;
1843 begin
1844 Result := FCursor.GetStatement.SQLParams;
1845 end;
1846
1847 function TIBSelectCursor.GetBuffer(aBufID: TRecordBuffer): PByte;
1848 begin
1849 Result := PDisplayBuffer(aBufID)^.dbBuffer;
1850 end;
1851
1852 procedure TIBSelectCursor.SetBuffer(aBufID: TRecordBuffer; aBuffer: PByte);
1853 begin
1854 PDisplayBuffer(aBufID)^.dbBuffer := aBuffer;
1855 end;
1856
1857 function TIBSelectCursor.GetCalcFields(aBufID: TRecordBuffer): PByte;
1858 begin
1859 Result := PDisplayBuffer(aBufID)^.dbCalcFields;
1860 end;
1861
1862 function TIBSelectCursor.FieldNo2ColumnIndex(aField: TField): integer;
1863 begin
1864 if (aField.FieldNo < 1) or (aField.FieldNo > Length(FFieldNo2ColumnMap)) then
1865 IBError(ibxeBadFieldNo,[aField.FieldNo, Length(FFieldNo2ColumnMap)-1]);
1866
1867 Result := FFieldNo2ColumnMap[aField.FieldNo];
1868 end;
1869
1870 function TIBSelectCursor.InternalGetUpdateStatus(aBuffer: PByte): TUpdateStatus;
1871 begin
1872 Result := PRecordHeader(aBuffer)^.rhUpdateStatus;
1873 end;
1874
1875 procedure TIBSelectCursor.InternalSetUpdateStatus(aBuffer: PByte;
1876 status: TUpdateStatus);
1877 begin
1878 PRecordHeader(aBuffer)^.rhUpdateStatus:= status;
1879 end;
1880
1881 procedure TIBSelectCursor.SetUpdateStatus(aBufID: TRecordBuffer;
1882 status: TUpdateStatus);
1883 var Buff: PByte;
1884 begin
1885 Buff := GetBuffer(aBufID);
1886 if Buff = nil then
1887 IBError(ibxeBufferNotSet, [nil]);
1888
1889 InternalSetUpdateStatus(Buff,status);
1890 end;
1891
1892 procedure TIBSelectCursor.Reset;
1893 begin
1894 FRecordCount := 0;
1895 ClearBlobCache;
1896 ClearArrayCache;
1897 FCursor := nil;
1898 FCurrentRecord := nil;
1899 FCurrentRecordStatus := csBOF;
1900 end;
1901
1902 function TIBSelectCursor.FetchNext: boolean;
1903 begin
1904 Result := Cursor.FetchNext;
1905 if not (csDesigning in Dataset.ComponentState) then
1906 MonitorHook.SQLFetch(self,Cursor.GetStatement.GetSQLText);
1907 end;
1908
1909 function TIBSelectCursor.NormaliseParamName(aName : AnsiString;
1910 var UseOldValue : boolean) : AnsiString;
1911 const
1912 sOldPrefix = 'OLD_';
1913 sNewPrefix = 'NEW';
1914 begin
1915 UseOldValue := false;
1916 Result := aName;
1917 if pos(sOldPrefix,Result) = 1 then
1918 begin
1919 system.Delete(Result,1,length(sOldPrefix));
1920 UseOldValue := true;
1921 end
1922 else
1923 begin
1924 if pos(sNewPrefix,Result) = 1 then
1925 system.Delete(Result,1,length(sNewPrefix));
1926 end;
1927 end ;
1928
1929 procedure TIBSelectCursor.ClearRegisteredQueries;
1930 var i: TRegisteredQueryTypes;
1931 begin
1932 for i := low(FRegisteredQueries) to high(FRegisteredQueries) do
1933 with FRegisteredQueries[i] do
1934 begin
1935 stmt := nil;
1936 SetLength(ParamMap,0);
1937 SetLength(UseOldValue,0);
1938 SetLength(ColMap,0);
1939 end;
1940 end ;
1941
1942 procedure TIBSelectCursor.ClearBlobCache;
1943 var i: Integer;
1944 begin
1945 for i := 0 to FBlobStreamList.Count - 1 do
1946 begin
1947 TIBBlobStream(FBlobStreamList[i]).Free;
1948 FBlobStreamList[i] := nil;
1949 end;
1950 FBlobStreamList.Pack;
1951 end;
1952
1953 procedure TIBSelectCursor.ClearArrayCache;
1954 var i: Integer;
1955 begin
1956 for i := 0 to FArrayList.Count - 1 do
1957 begin
1958 TIBArray(FArrayList[i]).Free;
1959 FArrayList[i] := nil;
1960 end;
1961 FArrayList.Pack;
1962 end;
1963
1964 procedure TIBSelectCursor.ClearRowCache(aBuffer: PByte);
1965 var i: Integer;
1966 begin
1967 for i := 0 to FColumnCount - 1 do
1968 with FColumnMetaData[i] do
1969 case fdDataType of
1970 SQL_BLOB:
1971 PIBBlobStream(aBuffer + fdObjOffset)^ := nil;
1972 SQL_ARRAY:
1973 PIBArray(aBuffer + fdObjOffset)^ := nil;
1974 end;
1975 end;
1976
1977 procedure TIBSelectCursor.CopyCursorDataToBuffer(QryResults: IResults; QryIndex,
1978 ColIndex: integer; destBuff: PByte);
1979 var LocalData: PByte;
1980 ColData: ISQLData;
1981 IsNull: boolean;
1982 DataLength: Short;
1983 BufPtr: PByte;
1984 begin
1985 QryResults.GetData(QryIndex,IsNull,DataLength,LocalData);
1986 with FColumnMetaData[ColIndex] do
1987 begin
1988 InternalSetIsNull(destBuff,ColIndex,IsNull);
1989 BufPtr := destBuff + fdDataOfs;
1990 if IsNull then
1991 FillChar(BufPtr^,fdDataSize,0)
1992 else
1993 begin
1994 ColData := QryResults[QryIndex];
1995 case fdDataType of {Get Formatted data for column types that need formatting}
1996 SQL_TYPE_DATE,
1997 SQL_TYPE_TIME,
1998 SQL_TIMESTAMP:
1999 {This is an IBX native format and not the TDataset approach. See also GetFieldData}
2000 PDateTime(BufPtr)^ := ColData.AsDateTime;
2001
2002 SQL_TIMESTAMP_TZ,
2003 SQL_TIMESTAMP_TZ_EX:
2004 begin
2005 with PIBBufferedDateTimeWithTimeZone(Bufptr)^ do
2006 ColData.GetAsDateTime(Timestamp,dstOffset,TimeZoneID);
2007 end;
2008
2009 SQL_TIME_TZ,
2010 SQL_TIME_TZ_EX:
2011 begin
2012 with PIBBufferedDateTimeWithTimeZone(Bufptr)^ do
2013 ColData.GetAsTime(Timestamp, dstOffset,TimeZoneID, FDefaultTZDate);
2014 end;
2015 SQL_SHORT:
2016 begin
2017 if (fdDataScale = 0) then
2018 PShort(BufPtr)^ := ColData.AsShort
2019 else
2020 if (fdDataScale >= (-4)) then
2021 PCurrency(BufPtr)^ := ColData.AsCurrency
2022 else
2023 PDouble(BufPtr)^ := ColData.AsDouble;
2024 end;
2025 SQL_LONG:
2026 begin
2027 if (fdDataScale = 0) then
2028 PLong(BufPtr)^ := ColData.AsLong
2029 else
2030 if (fdDataScale >= (-4)) then
2031 PCurrency(BufPtr)^ := ColData.AsCurrency
2032 else
2033 PDouble(BufPtr)^ := ColData.AsDouble;
2034 end;
2035 SQL_INT64:
2036 begin
2037 if (fdDataScale = 0) then
2038 PInt64(BufPtr)^ := ColData.AsInt64
2039 else
2040 if (fdDataScale >= (-4)) then
2041 PCurrency(BufPtr)^ := ColData.AsCurrency
2042 else
2043 PDouble(BufPtr)^ := ColData.AsDouble;
2044 end;
2045
2046 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2047 PDouble(BufPtr)^ := ColData.AsDouble;
2048
2049 SQL_BOOLEAN:
2050 system.PBoolean(BufPtr)^ := ColData.AsBoolean;
2051
2052 SQL_DEC16,
2053 SQL_DEC34,
2054 SQL_DEC_FIXED,
2055 SQL_INT128:
2056 pBCD(BufPtr)^ := ColData.GetAsBCD;
2057
2058 else
2059 begin
2060 if fdDataType = SQL_VARYING then
2061 begin
2062 PShort(BufPtr)^ := DataLength;
2063 Move(LocalData^, (BufPtr + sizeof(Short))^, DataLength);
2064 end
2065 else
2066 Move(LocalData^, BufPtr^, fdDataSize)
2067 end;
2068 end; {case}
2069 end;
2070 end;
2071 end;
2072
2073 function TIBSelectCursor.InternalGetIsNull(Buff: PByte; ColIndex: integer
2074 ): boolean;
2075 var pBitmap: PByte;
2076 mask: byte;
2077 begin
2078 pBitmap := Buff + FNullColBitmapOffset + ColIndex div 8;
2079 mask := $01 shl (ColIndex mod 8);
2080 Result := (pBitmap^ and mask) = 0; {bit is 0 => null}
2081 end;
2082
2083 procedure TIBSelectCursor.InternalSetIsNull(Buff: PByte; ColIndex: integer;
2084 IsNull: boolean);
2085 var pBitmap: PByte;
2086 mask: byte;
2087 begin
2088 pBitmap := Buff + FNullColBitmapOffset + ColIndex div 8;
2089 mask := $01 shl (ColIndex mod 8);
2090 if IsNull then
2091 pBitmap^ := pBitmap^ and not mask {unset bit}
2092 else
2093 pBitmap^ := pBitmap^ or mask; {set bit}
2094 end;
2095
2096 procedure TIBSelectCursor.SetRefreshRequired(Buff: PByte; ColIndex: integer;
2097 RefreshRequired: boolean);
2098 var pBitmap: PByte;
2099 mask: byte;
2100 begin
2101 pBitmap := Buff + FRefreshRequiredBitmapOffset + ColIndex div 8;
2102 mask := $01 shl (ColIndex mod 8);
2103 if RefreshRequired then
2104 pBitmap^ := pBitmap^ or mask {set bit}
2105 else
2106 pBitmap^ := pBitmap^ and not mask; {unset bit}
2107 end;
2108
2109 procedure TIBSelectCursor.SaveBlobsAndArrays(Buff: PByte);
2110 var pdb: PIBBlobStream;
2111 pda: PIBArray;
2112 i: integer;
2113 begin
2114 for i := 0 to FColumnCount - 1 do
2115 with FColumnMetadata[i] do
2116 begin
2117 case fdDataType of
2118 SQL_BLOB:
2119 begin
2120 pdb := PIBBlobStream(Buff + fdObjOffset);
2121 if pdb^ <> nil then
2122 begin
2123 pdb^.Finalize;
2124 PISC_QUAD(Buff + fdDataOfs)^ := pdb^.BlobID;
2125 InternalSetIsNull(Buff,i, pdb^.Size = 0);
2126 SetRefreshRequired(Buff,i,true);
2127 end
2128 end;
2129
2130 SQL_ARRAY:
2131 begin
2132 pda := PIBArray(Buff + fdObjOffset);
2133 if pda^ <> nil then
2134 begin
2135 PISC_QUAD(Buff + fdDataOfs)^ := pda^.ArrayIntf.GetArrayID;
2136 InternalSetIsNull(Buff,i, pda^.ArrayIntf.IsEmpty);
2137 SetRefreshRequired(Buff,i,true);
2138 end
2139 end;
2140 end;
2141 end;
2142 end;
2143
2144 function TIBSelectCursor.CalcRecordHdrSize: integer;
2145 begin
2146 Result := sizeof(TRecordHeader);
2147 end;
2148
2149 function TIBSelectCursor.ColIndexByName(aName: AnsiString; caseSensitive: boolean
2150 ): integer;
2151 var i: integer;
2152 begin
2153 Result := -1;
2154 if caseSensitive then
2155 for i := 0 to FColumnCount - 1 do
2156 begin
2157 if FColumnMetaData[i].fdAliasName = aName then
2158 begin
2159 Result := i;
2160 Exit;
2161 end
2162 end
2163 else
2164 begin
2165 aName := AnsiUpperCase(aName);
2166 for i := 0 to FColumnCount - 1 do
2167 if AnsiUpperCase(FColumnMetaData[i].fdAliasName) = aName then
2168 begin
2169 Result := i;
2170 Exit;
2171 end
2172 end;
2173 end;
2174
2175 procedure TIBSelectCursor.FetchCurrentRecord(destBuffer: PByte);
2176 var i: Integer;
2177 begin
2178 { Make sure blob and array caches are empty }
2179 ClearRowCache(destBuffer);
2180
2181 if Cursor.IsEOF then
2182 IBError(ibxeCursorAtEOF,[]);
2183
2184 for i := 0 to FColumnCount - 1 do
2185 CopyCursorDataToBuffer(FCursor,FColumnMetaData[i].fdSQLColIndex,i,destBuffer);
2186 InternalSetUpdateStatus(destBuffer,usUnModified);
2187 end;
2188
2189 procedure TIBSelectCursor.FieldChanged(aBuffer: PByte; aField: TField);
2190 begin
2191 THackedField(aField).DataChanged;
2192 end;
2193
2194 constructor TIBSelectCursor.Create(aDataset: TDataset; aName: string;
2195 aCursor: IResultSet; aFields: TFields; aMaxFieldNo: integer;
2196 aCalcFieldsSize: integer; aDefaultTZDate: TDateTime);
2197 begin
2198 inherited Create;
2199 FBlobStreamList := TList.Create;
2200 FArrayList := TList.Create;
2201 FDataset := aDataset;
2202 FName := aName;
2203 FCursor := aCursor;
2204 FCalcFieldsSize := aCalcFieldsSize;
2205 FDefaultTZDate := aDefaultTZDate;
2206 SetupBufferStructure(cursor.GetStatement.MetaData,aFields,aMaxFieldNo);
2207 FCurrentRecord := nil;
2208 FCurrentRecordStatus := csBOF;
2209 ClearRegisteredQueries;
2210 end;
2211
2212 destructor TIBSelectCursor.Destroy;
2213 begin
2214 ClearBlobCache;
2215 ClearArrayCache;
2216 FBlobStreamList.Free;
2217 FArrayList.Free;
2218 SetLength(FColumnMetadata,0);
2219 ClearRegisteredQueries;
2220 inherited Destroy;
2221 end;
2222
2223 {Note bufferindex starts at one to avoid confusion with a nil pointer.
2224 The "buffer" is an opaque pointer - actually an integer index to the
2225 FBuffers array.}
2226
2227 function TIBSelectCursor.AllocRecordBuffer: TRecordBuffer;
2228 begin
2229 Result := GetMem(sizeof(TDisplayBuffer));;
2230 if Result = nil then
2231 OutofMemoryError;
2232 with PDisplayBuffer(Result)^ do
2233 begin
2234 dbBookmarkFlag := bfCurrent;
2235 FillChar(dbBookmarkData,sizeof(dbBookmarkData),0);
2236 dbBuffer := InternalAllocRecordBuffer;
2237 dbCalcFields := GetMem(FCalcFieldsSize);
2238 if dbCalcFields = nil then
2239 OutOfMemoryError;
2240 end;
2241 end;
2242
2243 procedure TIBSelectCursor.FreeRecordBuffer(var Buffer: TRecordBuffer);
2244 begin
2245 if Buffer <> nil then
2246 begin
2247 with PDisplayBuffer(Buffer)^ do
2248 begin
2249 InternalFreeRecordBuffer(dbBuffer);
2250 FreeMem(dbCalcFields);
2251 end;
2252 FreeMem(Buffer);
2253 end;
2254 Buffer := nil;
2255 end;
2256
2257 procedure TIBSelectCursor.ClearRecordBuffer(Buffer: TRecordBuffer);
2258 begin
2259 if Buffer <> nil then
2260 begin
2261 with PDisplayBuffer(Buffer)^ do
2262 begin
2263 InternalFreeRecordBuffer(dbBuffer);
2264 dbBuffer := InternalAllocRecordBuffer;
2265 end;
2266 ClearCalcFields(Buffer);
2267 end;
2268 end;
2269
2270 procedure TIBSelectCursor.SetCurrentRecord(aBufID: TRecordBuffer);
2271 var Buff: PByte;
2272
2273 procedure SetBuffer;
2274 begin
2275 Buff := GetBuffer(aBufID);
2276 if Buff = nil then
2277 IBError(ibxeBufferNotSet, [nil]);
2278 FCurrentRecord := Buff;
2279 FCurrentRecordStatus := csRowBuffer;
2280 end;
2281
2282 begin
2283 case GetBookmarkFlag(aBufID) of
2284 bfBOF:
2285 FCurrentRecordStatus := csBOF;
2286 bfEOF:
2287 if FEditState = esInsert then
2288 SetBuffer
2289 else
2290 FCurrentRecordStatus := csEOF;
2291 else
2292 SetBuffer;
2293 end;
2294 end;
2295
2296 {Field.offset is a zero based integer indexing the blob field
2297 Field.FieldNo is a one based field index accross all of a record's fields}
2298
2299 function TIBSelectCursor.CreateBlobStream(aBufID: TRecordBuffer; Field: TField;
2300 Mode: TBlobStreamMode): TStream;
2301 var pdb: PIBBlobStream;
2302 fs: TIBBlobStream;
2303 Buff: PByte;
2304 ColMetadata: TColumnMetadata;
2305 begin
2306 Buff := GetBuffer(aBufID);
2307 if Buff = nil then
2308 begin
2309 fs := TIBBlobStream.Create;
2310 fs.Mode := bmReadWrite;
2311 fs.Database := (Field.Dataset as TIBCustomDataset).Database;
2312 fs.Transaction := (Field.Dataset as TIBCustomDataset).Transaction;
2313 fs.SetField(Field);
2314 FBlobStreamList.Add(Pointer(fs));
2315 end
2316 else
2317 begin
2318 ColMetadata := FColumnMetaData[FieldNo2ColumnIndex(Field)];
2319 pdb := PIBBlobStream(Buff + ColMetaData.fdObjOffset);
2320 if pdb^ = nil then {not yet assigned}
2321 begin
2322 fs := TIBBlobStream.Create;;
2323 fs.Mode := bmReadWrite;
2324 fs.Database := (Field.Dataset as TIBCustomDataset).Database;
2325 fs.Transaction := (Field.Dataset as TIBCustomDataset).Transaction;
2326 fs.SetField(Field);
2327 fs.BlobID := PISC_QUAD(Buff + ColMetaData.fdDataOfs)^;
2328 pdb^ := fs;
2329 FBlobStreamList.Add(Pointer(fs));
2330 end
2331 else
2332 fs := pdb^;
2333 end;
2334 Result := TIBDSBlobStream.Create(Field, fs, Mode);
2335 end;
2336
2337 function TIBSelectCursor.GetArray(aBufID: TRecordBuffer; Field: TField
2338 ): IArray;
2339 var Buff: PByte;
2340 pda: PIBArray;
2341 ColIndex: integer;
2342 ColMetadata: TColumnMetadata;
2343 ar: TIBArray;
2344 begin
2345 Buff := GetBuffer(aBufID);
2346 with Field.Dataset as TIBCustomDataset do
2347 begin
2348 if Buff = nil then
2349 Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
2350 (Field as TIBArrayField).RelationName,Field.FieldName)
2351 else
2352 begin
2353 ColIndex := FieldNo2ColumnIndex(Field);
2354 ColMetadata := FColumnMetaData[ColIndex];
2355 pda := PIBArray(Buff + ColMetadata.fdObjOffset);
2356 if pda^ = nil then
2357 begin
2358 if InternalGetIsNull(Buff,ColIndex) then
2359 Result := Database.Attachment.CreateArray(Transaction.TransactionIntf,
2360 (Field as TIBArrayField).RelationName,Field.FieldName)
2361 else
2362 Result := Database.Attachment.OpenArray(Transaction.TransactionIntf,
2363 (Field as TIBArrayField).RelationName,Field.FieldName,
2364 PISC_QUAD(Buff + ColMetaData.fdDataOfs)^);
2365 ar := TIBArray.Create(Field,Result);
2366 pda^ := ar;
2367 FArrayList.Add(ar);
2368 end
2369 else
2370 Result := pda^.ArrayIntf;
2371 end;
2372 end;
2373 end;
2374
2375 procedure TIBSelectCursor.SetArrayIntf(aBufID: TRecordBuffer; AnArray: IArray;
2376 Field: TField);
2377 var Buff: PByte;
2378 pda: PIBArray;
2379 ColIndex: integer;
2380 ColMetadata: TColumnMetadata;
2381 ar: TIBArray;
2382 IsNull: boolean;
2383 begin
2384 Buff := GetBuffer(aBufID);
2385 if Buff <> nil then
2386 begin
2387 ColIndex := FieldNo2ColumnIndex(Field);
2388 ColMetadata := FColumnMetaData[ColIndex];
2389 IsNull := AnArray = nil;
2390 InternalSetIsNull(Buff,ColIndex,IsNull);
2391 pda := PIBArray(Buff + ColMetaData.fdObjOffset);
2392 if pda^ = nil then
2393 begin
2394 if not IsNull then
2395 begin
2396 ar := TIBArray.Create(Field,AnArray);
2397 pda^ := ar;
2398 FArrayList.Add(ar);
2399 end
2400 else
2401 pda^.FArray := AnArray;
2402 end;
2403 FieldChanged(Buff,Field);
2404 end;
2405 end;
2406
2407 function TIBSelectCursor.GetRecDBkey(aBufID: TRecordBuffer): TIBDBKey;
2408 var Buff: PByte;
2409 begin
2410 FillChar(Result,sizeof(TIBDBKey),0);
2411 if FDBKeyFieldColumn >= 0 then
2412 with FColumnMetaData[FDBKeyFieldColumn] do
2413 begin
2414 Buff := GetBuffer(aBufID);
2415 if (Buff <> nil) and (fdDataSize <= 8) then
2416 Result := PIBDBKEY(Buff + fdDataOfs)^;
2417 end;
2418 end;
2419
2420 function TIBSelectCursor.GetFieldData(aBufID: TRecordBuffer; field: TField;
2421 outBuffer: PByte): boolean;
2422 var Buff: PByte;
2423 ColIndex: integer;
2424 Data: PByte;
2425 len: Short;
2426 begin
2427 Result := false;
2428 if aBufID = nil then Exit;
2429 if field.FieldNo < 0 then {Calculated Field}
2430 begin
2431 Buff := GetCalcFields(aBufID);
2432 if Buff = nil then Exit;
2433
2434 Inc(Buff, field.Offset); {For CalcFields, TField.offset is the buffer offset}
2435 Result := not Boolean(Buff[0]);
2436
2437 if Result and (outBuffer <> nil) then
2438 Move(Buff[1], outBuffer^, field.DataSize);
2439 end
2440 else
2441 begin
2442 Buff := GetBuffer(aBufID);
2443 if Buff = nil then Exit;
2444
2445 ColIndex := FieldNo2ColumnIndex(field);
2446 Result := not InternalGetIsNull(Buff,ColIndex);
2447 if Result and (outBuffer <> nil) then
2448 with FColumnMetaData[ColIndex] do
2449 begin
2450 Data := Buff + fdDataOfs;
2451 if fdDataType = SQL_VARYING then
2452 begin
2453 len := PShort(Data)^;
2454 if len <= field.DataSize then
2455 begin
2456 Inc(Data,sizeof(short));
2457 Move(Data^, outBuffer^, len);
2458 PAnsiChar(outBuffer)[len] := #0;
2459 end
2460 else
2461 IBError(ibxeFieldSizeError,[field.FieldName,field.DataSize,len])
2462 end
2463 else
2464 if fdDataSize <= Field.DataSize then
2465 Move(Data^, outBuffer^, fdDataSize)
2466 else
2467 IBError(ibxeFieldSizeError,[field.FieldName,field.DataSize,fdDataSize])
2468 end;
2469 end;
2470 end;
2471
2472 procedure TIBSelectCursor.SetFieldData(aBufID: TRecordBuffer; field: TField;
2473 inBuffer: PByte);
2474 var Buff: PByte;
2475 ColIndex: integer;
2476 Data: PByte;
2477 DataSize: Short;
2478 len: Short;
2479 IsNull: boolean;
2480 begin
2481 if field.FieldNo < 0 then {calaculated field}
2482 begin
2483 Buff := GetCalcFields(aBufID);
2484 if Buff = nil then Exit;
2485
2486 Inc(Buff, field.Offset); {For CalcFields, TField.offset is the buffer offset}
2487 IsNull := inBuffer = nil;
2488 Boolean(Buff[0]) := IsNull;
2489 if not IsNull then
2490 Move(inBuffer^, Buff[1], Field.DataSize);
2491 end
2492 else
2493 begin
2494 Buff := GetBuffer(aBufID);
2495 if Buff = nil then Exit;
2496
2497 ColIndex := FieldNo2ColumnIndex(field);
2498 Field.Validate(inBuffer);
2499 IsNull := (inBuffer = nil) or
2500 (Field is TStringField) and (PAnsiChar(inBuffer)^ = #0);
2501 InternalSetIsNull(Buff,Colindex,IsNull);
2502 if not IsNull then
2503 with FColumnMetadata[ColIndex] do
2504 begin
2505 Data := Buff + fdDataOfs;
2506 DataSize := fdDataSize;
2507 FillChar(Data^,DataSize,0);
2508 if fdDataType = SQL_VARYING then
2509 begin
2510 len := StrLen(PAnsiChar(inBuffer));
2511 PShort(Data)^ := len;
2512 Inc(Data,sizeof(Short));
2513 end;
2514
2515 if DataSize >= field.DataSize then
2516 Move(inBuffer^, Data^,DataSize)
2517 else
2518 IBError(ibxeDBBufferTooSmall,[DataSize,field.FieldName,field.DataSize]);
2519
2520 FieldChanged(Buff,field);
2521 end;
2522 end;
2523 end;
2524
2525 procedure TIBSelectCursor.SetSQLParams(aBufID: TRecordBuffer; params: ISQLParams);
2526 var Buff: PByte;
2527 OldBuffer: PByte;
2528 i: integer;
2529 Param: ISQLParam;
2530 ParamName: AnsiString;
2531 srcBuffer: PByte;
2532 ColIndex: integer;
2533 UseOldValue: boolean;
2534 begin
2535 Buff := GetBuffer(aBufID);
2536 if Buff = nil then
2537 IBError(ibxeBufferNotSet, [nil]);
2538
2539 SaveBlobsAndArrays(Buff);
2540
2541 OldBuffer := GetOldBufferFor(Buff);
2542 for i := 0 to Params.GetCount - 1 do
2543 begin
2544 Param := params[i];
2545 ParamName := NormaliseParamName(Param.Name,UseOldValue);
2546
2547 {Determine source buffer}
2548 if UseOldValue and (OldBuffer <> nil) then
2549 srcBuffer := OldBuffer
2550 else
2551 srcBuffer := Buff;
2552
2553 ColIndex := ColIndexByName(ParamName,params.GetHasCaseSensitiveParams);
2554 if ColIndex = -1 then
2555 continue;
2556
2557 SetParamValue(srcBuffer,ColIndex,Param);
2558 end;
2559 end;
2560
2561 {This method is called either using a Row Refresh query or an update/insert query
2562 with a returning clause}
2563
2564 procedure TIBSelectCursor.UpdateRecordFromQuery(aBufID: TRecordBuffer;
2565 QryResults: IResults);
2566 var Buff: PByte;
2567 ColIndex: integer;
2568 i: integer;
2569 begin
2570 Buff := GetBuffer(aBufID);
2571 if Buff = nil then
2572 IBError(ibxeBufferNotSet, [nil]);
2573 ClearRowCache(Buff);
2574
2575 for i := 0 to QryResults.Count - 1 do
2576 begin
2577 ColIndex := ColIndexByName(QryResults[i].GetAliasName);
2578 if ColIndex >= 0 then
2579 begin
2580 CopyCursorDataToBuffer(QryResults,i,ColIndex,Buff);
2581 SetRefreshRequired(Buff,ColIndex,false);
2582 end ;
2583 end;
2584 end;
2585
2586 function TIBSelectCursor.NeedRefresh(aBufID : TRecordBuffer) : boolean;
2587 var i: integer;
2588 Buff: PByte;
2589 begin
2590 Buff := GetBuffer(aBufID);
2591 if Buff = nil then
2592 IBError(ibxeBufferNotSet, [nil]);
2593
2594 Result := false;
2595 for i := 0 to FRefreshRequiredSize - 1 do
2596 if PByte(Buff + FRefreshRequiredBitmapOffset + i)^ <> 0 then
2597 begin
2598 Result := true;
2599 Exit;
2600 end;
2601 end;
2602
2603 function TIBSelectCursor.GetBookmarkFlag(aBufID: TRecordBuffer): TBookmarkFlag;
2604 begin
2605 Result := PDisplayBuffer(aBufID)^.dbBookmarkFlag;;
2606 end;
2607
2608 procedure TIBSelectCursor.SetBookmarkFlag(aBufID: TRecordBuffer;
2609 aBookmarkFlag: TBookmarkFlag);
2610 begin
2611 PDisplayBuffer(aBufID)^.dbBookmarkFlag := aBookmarkFlag;
2612 end;
2613
2614 procedure TIBSelectCursor.SetBookmarkData(aBufID: TRecordBuffer;
2615 RecNo: TIBRecordNumber);
2616 begin
2617 Move(RecNo,PDisplayBuffer(aBufID)^.dbBookmarkData,GetBookmarkSize);
2618 end;
2619
2620 procedure TIBSelectCursor.GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
2621 begin
2622 Move(PDisplayBuffer(Buffer)^.dbBookmarkData,Data^,GetBookmarkSize);
2623 end;
2624
2625 procedure TIBSelectCursor.SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
2626 begin
2627 if Data <> nil then
2628 Move(Data^,PDisplayBuffer(Buffer)^.dbBookmarkData, GetBookmarkSize);
2629 end;
2630
2631 function TIBSelectCursor.GetBookmarkSize: integer;
2632 begin
2633 Result := sizeof(TIBRecordNumber);
2634 end;
2635
2636 function TIBSelectCursor.GetRecordSize: word;
2637 begin
2638 Result := sizeof(TDisplayBuffer);
2639 end;
2640
2641 function TIBSelectCursor.GetCurrentRecNo: TIBRecordNumber;
2642 begin
2643 case FCurrentRecordStatus of
2644 csBOF:
2645 Result := 0;
2646 csEOF:
2647 Result := FRecordCount;
2648 csRowBuffer:
2649 Result := InternalGetRecNo(FCurrentRecord);
2650 end;
2651 end;
2652
2653 procedure TIBSelectCursor.SwapDataBuffer(buf1, buf2: TRecordBuffer);
2654 var TmpBuf: PByte;
2655 TmpBookmarkFlag: TBookmarkFlag;
2656 TmpBookmarkData1: TBytes;
2657 TmpBookmarkData2: TBytes;
2658 begin
2659 TmpBuf := PDisplayBuffer(Buf1)^.dbBuffer;
2660 TmpBookmarkFlag := GetBookmarkFlag(buf1);
2661 SetLength(TmpBookmarkData1,GetBookmarkSize);
2662 GetBookmarkData(buf1,pointer(TmpBookmarkData1));
2663 SetLength(TmpBookmarkData2,GetBookmarkSize);
2664 GetBookmarkData(buf1,pointer(TmpBookmarkData2));
2665 PDisplayBuffer(Buf1)^.dbBuffer := PDisplayBuffer(Buf2)^.dbBuffer;
2666 SetBookmarkFlag(buf1,GetBookmarkFlag(buf2));
2667 SetBookmarkData(buf1,pointer(TmpBookmarkData2));
2668 PDisplayBuffer(buf2)^.dbBuffer := TmpBuf;
2669 SetBookmarkFlag(buf2,TmpBookmarkFlag);
2670 SetBookmarkData(buf2,pointer(TmpBookmarkData1));
2671 end;
2672
2673 function TIBSelectCursor.GetAliasName(FieldNo: integer): AnsiString;
2674 begin
2675 if (FieldNo < 1) or (FieldNo > Length(FFieldNo2ColumnMap)) then
2676 IBError(ibxeBadFieldNo,[FieldNo, Length(FFieldNo2ColumnMap)-1]);
2677
2678 Result := FColumnMetaData[ FFieldNo2ColumnMap[FieldNo] ].fdAliasName;
2679 end;
2680
2681 procedure TIBSelectCursor.InitRecord(aBufID: TRecordBuffer);
2682 begin
2683 with PDisplayBuffer(aBufID)^ do
2684 begin
2685 dbBookmarkFlag := bfInserted;
2686 Fillchar(dbCalcFields^,CalcFieldsSize,0);
2687 FillChar(dbBookmarkData,GetBookmarkSize,0);
2688 if dbBuffer <> nil then
2689 InternalSetUpdateStatus(dbBuffer,usInserted);
2690 end;
2691 end;
2692
2693 function TIBSelectCursor.AtBOF: boolean;
2694 begin
2695 Result := FCurrentRecordStatus = csBOF;
2696 end;
2697
2698 function TIBSelectCursor.AtEOF: boolean;
2699 begin
2700 Result := (FCurrentRecordStatus = csEOF) and Cursor.IsEof;
2701 end;
2702
2703 function TIBSelectCursor.CursorAtEOF : boolean;
2704 begin
2705 Result := FCursor.IsEof;
2706 end;
2707
2708 procedure TIBSelectCursor.Delete(aBufID: TRecordBuffer);
2709 var Buff: PByte;
2710 begin
2711 Buff := GetBuffer(aBufID);
2712 if Buff = nil then
2713 IBError(ibxeBufferNotSet, [nil]);
2714
2715 InternalDelete(aBufID);
2716 end;
2717
2718 procedure TIBSelectCursor.UnDelete(aBufID: TRecordBuffer);
2719 var Buff: PByte;
2720 begin
2721 Buff := GetBuffer(aBufID);
2722 if Buff = nil then
2723 IBError(ibxeBufferNotSet, [nil]);
2724
2725 InternalUnDelete(Buff);
2726 end;
2727
2728 function TIBSelectCursor.GetUpdateStatus(aBufID: TRecordBuffer): TUpdateStatus;
2729 var Buff: PByte;
2730 begin
2731 Buff := GetBuffer(aBufID);
2732 if Buff = nil then
2733 IBError(ibxeBufferNotSet, [nil]);
2734 Result := InternalGetUpdateStatus(Buff);
2735 end;
2736
2737 procedure TIBSelectCursor.ClearCalcFields(aBufID: TRecordBuffer);
2738 var Buff: PByte;
2739 begin
2740 Buff := GetCalcFields(aBufID);
2741 FillChar(Buff^,FCalcFieldsSize,0);
2742 end;
2743
2744 procedure TIBSelectCursor.SetCursor(aCursor: IResultSet);
2745 begin
2746 if (FCursor <> nil) and (FCursor.GetStatement <> aCursor.GetStatement) then
2747 IBError(ibxeDifferentStatement,[]);
2748 Reset;
2749 FCursor := aCursor;
2750 end;
2751
2752 procedure TIBSelectCursor.RegisterQuery(qryType : TRegisteredQueryTypes;
2753 qry : IStatement; OnValuesReturnedProc : TOnValuesReturned);
2754 var i: integer;
2755 ParamName: AnsiString;
2756 begin
2757 with FRegisteredQueries[qryType] do
2758 begin
2759 stmt := qry;
2760 OnValuesReturned := OnValuesReturnedProc;
2761 SetLength(ParamMap,Qry.SQLParams.count);
2762 SetLength(UseOldValue, Qry.SQLParams.count);
2763 for i := 0 to Qry.SQLParams.Count - 1 do
2764 begin
2765 ParamName := NormaliseParamName(Qry.SQLParams[i].Name,UseOldValue[i]);
2766 ParamMap[i] := ColIndexByName(ParamName,Qry.SQLParams.GetHasCaseSensitiveParams);
2767 end;
2768
2769 SetLength(ColMap,qry.MetaData.Count);
2770 for i := 0 to qry.MetaData.Count - 1 do
2771 ColMap[i] := ColIndexByName(qry.MetaData[i].getAliasName);
2772 end;
2773 end;
2774
2775 procedure TIBSelectCursor.ExecRegisteredQuery(qryType : TRegisteredQueryTypes;
2776 aBufID : TRecordBuffer; var SelectCount, InsertCount, UpdateCount,
2777 DeleteCount : integer);
2778 var Buff: PByte;
2779 i: integer;
2780 OldBuffer: PByte;
2781 qryResults: IResults;
2782 qryResultSet: IResultSet;
2783 begin
2784 Buff := GetBuffer(aBufID);
2785 if Buff = nil then
2786 IBError(ibxeBufferNotSet, [nil]);
2787
2788 SaveBlobsAndArrays(Buff);
2789 OldBuffer := GetOldBufferFor(Buff);
2790
2791 with FRegisteredQueries[qryType] do
2792 begin
2793 {set param values}
2794 for i := 0 to Length(ParamMap) - 1 do
2795 if ParamMap[i] <> -1 then
2796 begin
2797 if UseOldValue[i] and (OldBuffer <> nil) then
2798 SetParamValue(OldBuffer,ParamMap[i],stmt.SQLParams[i])
2799 else
2800 SetParamValue(Buff,ParamMap[i],stmt.SQLParams[i]);
2801 end;
2802
2803 {execute query}
2804 qryResultSet := nil;
2805 qryResults := nil;
2806 if stmt.SQLStatementType = SQLSelect then
2807 begin
2808 qryResultSet := stmt.OpenCursor;
2809 if qryResultSet.FetchNext then
2810 qryResults := qryResultSet; {Only single results expected}
2811 end
2812 else
2813 qryResults := stmt.Execute;
2814 stmt.GetRowsAffected(SelectCount, InsertCount, UpdateCount, DeleteCount);
2815 if not (csDesigning in Dataset.ComponentState) then
2816 MonitorHook.SQLExecute(self,stmt.GetSQLText);
2817
2818 {process any return values}
2819 if qryType = rqDelete then
2820 InternalDelete(aBufID)
2821 else
2822 begin
2823 ClearRowCache(Buff);
2824 if qryResults <> nil then
2825 for i := 0 to Length(ColMap) - 1 do
2826 if ColMap[i] <> -1 then
2827 begin
2828 CopyCursorDataToBuffer(qryResults,i,ColMap[i],Buff);
2829 SetRefreshRequired(Buff,ColMap[i],false);
2830 end;
2831 end;
2832 if (qryResults <> nil) and assigned(OnValuesReturned) then
2833 OnValuesReturned(qryResults);
2834 end;
2835 end;
2836
2837 function TIBSelectCursor.HasRegisteredQuery(qryType : TRegisteredQueryTypes
2838 ) : boolean;
2839 begin
2840 Result := FRegisteredQueries[qryType].stmt <> nil;
2841 end;
2842
2843 procedure TIBSelectCursor.SetParamValue(Buff : PByte; colIndex : integer;
2844 Param : ISQLParam);
2845 var Data: PByte;
2846 DataLength: Short;
2847 st: RawByteString;
2848 pda: PIBArray;
2849 begin
2850 if InternalGetIsNull(Buff,ColIndex) then
2851 Param.IsNull := true
2852 else
2853 with FColumnMetaData[ColIndex] do
2854 begin
2855 Data := Buff + fdDataOfs;
2856 case fdDataType of
2857 SQL_TEXT:
2858 if Param.getColMetadata.getCharSetID <= 1 {NONE or OCTETS} then
2859 Param.SetAsPointer(Data)
2860 else
2861 begin
2862 DataLength := strlen(PAnsiChar(Data));
2863 if DataLength > fdDataSize then
2864 DataLength := fdDataSize;
2865 SetString(st, PAnsiChar(Data), DataLength);
2866 SetCodePage(st,fdCodePage,false);
2867 Param.AsString := st;
2868 end;
2869 SQL_VARYING:
2870 begin
2871 DataLength := PShort(Data)^;
2872 Inc(Data,sizeof(Short));
2873 SetString(st, PAnsiChar(Data), DataLength);
2874 SetCodePage(st,fdCodePage,false);
2875 Param.AsString := st;
2876 end;
2877 SQL_FLOAT, SQL_DOUBLE, SQL_D_FLOAT:
2878 Param.AsDouble := PDouble(Data)^;
2879 SQL_SHORT:
2880 begin
2881 if fdDataScale = 0 then
2882 Param.AsShort := PShort(Data)^
2883 else
2884 if fdDataScale >= (-4) then
2885 Param.AsCurrency := PCurrency(Data)^
2886 else
2887 Param.AsDouble := PDouble(Data)^;
2888 end;
2889 SQL_LONG:
2890 begin
2891 if fdDataScale = 0 then
2892 Param.AsLong := PLong(Data)^
2893 else
2894 if fdDataScale >= (-4) then
2895 Param.AsCurrency := PCurrency(Data)^
2896 else
2897 Param.AsDouble := PDouble(Data)^;
2898 end;
2899 SQL_INT64:
2900 begin
2901 if fdDataScale = 0 then
2902 Param.AsInt64 := PInt64(Data)^
2903 else
2904 if fdDataScale >= (-4) then
2905 Param.AsCurrency := PCurrency(Data)^
2906 else
2907 Param.AsDouble := PDouble(Data)^;
2908 end;
2909 SQL_BLOB, SQL_QUAD:
2910 Param.AsQuad := PISC_QUAD(Data)^;
2911 SQL_ARRAY:
2912 begin
2913 pda := PIBArray(Buff + fdObjOffset);
2914 if pda^ = nil then
2915 Param.AsQuad := PISC_QUAD(Data)^
2916 else
2917 Param.AsArray := pda^.ArrayIntf;
2918 end;
2919 SQL_TYPE_DATE,
2920 SQL_TYPE_TIME,
2921 SQL_TIMESTAMP:
2922 {This is an IBX native format and not the TDataset approach. See also SetFieldData}
2923 Param.AsDateTime := PDateTime(Data)^;
2924 SQL_TIMESTAMP_TZ_EX,
2925 SQL_TIMESTAMP_TZ:
2926 with PIBBufferedDateTimeWithTimeZone(Data)^ do
2927 Param.SetAsDateTime(Timestamp,TimeZoneID);
2928 SQL_TIME_TZ_EX,
2929 SQL_TIME_TZ:
2930 with PIBBufferedDateTimeWithTimeZone(Data)^ do
2931 Param.SetAsTime(Timestamp,FDefaultTZDate,TimeZoneID);
2932 SQL_BOOLEAN:
2933 Param.AsBoolean := PWordBool(Data)^;
2934 SQL_DEC16,
2935 SQL_DEC34,
2936 SQL_DEC_FIXED,
2937 SQL_INT128:
2938 Param.AsBCD := pBCD(Data)^;
2939 else
2940 IBError(ibxeUnknownSQLType,[fdDataType]);
2941 end;
2942 end;
2943 end;
2944
2945 { TIBSimpleBufferPool }
2946
2947 function TIBSimpleBufferPool.AllocBlock(buffers: integer): PByte;
2948 var blockSize: integer;
2949 userBufferAreaSize: integer;
2950 begin
2951 userBufferAreaSize := buffers * (BufferSize + sizeof(TBufferHeader));
2952 blockSize := sizeof(TStartHeader) + userBufferAreaSize + sizeof(TEndHeader);
2953 Result := GetMem(blockSize);
2954 if Result <> nil then
2955 begin
2956 FillChar(Result^,blockSize,0);
2957 with PStartHeader(Result)^ do
2958 begin
2959 HeaderType := htStart;
2960 {add to end of list}
2961 PreviousBlock := FLastBlock;
2962 NextBlock := nil;
2963 MaxBuffers := buffers;
2964 BuffersInUse := 0;
2965 FirstRecNo := 1;
2966 if PreviousBlock <> nil then
2967 begin
2968 FirstRecNo := PStartHeader(PreviousBlock)^.FirstRecNo + PStartHeader(PreviousBlock)^.BuffersInUse;
2969 PStartHeader(PreviousBlock)^.NextBlock := Result;
2970 end;
2971 end;
2972 with PEndHeader(Result + sizeof(TStartHeader) + userBufferAreaSize)^ do
2973 begin
2974 HeaderType := htEnd;
2975 StartHeader := Result;
2976 end;
2977 FLastBlock := Result;
2978 FBufferIndex.Add(Result);
2979 end
2980 else
2981 OutofMemoryError;
2982 end;
2983
2984 procedure TIBSimpleBufferPool.CheckValidBuffer(P: PByte);
2985 begin
2986 Dec(P,sizeof(TBufferHeader));
2987 InternalCheckValidBuffer(P);
2988 end;
2989
2990 procedure TIBSimpleBufferPool.CheckBuffersAvailable;
2991 begin
2992 if Empty then
2993 IBError(ibxeEmptyBufferPool,[FName]);
2994 end;
2995
2996 procedure TIBSimpleBufferPool.InternalCheckValidBuffer(P: PByte);
2997 begin
2998 if not (PBufferHeader(P)^.HeaderType in [htFirstBuffer,htBuffer]) then
2999 IBError(ibxeNotABuffer,[FName]);
3000 end;
3001
3002 constructor TIBSimpleBufferPool.Create(aName: string; bufSize,
3003 aBuffersPerBlock, firstBlockBuffers: integer);
3004 begin
3005 inherited Create;
3006 FName := aName;
3007 FBufferSize := bufSize;
3008 if (aBuffersPerBlock <= 1) or (firstBlockBuffers <= 1) then
3009 IBError(ibxeNotEnoughBuffers,[FName]);
3010 FBuffersPerBlock := aBuffersPerBlock;
3011 FFirstBlockBuffers := firstBlockBuffers;
3012 FBufferIndex := TList.Create;
3013 end;
3014
3015 destructor TIBSimpleBufferPool.Destroy;
3016 begin
3017 Clear;
3018 if FBufferIndex <> nil then FBufferIndex.Free;
3019 inherited Destroy;
3020 end;
3021
3022 function TIBSimpleBufferPool.Append: PByte;
3023 begin
3024 Result := AddBuffer;
3025 end;
3026
3027 function TIBSimpleBufferPool.AddBuffer: PByte;
3028 begin
3029 Result := nil;
3030 if FFirstBlock = nil then
3031 FFirstBlock := AllocBlock(FFirstBlockBuffers);
3032
3033 with PStartHeader(FLastBlock)^ do
3034 if BuffersInUse >= MaxBuffers then
3035 AllocBlock(FBuffersPerBlock); {Add a Block and set FLastBlock to newly added block}
3036
3037 with PStartHeader(FLastBlock)^ do
3038 begin
3039 Result := FLastBlock + sizeof(TStartHeader) + BuffersInUse * (FBufferSize + sizeof(TBufferHeader));
3040 with PBufferHeader(Result)^ do
3041 begin
3042 if BuffersInUse = 0 then
3043 HeaderType := htFirstBuffer
3044 else
3045 HeaderType := htBuffer;
3046 RecNo := FirstRecNo + BuffersInUse;
3047 Inc(BuffersInUse);
3048 end;
3049 end;
3050 FLastBuffer := Result;
3051 Inc(Result,sizeof(TBufferHeader)); {start of user data}
3052 Inc(FRecordCount);
3053 end;
3054
3055 procedure TIBSimpleBufferPool.Clear;
3056 var P, P1: PByte;
3057 begin
3058 P := FFirstBlock;
3059 while P <> nil do
3060 begin
3061 P1 := PStartHeader(P)^.NextBlock;
3062 FreeMem(P);
3063 P := P1;
3064 end;
3065 FFirstBlock := nil;
3066 FLastBlock := nil;
3067 FLastBuffer := nil;
3068 FCurrent := nil;
3069 FBufferIndex.Clear;
3070 FRecordCount := 0;
3071 end;
3072
3073 function TIBSimpleBufferPool.GetFirst: PByte;
3074 begin
3075 CheckBuffersAvailable;
3076 Result := FFirstBlock + sizeof(TStartHeader);
3077 InternalCheckValidBuffer(Result);
3078 FCurrent := Result;
3079 Inc(Result,sizeof(TBufferHeader))
3080 end;
3081
3082 function TIBSimpleBufferPool.GetLast: PByte;
3083 begin
3084 CheckBuffersAvailable;
3085 Result := FLastBuffer;
3086 InternalCheckValidBuffer(Result);
3087 FCurrent := Result;
3088 Inc(Result,sizeof(TBufferHeader))
3089 end;
3090
3091 function TIBSimpleBufferPool.GetBuffer(RecNo: TIBRecordNumber): PByte;
3092 var i: integer;
3093 begin
3094 Result := nil;
3095 CheckBuffersAvailable;
3096
3097 for i := 0 to FBufferIndex.Count - 1 do
3098 begin
3099 with PStartHeader(FBufferIndex[i]) ^ do
3100 if (BuffersInUse > 0 ) and (RecNo < FirstRecNo + BuffersInUse) then
3101 begin
3102 Result := FBufferIndex[i] + sizeof(TStartHeader) +
3103 (RecNo - FirstRecNo) * (sizeof(TBufferHeader) + FBufferSize);
3104 break;
3105 end;
3106 end;
3107
3108 if Result <> nil then
3109 begin
3110 InternalCheckValidBuffer(Result);
3111 FCurrent := Result;
3112 Inc(Result, sizeof(TBufferHeader));
3113 end;
3114 end;
3115
3116 {Returns either pointer to next user buffer or nil if EOF}
3117
3118 function TIBSimpleBufferPool.GetNextBuffer(aBuffer: PByte): PByte;
3119 var P: PByte;
3120 begin
3121 Result := nil;
3122 CheckBuffersAvailable;
3123
3124 if aBuffer = nil then {Implicit request for current buffer}
3125 Result := FCurrent
3126 else
3127 begin
3128 P := aBuffer - sizeof(TBufferHeader);
3129 InternalCheckValidBuffer(P);
3130 Inc(P,sizeof(TBufferHeader)+FBufferSize);
3131 case PBufferHeader(P)^.HeaderType of
3132 htFirstBuffer,htBuffer:
3133 Result := P ;
3134
3135 htEmptyslot:
3136 ; {No more buffers}
3137
3138 htEnd:
3139 {get first buffer in next block if available}
3140 begin
3141 P := PStartHeader(PEndHeader(P)^.StartHeader)^.NextBlock;
3142 if (P <> nil) and (PStartHeader(P)^.BuffersInUse <> 0) then
3143 Result := P + sizeof(TStartHeader);
3144 end;
3145
3146 else
3147 IBError(ibxeUnrecognisedHeaderType,[ord(PBufferHeader(P)^.HeaderType)]);
3148 end;
3149 end;
3150 if Result <> nil then
3151 begin
3152 InternalCheckValidBuffer(Result);
3153 FCurrent := Result;
3154 Inc(Result, sizeof(TBufferHeader));
3155 end;
3156 end;
3157
3158 {returns either pointer to previous user buffer or nil if BOF}
3159
3160 function TIBSimpleBufferPool.GetPriorBuffer(aBuffer: PByte): PByte;
3161 var P: PByte;
3162 begin
3163 Result := nil;
3164 CheckBuffersAvailable;
3165
3166 if aBuffer = nil then {Implicit request for current buffer}
3167 Result := FCurrent
3168 else
3169 begin
3170 P := aBuffer - sizeof(TBufferHeader);
3171 InternalCheckValidBuffer(P);
3172 if PBufferHeader(P)^.HeaderType = htFirstBuffer then
3173 begin
3174 P := PStartHeader(P- sizeof(TStartHeader))^.PreviousBlock;
3175 if (P <> nil) and (PStartHeader(P)^.BuffersInUse <> 0) then
3176 Result := P + sizeof(TStartHeader) +
3177 (PStartHeader(P)^.BuffersInUse - 1)*(sizeof(TBufferHeader) + FBufferSize);
3178 end
3179 else
3180 Result := P - FBufferSize - sizeof(TBufferHeader);
3181 end;
3182
3183 if Result <> nil then
3184 begin
3185 InternalCheckValidBuffer(Result);
3186 FCurrent := Result;
3187 Inc(Result, sizeof(TBufferHeader));
3188 end;
3189 end;
3190
3191 function TIBSimpleBufferPool.GetRecNo(aBuffer: PByte): TIBRecordNumber;
3192 var P: PByte;
3193 begin
3194 P := aBuffer - sizeof(TBufferHeader);
3195 InternalCheckValidBuffer(P);
3196 Result := PBufferHeader(P)^.RecNo;
3197 end;
3198
3199 function TIBSimpleBufferPool.GetRecordCount: TIBRecordNumber;
3200 begin
3201 Result := FRecordCount;
3202 end;
3203
3204 function TIBSimpleBufferPool.Empty: boolean;
3205 begin
3206 Result := FFirstBlock = nil;
3207 end;
3208
3209 { TIBBufferPool }
3210
3211 constructor TIBBufferPool.Create(aName: string; bufSize, aBuffersPerBlock,
3212 firstBlockBuffers: integer);
3213 begin
3214 inherited Create(aName,bufSize + sizeof(TRecordData), aBuffersPerBlock, firstBlockBuffers);
3215 end;
3216
3217 procedure TIBBufferPool.Clear;
3218 begin
3219 inherited Clear;
3220 FFirstRecord := nil;
3221 FLastRecord := nil;
3222 FInsertedRecords := 0;
3223 FDeletedRecords := 0;
3224 end;
3225
3226 function TIBBufferPool.GetFirst: PByte;
3227 begin
3228 Result := FFirstRecord;
3229 if (Result <> nil) and (PRecordData(Result)^.rdStatus in [rsAppendDeleted,rsInsertDeleted]) then
3230 Result := InternalGetNextBuffer(Result,false);
3231 if Result <> nil then
3232 Inc(Result,sizeof(TRecordData));
3233 end;
3234
3235 function TIBBufferPool.GetLast: PByte;
3236 begin
3237 Result := FLastRecord;
3238 while (Result <> nil) and (PRecordData(Result)^.rdStatus in [rsAppendDeleted,rsInsertDeleted]) do
3239 Result := PRecordData(Result)^.rdPreviousBuffer;
3240 if Result <> nil then
3241 Inc(Result,sizeof(TRecordData));
3242 end;
3243
3244 {InternalGetNextBuffer skips over deleted records and tries to find the next
3245 record that points back to aBuffer}
3246
3247 function TIBBufferPool.InternalGetNextBuffer(aBuffer: PByte;
3248 IncludeDeleted: boolean): PByte;
3249 {aBuffer points to TRecordData}
3250 var CurBuffer:PByte;
3251 temp: PByte;
3252 begin
3253 Result := aBuffer;
3254 repeat
3255 CurBuffer := Result;
3256 case PRecordData(CurBuffer)^.rdStatus of
3257
3258 {records are always appended in sequence but the sequence may be interrupted by
3259 inserted out of sequence records. These should generally be ignored but could
3260 be the next in sequence - so have to check them}
3261
3262 rsAppended, rsAppendDeleted:
3263 repeat {look for the next undeleted buffer with a previous pointer to this buffer}
3264 Result := inherited GetNextBuffer(Result);
3265 until (Result = nil) or (PRecordData(Result)^.rdPreviousBuffer = CurBuffer);
3266
3267 {Inserted records are typically out of sequence, but can be part of a local
3268 sequence of inserted buffers.
3269
3270 1. lookahead until either the next in sequence is found or the local sequence
3271 breaks.
3272 2. If not found then work backwards to current sequence insertion point
3273 3. Then walk forwards to find the buffer
3274 }
3275
3276 rsInserted, rsInsertDeleted:
3277 begin
3278 Result := inherited GetNextBuffer(CurBuffer);
3279 if Result = nil then
3280 begin
3281 {go back to insertion point and walk forwards}
3282 Result := CurBuffer;
3283 temp := PRecordData(Result)^.rdPreviousBuffer;
3284 while (temp <> nil) and
3285 (PRecordData(temp)^.rdStatus in [rsInsertDeleted, rsAppendDeleted]) do
3286 begin
3287 Result := PRecordData(temp)^.rdPreviousBuffer;
3288 temp := PRecordData(Result)^.rdPreviousBuffer;
3289 end;
3290
3291 if PRecordData(Result)^.rdPreviousBuffer <> nil then
3292 Result := inherited GetNextBuffer(PRecordData(Result)^.rdPreviousBuffer)
3293 else
3294 {inserted at start. Have to walk the pool to find the next buffer}
3295 Result := LocatePreviousBuffer(CurBuffer);
3296 end
3297 else
3298 if (Result <> nil) and (PRecordData(Result)^.rdPreviousBuffer <> CurBuffer) then {otherwise found it}
3299 begin
3300 Result := CurBuffer;
3301 {Go back to insertion point}
3302 repeat
3303 Result := PRecordData(Result)^.rdPreviousBuffer;
3304 until (Result = nil) or (PRecordData(Result)^.rdStatus in [rsAppended, rsAppendDeleted]);
3305
3306 if Result <> nil then {now back at the point where the buffer(s) were
3307 inserted.}
3308 begin
3309 {find the next appended buffer}
3310 repeat
3311 Result := inherited GetNextBuffer(Result);
3312 until (Result = nil) or (PRecordData(Result)^.rdStatus in [rsAppended, rsAppendDeleted]);
3313
3314 {now work backwards to find the next buffer in the sequence}
3315 while (Result <> nil) and (PRecordData(Result)^.rdPreviousBuffer <> CurBuffer) do
3316 {look backwards for the next buffer with a previous pointer to this buffer}
3317 Result := PRecordData(Result)^.rdPreviousBuffer;
3318 end;
3319 end;
3320 end;
3321 end;
3322 until (Result = nil) or (PRecordData(Result)^.rdStatus in [rsAppended,rsInserted])
3323 or (IncludeDeleted and (PRecordData(Result)^.rdStatus in [rsAppendDeleted,rsInsertDeleted]));
3324 end;
3325
3326 function TIBBufferPool.GetBuffer(RecNo: TIBRecordNumber): PByte;
3327 begin
3328 Result := inherited GetBuffer(RecNo);
3329 if PRecordData(Result)^.rdStatus in [rsInsertDeleted, rsAppendDeleted] then
3330 IBError(ibxeRecordisDeleted,[RecNo]);
3331 if Result <> nil then
3332 Inc(Result,sizeof(TRecordData));
3333 end;
3334
3335 function TIBBufferPool.GetNextBuffer(aBuffer: PByte): PByte;
3336 begin
3337 Dec(aBuffer,sizeof(TRecordData));
3338 CheckValidBuffer(aBuffer);
3339 Result := InternalGetNextBuffer(aBuffer,false);
3340 if Result <> nil then
3341 Inc(Result,sizeof(TRecordData));
3342 end;
3343
3344 function TIBBufferPool.GetNextBuffer(aBuffer : PByte; IncludeDeleted : boolean
3345 ) : PByte;
3346 begin
3347 Dec(aBuffer,sizeof(TRecordData));
3348 CheckValidBuffer(aBuffer);
3349 Result := InternalGetNextBuffer(aBuffer,IncludeDeleted);
3350 if Result <> nil then
3351 Inc(Result,sizeof(TRecordData));
3352 end;
3353
3354 function TIBBufferPool.GetPriorBuffer(aBuffer: PByte): PByte;
3355 begin
3356 Dec(aBuffer,sizeof(TRecordData));
3357 CheckValidBuffer(aBuffer);
3358 Result := aBuffer;
3359 repeat
3360 Result := PRecordData(Result)^.rdPreviousBuffer
3361 until (Result = nil) or (PRecordData(Result)^.rdStatus in [rsAppended,rsInserted]);
3362 if Result <> nil then
3363 Inc(Result,sizeof(TRecordData));
3364 end;
3365
3366 function TIBBufferPool.GetRecNo(aBuffer: PByte): TIBRecordNumber;
3367 begin
3368 if aBuffer = nil then
3369 Result := 0
3370 else
3371 begin
3372 Dec(aBuffer,sizeof(TRecordData));
3373 CheckValidBuffer(aBuffer);
3374 if PRecordData(aBuffer)^.rdStatus in [rsInsertDeleted, rsAppendDeleted] then
3375 Result := 0;
3376 Result := inherited GetRecNo(aBuffer);
3377 end;
3378 end;
3379
3380 {Locate by walking bufferpool from start to finish}
3381
3382 function TIBBufferPool.LocatePreviousBuffer(aBuffer : PByte) : PByte;
3383 begin
3384 Result := inherited GetFirst;
3385 while (Result <> nil) and (PRecordData(Result)^.rdPreviousBuffer <> aBuffer) do
3386 Result := inherited GetNextBuffer(Result);
3387 end;
3388
3389 function TIBBufferPool.InsertBefore(aBuffer: PByte): PByte;
3390 begin
3391 if Empty then
3392 Result := Append
3393 else
3394 begin
3395 Dec(aBuffer,sizeof(TRecordData));
3396 CheckValidBuffer(aBuffer);
3397 Result := AddBuffer;
3398 with PRecordData(Result)^ do
3399 begin
3400 rdStatus := rsInserted;
3401 rdPreviousBuffer := PRecordData(aBuffer)^.rdPreviousBuffer;
3402 end;
3403 PRecordData(aBuffer)^.rdPreviousBuffer := Result;
3404 if aBuffer = FFirstRecord then
3405 FFirstRecord := Result;
3406 end;
3407 Inc(Result,sizeof(TRecordData));
3408 Inc(FInsertedRecords);
3409 end;
3410
3411 function TIBBufferPool.InsertAfter(aBuffer: PByte): PByte;
3412 begin
3413 if Empty then
3414 Result := Append
3415 else
3416 begin
3417 Dec(aBuffer,sizeof(TRecordData));
3418 CheckValidBuffer(aBuffer);
3419 Result := AddBuffer;
3420 with PRecordData(Result)^ do
3421 begin
3422 rdPreviousBuffer := aBuffer;
3423 if aBuffer = FLastRecord then
3424 begin
3425 rdStatus := rsAppended;
3426 FLastRecord := Result;
3427 end
3428 else
3429 begin
3430 rdStatus := rsInserted;
3431 {assumes InternalGetNextBuffer can never return nil given aBuffer is not last}
3432 PRecordData(InternalGetNextBuffer(aBuffer,true))^.rdPreviousBuffer := Result;
3433 end;
3434 end;
3435 end;
3436 Inc(Result,sizeof(TRecordData));
3437 Inc(FInsertedRecords);
3438 end;
3439
3440 function TIBBufferPool.Append: PByte;
3441 begin
3442 Result := AddBuffer;
3443 with PRecordData(Result)^ do
3444 begin
3445 rdPreviousBuffer := FLastRecord;
3446 rdStatus := rsAppended;
3447 FLastRecord := Result;
3448 if FFirstRecord = nil then
3449 FFirstRecord := Result;
3450 end;
3451 Inc(Result,sizeof(TRecordData));
3452 Inc(FInsertedRecords);
3453 end;
3454
3455 function TIBBufferPool.Delete(aBuffer: PByte): PByte;
3456 begin
3457 Result := GetPriorBuffer(aBuffer);
3458 Dec(aBuffer,sizeof(TRecordData));
3459 CheckValidBuffer(aBuffer);
3460 case PRecordData(aBuffer)^.rdStatus of
3461 rsInserted:
3462 begin
3463 if FFirstRecord = aBuffer then
3464 begin
3465 FFirstRecord := InternalGetNextBuffer(aBuffer,true);
3466 if FFirstRecord <> nil then
3467 PRecordData(FFirstRecord)^.rdPreviousBuffer := nil;
3468 end;
3469 PRecordData(aBuffer)^.rdStatus := rsInsertDeleted;
3470 end;
3471 rsAppended:
3472 PRecordData(aBuffer)^.rdStatus := rsAppendDeleted;
3473 end;
3474 // writeln('Rec No = ',inherited GetRecNo(aBuffer),' status = ', PRecordData(aBuffer)^.rdStatus);
3475 Inc(FDeletedRecords);
3476 end;
3477
3478 procedure TIBBufferPool.UnDelete(aBuffer: PByte);
3479 begin
3480 Dec(aBuffer,sizeof(TRecordData));
3481 CheckValidBuffer(aBuffer);
3482 case PRecordData(aBuffer)^.rdStatus of
3483 rsInsertDeleted:
3484 begin
3485 PRecordData(aBuffer)^.rdStatus := rsInserted;
3486 if PRecordData(aBuffer)^.rdPreviousBuffer = nil then
3487 {restore as first record}
3488 begin
3489 PrecordData(FFirstRecord)^.rdPreviousBuffer := aBuffer;
3490 FFirstRecord := aBuffer;
3491 end;
3492 end;
3493 rsAppendDeleted:
3494 PRecordData(aBuffer)^.rdStatus := rsAppended;
3495 end;
3496 Dec(FDeletedRecords);
3497 end;
3498
3499 function TIBBufferPool.GetUpdateStatus(aBuffer: PByte): TUpdateStatus;
3500 begin
3501 Dec(aBuffer,sizeof(TRecordData));
3502 CheckValidBuffer(aBuffer);
3503 case PRecordData(aBuffer)^.rdStatus of
3504 rsInsertDeleted,
3505 rsAppendDeleted:
3506 Result := usDeleted;
3507
3508 rsInserted,
3509 rsAppended:
3510 Result := usInserted;
3511
3512 else
3513 Result := usUnmodified;
3514 end;
3515 end;
3516
3517 function TIBBufferPool.GetRecordStatus(aBuffer: PByte): TRecordStatus;
3518 begin
3519 Dec(aBuffer,sizeof(TRecordData));
3520 CheckValidBuffer(aBuffer);
3521 Result := PRecordData(aBuffer)^.rdStatus;
3522 end;
3523
3524 {$ifdef PrintBuf}
3525 procedure TIBBufferPool.PrintBufferList;
3526 var buff: PByte;
3527 begin
3528 writeln('Print Buffer List for ',Name);
3529 writeln('Record Count = ',RecordCount);
3530 buff := GetFirst;
3531 while buff <> nil do
3532 begin
3533 writeln('Rec No = ',GetRecNo(buff),' status = ',GetRecordStatus(buff),' previous = ',
3534 GetRecNo(GetPriorBuffer(buff)));
3535 buff := GetNextBuffer(buff);
3536 end;
3537 writeln('Include Deleted');
3538 buff := GetFirst;
3539 while buff <> nil do
3540 begin
3541 writeln('Rec No = ',GetRecNo(buff),' status = ',GetRecordStatus(buff));
3542 buff := GetNextBuffer(buff,true);
3543 end;
3544 end;
3545 {$endif}
3546
3547 { TIBOldBufferPool }
3548
3549 procedure TIBOldBufferPool.CheckValidBuffer(P: PByte);
3550 begin
3551 Dec(P,sizeof(TRecordData));
3552 inherited CheckValidBuffer(P);
3553 end;
3554
3555 constructor TIBOldBufferPool.Create(aName: string; bufSize, aBuffersPerBlock,
3556 firstBlockBuffers: integer);
3557 begin
3558 inherited Create(aName,bufSize + sizeof(TRecordData),aBuffersPerBlock,
3559 firstBlockBuffers);
3560 end;
3561
3562 function TIBOldBufferPool.Append(RecNo: TIBRecordNumber; DataBuffer: PByte
3563 ): PByte;
3564 begin
3565 Result := AddBuffer;
3566 with PRecordData(Result)^ do
3567 begin
3568 rdStatus := cusUnModified;
3569 rdRecordNumber := RecNo;
3570 rdDataBuffer := DataBuffer;
3571 end;
3572 Inc(Result,sizeof(TRecordData));
3573 end;
3574
3575 procedure TIBOldBufferPool.Clear;
3576 begin
3577 inherited Clear;
3578 FModifiedRecords := 0;
3579 end;
3580
3581 function TIBOldBufferPool.FindOldBufferFor(RecNo: TIBRecordNumber): PByte;
3582 var buffer: PByte;
3583 begin
3584 Result := nil;
3585 buffer := GetFirst;
3586 while (buffer <> nil) do
3587 begin
3588 if PRecordData(buffer)^.rdRecordNumber = RecNo then
3589 begin
3590 Result := buffer;
3591 break;
3592 end;
3593 buffer := GetNextBuffer(buffer);
3594 end;
3595 end;
3596
3597 function TIBOldBufferPool.GetBuffer(RecNo: TIBRecordNumber): PByte;
3598 begin
3599 Result := inherited GetBuffer(RecNo);
3600 if Result <> nil then
3601 Inc(Result,sizeof(TRecordData));
3602 end;
3603
3604 function TIBOldBufferPool.GetRecNo(aBuffer: PByte): TIBRecordNumber;
3605 begin
3606 Result := inherited GetRecNo(aBuffer - sizeof(TRecordData));
3607 end;
3608
3609 function TIBOldBufferPool.GetStatus(aBuffer: PByte): TCachedUpdateStatus;
3610 begin
3611 Dec(aBuffer,sizeof(TRecordData));
3612 CheckValidBuffer(aBuffer);
3613 Result := PRecordData(aBuffer)^.rdStatus;
3614 end;
3615
3616 function TIBOldBufferPool.GetStatus(RecNo: TIBRecordNumber
3617 ): TCachedUpdateStatus;
3618 var buffer: PByte;
3619 begin
3620 buffer := GetFirst;
3621 while (buffer <> nil) do
3622 begin
3623 if PRecordData(buffer)^.rdRecordNumber = RecNo then
3624 break;
3625 buffer := GetNextBuffer(buffer);
3626 end;
3627 if Buffer = nil then
3628 Result := cusUnmodified
3629 else
3630 Result := PRecordData(buffer)^.rdStatus
3631 end;
3632
3633 procedure TIBOldBufferPool.SetStatus(aBuffer: PByte;
3634 status: TCachedUpdateStatus);
3635 begin
3636 CheckValidBuffer(aBuffer);
3637 Dec(aBuffer,sizeof(TRecordData));
3638 if PRecordData(aBuffer)^.rdStatus <> status then
3639 begin
3640 PRecordData(aBuffer)^.rdStatus := status;
3641 case status of
3642 cusUnmodified:
3643 Inc(FModifiedRecords);
3644 else
3645 Dec(FModifiedRecords);
3646 end;
3647 end;
3648 end;
3649
3650 procedure TIBOldBufferPool.SetDataBuffer(aBuffer: PByte; aDataBuffer: PByte);
3651 begin
3652 Dec(aBuffer,sizeof(TRecordData));
3653 CheckValidBuffer(aBuffer);
3654 PRecordData(aBuffer)^.rdDataBuffer := aDataBuffer;
3655 end;
3656
3657 procedure TIBOldBufferPool.ForwardIterator(iterator: TIterator);
3658 var buf: PByte;
3659 begin
3660 buf := GetFirst;
3661 while (buf <> nil) do
3662 with PRecordData(buf)^ do
3663 begin
3664 if rdStatus <> cusUnModified then
3665 iterator(rdStatus,rdDataBuffer,buf + sizeof(TRecordData));
3666 buf := GetNextBuffer(buf);
3667 end;
3668 end;
3669
3670 procedure TIBOldBufferPool.BackwardsIterator(iterator: TIterator);
3671 var buf: PByte;
3672 begin
3673 buf := GetLast;
3674 while (buf <> nil) do
3675 with PRecordData(buf)^ do
3676 begin
3677 if rdStatus <> cusUnModified then
3678 iterator(rdStatus,rdDataBuffer,buf + sizeof(TRecordData));
3679 buf := GetPriorBuffer(buf);
3680 end;
3681 end;
3682
3683 { TIBDSBlobStream }
3684
3685 procedure TIBDSBlobStream.FieldChanged;
3686 begin
3687 TBlobField(FField).Modified := true;
3688 THackedField(FField).DataChanged;
3689 end;
3690
3691 function TIBDSBlobStream.GetSize: Int64;
3692 begin
3693 Result := FBlobStream.BlobSize;
3694 end;
3695
3696 constructor TIBDSBlobStream.Create(AField: TField; ABlobStream: TIBBlobStream;
3697 Mode: TBlobStreamMode);
3698 begin
3699 FField := AField;
3700 FBlobStream := ABlobStream;
3701 FBlobStream.Seek(0, soFromBeginning);
3702 if (Mode = bmWrite) then
3703 begin
3704 FBlobStream.Truncate;
3705 FieldChanged;
3706 FHasWritten := true;
3707 end;
3708 end;
3709
3710 destructor TIBDSBlobStream.Destroy;
3711 begin
3712 if FHasWritten then
3713 FieldChanged;
3714 inherited Destroy;
3715 end;
3716
3717 function TIBDSBlobStream.Read(var Buffer; Count: Longint): Longint;
3718 begin
3719 result := FBlobStream.Read(Buffer, Count);
3720 end;
3721
3722 function TIBDSBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
3723 begin
3724 result := FBlobStream.Seek(Offset, Origin);
3725 end;
3726
3727 procedure TIBDSBlobStream.SetSize(NewSize: Longint);
3728 begin
3729 FBlobStream.SetSize(NewSize);
3730 end;
3731
3732 function TIBDSBlobStream.Write(const Buffer; Count: Longint): Longint;
3733 begin
3734 if not (FField.DataSet.State in [dsEdit, dsInsert]) then
3735 IBError(ibxeNotEditing, [nil]);
3736 FieldChanged;
3737 result := FBlobStream.Write(Buffer, Count);
3738 FHasWritten := true;
3739 end;
3740
3741 end.
3742

Properties

Name Value
svn:eol-style native