1 {
2 This file is part of the Free Pascal run time library.
3 Copyright (c) 1999-2014 by Joost van der Sluis and other members of the
4 Free Pascal development team
5
6 BufDataset implementation
7
8 See the file COPYING.FPC, included in this distribution,
9 for details about the copyright.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 **********************************************************************}
16
17 unit BufDataset;
18
19 {$mode objfpc}
20 {$h+}
21
22 interface
23
24 uses Classes,Sysutils,db,bufdataset_parser;
25
26 type
27 TCustomBufDataset = Class;
28
29 TResolverErrorEvent = procedure(Sender: TObject; DataSet: TCustomBufDataset; E: EUpdateError;
30 UpdateKind: TUpdateKind; var Response: TResolverResponse) of object;
31
32 { TBlobBuffer }
33
34 PBlobBuffer = ^TBlobBuffer;
35 TBlobBuffer = record
36 FieldNo : integer;
37 OrgBufID: integer;
38 Buffer : pointer;
39 Size : PtrInt;
40 end;
41
42 PBufBlobField = ^TBufBlobField;
43 TBufBlobField = record
44 ConnBlobBuffer : array[0..11] of byte; // DB specific data is stored here
45 BlobBuffer : PBlobBuffer;
46 end;
47
48 { TBufBlobStream }
49
50 TBufBlobStream = class(TStream)
51 private
52 FField : TBlobField;
53 FDataSet : TCustomBufDataset;
54 FBlobBuffer : PBlobBuffer;
55 FPosition : PtrInt;
56 FModified : boolean;
57 protected
Seeknull58 function Seek(Offset: Longint; Origin: Word): Longint; override;
Readnull59 function Read(var Buffer; Count: Longint): Longint; override;
Writenull60 function Write(const Buffer; Count: Longint): Longint; override;
61 public
62 constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
63 destructor Destroy; override;
64 end;
65
66
67 PBufRecLinkItem = ^TBufRecLinkItem;
68 TBufRecLinkItem = record
69 prior : PBufRecLinkItem;
70 next : PBufRecLinkItem;
71 end;
72
73 PBufBookmark = ^TBufBookmark;
74 TBufBookmark = record
75 BookmarkData : PBufRecLinkItem;
76 BookmarkInt : integer; // was used by TArrayBufIndex
77 BookmarkFlag : TBookmarkFlag;
78 end;
79
80 TRecUpdateBuffer = record
81 UpdateKind : TUpdateKind;
82 { BookMarkData:
83 - Is -1 if the update has canceled out. For example: an appended record has been deleted again
84 - If UpdateKind is ukInsert, it contains a bookmark to the newly created record
85 - If UpdateKind is ukModify, it contains a bookmark to the record with the new data
86 - If UpdateKind is ukDelete, it contains a bookmark to the deleted record (ie: the record is still there)
87 }
88 BookmarkData : TBufBookmark;
89 { NextBookMarkData:
90 - If UpdateKind is ukDelete, it contains a bookmark to the record just after the deleted record
91 }
92 NextBookmarkData : TBufBookmark;
93 { OldValuesBuffer:
94 - If UpdateKind is ukModify, it contains a record buffer which contains the old data
95 - If UpdateKind is ukDelete, it contains a record buffer with the data of the deleted record
96 }
97 OldValuesBuffer : TRecordBuffer;
98 end;
99 TRecordsUpdateBuffer = array of TRecUpdateBuffer;
100
ubValuenull101 TCompareFunc = function(subValue, aValue: pointer; size: integer; options: TLocateOptions): int64;
102
103 TDBCompareRec = record
104 CompareFunc : TCompareFunc;
105 Off : PtrInt;
106 NullBOff : PtrInt;
107 FieldInd : longint;
108 Size : integer;
109 Options : TLocateOptions;
110 Desc : Boolean;
111 end;
112 TDBCompareStruct = array of TDBCompareRec;
113
114 { TBufIndex }
115
116 TBufIndex = class(TObject)
117 private
118 FDataset : TCustomBufDataset;
119 protected
GetBookmarkSizenull120 function GetBookmarkSize: integer; virtual; abstract;
GetCurrentBuffernull121 function GetCurrentBuffer: Pointer; virtual; abstract;
GetCurrentRecordnull122 function GetCurrentRecord: TRecordBuffer; virtual; abstract;
GetIsInitializednull123 function GetIsInitialized: boolean; virtual; abstract;
GetSpareBuffernull124 function GetSpareBuffer: TRecordBuffer; virtual; abstract;
GetSpareRecordnull125 function GetSpareRecord: TRecordBuffer; virtual; abstract;
GetRecNonull126 function GetRecNo: Longint; virtual; abstract;
127 procedure SetRecNo(ARecNo: Longint); virtual; abstract;
128 public
129 DBCompareStruct : TDBCompareStruct;
130 Name : String;
131 FieldsName : String;
132 CaseinsFields : String;
133 DescFields : String;
134 Options : TIndexOptions;
135 IndNr : integer;
136
137 constructor Create(const ADataset : TCustomBufDataset); virtual;
ScrollBackwardnull138 function ScrollBackward : TGetResult; virtual; abstract;
ScrollForwardnull139 function ScrollForward : TGetResult; virtual; abstract;
GetCurrentnull140 function GetCurrent : TGetResult; virtual; abstract;
ScrollFirstnull141 function ScrollFirst : TGetResult; virtual; abstract;
142 procedure ScrollLast; virtual; abstract;
143 // Gets prior/next record relative to given bookmark; does not change current record
GetRecordnull144 function GetRecord(ABookmark: PBufBookmark; GetMode: TGetMode): TGetResult; virtual;
145
146 procedure SetToFirstRecord; virtual; abstract;
147 procedure SetToLastRecord; virtual; abstract;
148
149 procedure StoreCurrentRecord; virtual; abstract;
150 procedure RestoreCurrentRecord; virtual; abstract;
151
CanScrollForwardnull152 function CanScrollForward : Boolean; virtual; abstract;
153 procedure DoScrollForward; virtual; abstract;
154
155 procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); virtual; abstract;
156 procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); virtual; abstract;
157 procedure GotoBookmark(const ABookmark : PBufBookmark); virtual; abstract;
BookmarkValidnull158 function BookmarkValid(const ABookmark: PBufBookmark): boolean; virtual;
CompareBookmarksnull159 function CompareBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : integer; virtual;
SameBookmarksnull160 function SameBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : boolean; virtual;
161
162 procedure InitialiseIndex; virtual; abstract;
163
164 procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); virtual; abstract;
165 procedure ReleaseSpareRecord; virtual; abstract;
166
167 procedure BeginUpdate; virtual; abstract;
168 // Adds a record to the end of the index as the new last record (spare record)
169 // Normally only used in GetNextPacket
170 procedure AddRecord; virtual; abstract;
171 // Inserts a record before the current record, or if the record is sorted,
172 // inserts it in the proper position
173 procedure InsertRecordBeforeCurrentRecord(Const ARecord : TRecordBuffer); virtual; abstract;
174 procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); virtual; abstract;
175 procedure OrderCurrentRecord; virtual; abstract;
176 procedure EndUpdate; virtual; abstract;
177
178 property SpareRecord : TRecordBuffer read GetSpareRecord;
179 property SpareBuffer : TRecordBuffer read GetSpareBuffer;
180 property CurrentRecord : TRecordBuffer read GetCurrentRecord;
181 property CurrentBuffer : Pointer read GetCurrentBuffer;
182 property IsInitialized : boolean read GetIsInitialized;
183 property BookmarkSize : integer read GetBookmarkSize;
184 property RecNo : Longint read GetRecNo write SetRecNo;
185 end;
186
187 { TDoubleLinkedBufIndex }
188
189 TDoubleLinkedBufIndex = class(TBufIndex)
190 private
191 FCursOnFirstRec : boolean;
192
193 FStoredRecBuf : PBufRecLinkItem;
194 FCurrentRecBuf : PBufRecLinkItem;
195 protected
GetBookmarkSizenull196 function GetBookmarkSize: integer; override;
GetCurrentBuffernull197 function GetCurrentBuffer: Pointer; override;
GetCurrentRecordnull198 function GetCurrentRecord: TRecordBuffer; override;
GetIsInitializednull199 function GetIsInitialized: boolean; override;
GetSpareBuffernull200 function GetSpareBuffer: TRecordBuffer; override;
GetSpareRecordnull201 function GetSpareRecord: TRecordBuffer; override;
GetRecNonull202 function GetRecNo: Longint; override;
203 procedure SetRecNo(ARecNo: Longint); override;
204 public
205 FLastRecBuf : PBufRecLinkItem;
206 FFirstRecBuf : PBufRecLinkItem;
207 FNeedScroll : Boolean;
208
ScrollBackwardnull209 function ScrollBackward : TGetResult; override;
ScrollForwardnull210 function ScrollForward : TGetResult; override;
GetCurrentnull211 function GetCurrent : TGetResult; override;
ScrollFirstnull212 function ScrollFirst : TGetResult; override;
213 procedure ScrollLast; override;
GetRecordnull214 function GetRecord(ABookmark: PBufBookmark; GetMode: TGetMode): TGetResult; override;
215
216 procedure SetToFirstRecord; override;
217 procedure SetToLastRecord; override;
218
219 procedure StoreCurrentRecord; override;
220 procedure RestoreCurrentRecord; override;
221
CanScrollForwardnull222 function CanScrollForward : Boolean; override;
223 procedure DoScrollForward; override;
224
225 procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); override;
226 procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
227 procedure GotoBookmark(const ABookmark : PBufBookmark); override;
CompareBookmarksnull228 function CompareBookmarks(const ABookmark1, ABookmark2: PBufBookmark): integer; override;
SameBookmarksnull229 function SameBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : boolean; override;
230 procedure InitialiseIndex; override;
231
232 procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); override;
233 procedure ReleaseSpareRecord; override;
234
235 procedure BeginUpdate; override;
236 procedure AddRecord; override;
237 procedure InsertRecordBeforeCurrentRecord(Const ARecord : TRecordBuffer); override;
238 procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); override;
239 procedure OrderCurrentRecord; override;
240 procedure EndUpdate; override;
241 end;
242
243 { TUniDirectionalBufIndex }
244
245 TUniDirectionalBufIndex = class(TBufIndex)
246 private
247 FSPareBuffer: TRecordBuffer;
248 protected
GetBookmarkSizenull249 function GetBookmarkSize: integer; override;
GetCurrentBuffernull250 function GetCurrentBuffer: Pointer; override;
GetCurrentRecordnull251 function GetCurrentRecord: TRecordBuffer; override;
GetIsInitializednull252 function GetIsInitialized: boolean; override;
GetSpareBuffernull253 function GetSpareBuffer: TRecordBuffer; override;
GetSpareRecordnull254 function GetSpareRecord: TRecordBuffer; override;
GetRecNonull255 function GetRecNo: Longint; override;
256 procedure SetRecNo(ARecNo: Longint); override;
257 public
ScrollBackwardnull258 function ScrollBackward : TGetResult; override;
ScrollForwardnull259 function ScrollForward : TGetResult; override;
GetCurrentnull260 function GetCurrent : TGetResult; override;
ScrollFirstnull261 function ScrollFirst : TGetResult; override;
262 procedure ScrollLast; override;
263
264 procedure SetToFirstRecord; override;
265 procedure SetToLastRecord; override;
266
267 procedure StoreCurrentRecord; override;
268 procedure RestoreCurrentRecord; override;
269
CanScrollForwardnull270 function CanScrollForward : Boolean; override;
271 procedure DoScrollForward; override;
272
273 procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); override;
274 procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
275 procedure GotoBookmark(const ABookmark : PBufBookmark); override;
276
277 procedure InitialiseIndex; override;
278 procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); override;
279 procedure ReleaseSpareRecord; override;
280
281 procedure BeginUpdate; override;
282 procedure AddRecord; override;
283 procedure InsertRecordBeforeCurrentRecord(Const ARecord : TRecordBuffer); override;
284 procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); override;
285 procedure OrderCurrentRecord; override;
286 procedure EndUpdate; override;
287 end;
288
289
290 { TArrayBufIndex }
291
292 TArrayBufIndex = class(TBufIndex)
293 private
294 FStoredRecBuf : integer;
295
296 FInitialBuffers,
297 FGrowBuffer : integer;
GetRecordFromBookmarknull298 Function GetRecordFromBookmark(ABookmark: TBufBookmark) : integer;
299 protected
GetBookmarkSizenull300 function GetBookmarkSize: integer; override;
GetCurrentBuffernull301 function GetCurrentBuffer: Pointer; override;
GetCurrentRecordnull302 function GetCurrentRecord: TRecordBuffer; override;
GetIsInitializednull303 function GetIsInitialized: boolean; override;
GetSpareBuffernull304 function GetSpareBuffer: TRecordBuffer; override;
GetSpareRecordnull305 function GetSpareRecord: TRecordBuffer; override;
GetRecNonull306 function GetRecNo: Longint; override;
307 procedure SetRecNo(ARecNo: Longint); override;
308 public
309 FRecordArray : array of Pointer;
310 FCurrentRecInd : integer;
311 FLastRecInd : integer;
312 FNeedScroll : Boolean;
313 constructor Create(const ADataset: TCustomBufDataset); override;
ScrollBackwardnull314 function ScrollBackward : TGetResult; override;
ScrollForwardnull315 function ScrollForward : TGetResult; override;
GetCurrentnull316 function GetCurrent : TGetResult; override;
ScrollFirstnull317 function ScrollFirst : TGetResult; override;
318 procedure ScrollLast; override;
319
320 procedure SetToFirstRecord; override;
321 procedure SetToLastRecord; override;
322
323 procedure StoreCurrentRecord; override;
324 procedure RestoreCurrentRecord; override;
325
CanScrollForwardnull326 function CanScrollForward : Boolean; override;
327 procedure DoScrollForward; override;
328
329 procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); override;
330 procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
331 procedure GotoBookmark(const ABookmark : PBufBookmark); override;
332
333 procedure InitialiseIndex; override;
334
335 procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); override;
336 procedure ReleaseSpareRecord; override;
337
338 procedure BeginUpdate; override;
339 procedure AddRecord; override;
340 procedure InsertRecordBeforeCurrentRecord(Const ARecord : TRecordBuffer); override;
341 procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); override;
342 procedure EndUpdate; override;
343 end;
344
345
346 { TBufDatasetReader }
347
348 type
349 TRowStateValue = (rsvOriginal, rsvDeleted, rsvInserted, rsvUpdated, rsvDetailUpdates);
350 TRowState = set of TRowStateValue;
351
352 type
353
354 { TDataPacketReader }
355
356 TDataPacketFormat = (dfBinary,dfXML,dfXMLUTF8,dfAny,dfDefault);
357
358 TDatapacketReaderClass = class of TDatapacketReader;
359 TDataPacketReader = class(TObject)
360 FDataSet: TCustomBufDataset;
361 FStream : TStream;
362 protected
RowStateToBytenull363 class function RowStateToByte(const ARowState : TRowState) : byte;
ByteToRowStatenull364 class function ByteToRowState(const AByte : Byte) : TRowState;
365 procedure RestoreBlobField(AField: TField; ASource: pointer; ASize: integer);
366 property DataSet: TCustomBufDataset read FDataSet;
367 property Stream: TStream read FStream;
368 public
369 constructor Create(ADataSet: TCustomBufDataset; AStream : TStream); virtual;
370 // Load a dataset from stream:
371 // Load the field definitions from a stream.
372 procedure LoadFieldDefs(var AnAutoIncValue : integer); virtual; abstract;
373 // Is called before the records are loaded
374 procedure InitLoadRecords; virtual; abstract;
375 // Returns if there is at least one more record available in the stream
GetCurrentRecordnull376 function GetCurrentRecord : boolean; virtual; abstract;
377 // Return the RowState of the current record, and the order of the update
GetRecordRowStatenull378 function GetRecordRowState(out AUpdOrder : Integer) : TRowState; virtual; abstract;
379 // Store a record from stream in the current record buffer
380 procedure RestoreRecord; virtual; abstract;
381 // Move the stream to the next record
382 procedure GotoNextRecord; virtual; abstract;
383
384 // Store a dataset to stream:
385 // Save the field definitions to a stream.
386 procedure StoreFieldDefs(AnAutoIncValue : integer); virtual; abstract;
387 // Save a record from the current record buffer to the stream
388 procedure StoreRecord(ARowState : TRowState; AUpdOrder : integer = 0); virtual; abstract;
389 // Is called after all records are stored
390 procedure FinalizeStoreRecords; virtual; abstract;
391 // Checks if the provided stream is of the right format for this class
RecognizeStreamnull392 class function RecognizeStream(AStream : TStream) : boolean; virtual; abstract;
393 end;
394
395 { TFpcBinaryDatapacketReader }
396
397 { Data layout:
398 Header section:
399 Identification: 13 bytes: 'BinBufDataSet'
400 Version: 1 byte
401 Columns section:
402 Number of Fields: 2 bytes
403 For each FieldDef: Name, DisplayName, Size: 2 bytes, DataType: 2 bytes, ReadOnlyAttr: 1 byte
404 Parameter section:
405 AutoInc Value: 4 bytes
406 Rows section:
407 Row header: each row begins with $fe: 1 byte
408 row state: 1 byte (original, deleted, inserted, modified)
409 update order: 4 bytes
410 null bitmap: 1 byte per each 8 fields (if field is null corresponding bit is 1)
411 Row data: variable length data are prefixed with 4 byte length indicator
412 null fields are not stored (see: null bitmap)
413 }
414
415 TFpcBinaryDatapacketReader = class(TDataPacketReader)
416 private
417 const
418 FpcBinaryIdent1 = 'BinBufDataset'; // Old version 1; support for transient period;
419 FpcBinaryIdent2 = 'BinBufDataSet';
420 StringFieldTypes = [ftString,ftFixedChar,ftWideString,ftFixedWideChar];
421 BlobFieldTypes = [ftBlob,ftMemo,ftGraphic,ftWideMemo];
422 VarLenFieldTypes = StringFieldTypes + BlobFieldTypes + [ftBytes,ftVarBytes];
423 var
424 FNullBitmapSize: integer;
425 FNullBitmap: TBytes;
426 protected
427 var
428 FVersion: byte;
429 public
430 constructor Create(ADataSet: TCustomBufDataset; AStream : TStream); override;
431 procedure LoadFieldDefs(var AnAutoIncValue : integer); override;
432 procedure StoreFieldDefs(AnAutoIncValue : integer); override;
433 procedure InitLoadRecords; override;
GetCurrentRecordnull434 function GetCurrentRecord : boolean; override;
GetRecordRowStatenull435 function GetRecordRowState(out AUpdOrder : Integer) : TRowState; override;
436 procedure RestoreRecord; override;
437 procedure GotoNextRecord; override;
438 procedure StoreRecord(ARowState : TRowState; AUpdOrder : integer = 0); override;
439 procedure FinalizeStoreRecords; override;
RecognizeStreamnull440 class function RecognizeStream(AStream : TStream) : boolean; override;
441 end;
442
443 { TCustomBufDataset }
444
445 TCustomBufDataset = class(TDBDataSet)
446 Private
447 Type
448
449 { TBufDatasetIndex }
450 TIndexType = (itNormal,itDefault,itCustom);
451 TBufDatasetIndex = Class(TIndexDef)
452 private
453 FBufferIndex: TBufIndex;
454 FDiscardOnClose: Boolean;
455 FIndexType : TIndexType;
456 Public
457 Destructor Destroy; override;
458 // Free FBufferIndex;
459 Procedure Clearindex;
460 // Set TIndexDef properties on FBufferIndex;
461 Procedure SetIndexProperties;
462 // Return true if the buffer must be built.
463 // Default buffer must not be built, custom only when it is not the current.
MustBuildnull464 Function MustBuild(aCurrent : TBufDatasetIndex) : Boolean;
465 // Return true if the buffer must be updated
466 // This are all indexes except custom, unless it is the active index
IsActiveIndexnull467 Function IsActiveIndex(aCurrent : TBufDatasetIndex) : Boolean;
468 // The actual buffer.
469 Property BufferIndex : TBufIndex Read FBufferIndex Write FBufferIndex;
470 // If the Index is created after Open, then it will be discarded on close.
471 Property DiscardOnClose : Boolean Read FDiscardOnClose;
472 // Skip build of this index
473 Property IndexType : TIndexType Read FIndexType Write FIndexType;
474 end;
475
476 { TBufDatasetIndexDefs }
477 TBufDatasetIndexDefs = Class(TIndexDefs)
478 private
GetBufDatasetIndexnull479 function GetBufDatasetIndex(AIndex : Integer): TBufDatasetIndex;
GetBufferIndexnull480 function GetBufferIndex(AIndex : Integer): TBufIndex;
481 Public
482 Constructor Create(aDataset : TDataset); override;
483 // Does not raise an exception if not found.
FindIndexnull484 function FindIndex(const IndexName: string): TBufDatasetIndex;
485 Property BufIndexdefs [AIndex : Integer] : TBufDatasetIndex Read GetBufDatasetIndex;
486 Property BufIndexes [AIndex : Integer] : TBufIndex Read GetBufferIndex;
487 end;
488
489 procedure BuildCustomIndex;
GetBufIndexnull490 function GetBufIndex(Aindex : Integer): TBufIndex;
GetBufIndexDefnull491 function GetBufIndexDef(Aindex : Integer): TBufDatasetIndex;
GetCurrentIndexBufnull492 function GetCurrentIndexBuf: TBufIndex;
493 procedure InitUserIndexes;
494 private
495 FFileName: TFileName;
496 FReadFromFile : boolean;
497 FFileStream : TFileStream;
498 FDatasetReader : TDataPacketReader;
499 FMaxIndexesCount: integer;
500 FDefaultIndex,
501 FCurrentIndexDef : TBufDatasetIndex;
502 FFilterBuffer : TRecordBuffer;
503 FBRecordCount : integer;
504 FReadOnly : Boolean;
505 FSavedState : TDatasetState;
506 FPacketRecords : integer;
507 FRecordSize : Integer;
508 FIndexFieldNames : String;
509 FIndexName : String;
510 FNullmaskSize : byte;
511 FOpen : Boolean;
512 FUpdateBuffer : TRecordsUpdateBuffer;
513 FCurrentUpdateBuffer : integer;
514 FAutoIncValue : longint;
515 FAutoIncField : TAutoIncField;
516 FIndexes : TBufDataSetIndexDefs;
517 FParser : TBufDatasetParser;
518 FFieldBufPositions : array of longint;
519 FAllPacketsFetched : boolean;
520 FOnUpdateError : TResolverErrorEvent;
521
522 FBlobBuffers : array of PBlobBuffer;
523 FUpdateBlobBuffers: array of PBlobBuffer;
524 FManualMergeChangeLog : Boolean;
525 FRefreshing : Boolean;
526
527 procedure ProcessFieldsToCompareStruct(const AFields, ADescFields, ACInsFields: TList;
528 const AIndexOptions: TIndexOptions; const ALocateOptions: TLocateOptions; out ACompareStruct: TDBCompareStruct);
BufferOffsetnull529 function BufferOffset: integer;
GetFieldSizenull530 function GetFieldSize(FieldDef : TFieldDef) : longint;
531 procedure CalcRecordSize;
IntAllocRecordBuffernull532 function IntAllocRecordBuffer: TRecordBuffer;
533 procedure IntLoadFieldDefsFromFile;
534 procedure IntLoadRecordsFromFile;
GetCurrentBuffernull535 function GetCurrentBuffer: TRecordBuffer;
536 procedure CurrentRecordToBuffer(Buffer: TRecordBuffer);
LoadBuffernull537 function LoadBuffer(Buffer : TRecordBuffer): TGetResult;
538 procedure FetchAll;
GetRecordUpdateBuffernull539 function GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludePrior : boolean = false; AFindNext : boolean = false) : boolean;
GetRecordUpdateBufferCachednull540 function GetRecordUpdateBufferCached(const ABookmark : TBufBookmark; IncludePrior : boolean = false) : boolean;
GetActiveRecordUpdateBuffernull541 function GetActiveRecordUpdateBuffer : boolean;
542 procedure CancelRecordUpdateBuffer(AUpdateBufferIndex: integer; var ABookmark: TBufBookmark);
543 procedure ParseFilter(const AFilter: string);
544
GetBufUniDirectionalnull545 function GetBufUniDirectional: boolean;
546 // indexes handling
GetIndexDefsnull547 function GetIndexDefs : TIndexDefs;
GetIndexFieldNamesnull548 function GetIndexFieldNames: String;
GetIndexNamenull549 function GetIndexName: String;
550 procedure SetIndexFieldNames(const AValue: String);
551 procedure SetIndexName(AValue: String);
552 procedure SetMaxIndexesCount(const AValue: Integer);
553 procedure SetBufUniDirectional(const AValue: boolean);
DefaultIndexnull554 Function DefaultIndex : TBufDatasetIndex;
DefaultBufferIndexnull555 Function DefaultBufferIndex : TBufIndex;
556 procedure InitDefaultIndexes;
557 procedure BuildIndex(AIndex : TBufIndex);
558 procedure BuildIndexes;
559 procedure RemoveRecordFromIndexes(const ABookmark : TBufBookmark);
560 procedure InternalCreateIndex(F: TBufDataSetIndex); virtual;
561 Property CurrentIndexBuf : TBufIndex Read GetCurrentIndexBuf;
562 Property CurrentIndexDef : TBufDatasetIndex Read FCurrentIndexDef;
563 Property BufIndexDefs[Aindex : Integer] : TBufDatasetIndex Read GetBufIndexDef;
564 Property BufIndexes[Aindex : Integer] : TBufIndex Read GetBufIndex;
565 protected
566 // abstract & virtual methods of TDataset
DefaultReadFileFormatnull567 class function DefaultReadFileFormat : TDataPacketFormat; virtual;
DefaultWriteFileFormatnull568 class function DefaultWriteFileFormat : TDataPacketFormat; virtual;
DefaultPacketClassnull569 class function DefaultPacketClass : TDataPacketReaderClass ; virtual;
CreateDefaultPacketReadernull570 function CreateDefaultPacketReader(aStream : TStream): TDataPacketReader ; virtual;
571 procedure SetPacketRecords(aValue : integer); virtual;
572 procedure SetRecNo(Value: Longint); override;
GetRecNonull573 function GetRecNo: Longint; override;
GetChangeCountnull574 function GetChangeCount: integer; virtual;
AllocRecordBuffernull575 function AllocRecordBuffer: TRecordBuffer; override;
576 procedure FreeRecordBuffer(var Buffer: TRecordBuffer); override;
577 procedure ClearCalcFields(Buffer: TRecordBuffer); override;
578 procedure InternalInitRecord(Buffer: TRecordBuffer); override;
GetCanModifynull579 function GetCanModify: Boolean; override;
GetRecordnull580 function GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
581 procedure DoBeforeClose; override;
582 procedure InternalInitFieldDefs; override;
583 procedure InternalOpen; override;
584 procedure InternalClose; override;
GetRecordSizenull585 function GetRecordSize: Word; override;
586 procedure InternalPost; override;
587 procedure InternalCancel; Override;
588 procedure InternalDelete; override;
589 procedure InternalFirst; override;
590 procedure InternalLast; override;
591 procedure InternalSetToRecord(Buffer: TRecordBuffer); override;
592 procedure InternalGotoBookmark(ABookmark: Pointer); override;
593 procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
594 procedure SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag); override;
595 procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
GetBookmarkFlagnull596 function GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag; override;
IsCursorOpennull597 function IsCursorOpen: Boolean; override;
GetRecordCountnull598 function GetRecordCount: Longint; override;
599 procedure ApplyRecUpdate(UpdateKind : TUpdateKind); virtual;
600 procedure SetOnUpdateError(const AValue: TResolverErrorEvent);
601 procedure SetFilterText(const Value: String); override; {virtual;}
602 procedure SetFiltered(Value: Boolean); override; {virtual;}
603 procedure InternalRefresh; override;
604 procedure DataEvent(Event: TDataEvent; Info: PtrInt); override;
605 // virtual or methods, which can be used by descendants
GetNewBlobBuffernull606 function GetNewBlobBuffer : PBlobBuffer;
GetNewWriteBlobBuffernull607 function GetNewWriteBlobBuffer : PBlobBuffer;
608 procedure FreeBlobBuffer(var ABlobBuffer: PBlobBuffer);
InternalAddIndexnull609 Function InternalAddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string;
610 const ACaseInsFields: string) : TBufDatasetIndex; virtual;
611 procedure BeforeRefreshOpenCursor; virtual;
612 procedure DoFilterRecord(out Acceptable: Boolean); virtual;
613 procedure SetReadOnly(AValue: Boolean); virtual;
IsReadFromPacketnull614 function IsReadFromPacket : Boolean;
getnextpacketnull615 function getnextpacket : integer;
GetPacketReadernull616 function GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader; virtual;
617 // abstracts, must be overidden by descendents
Fetchnull618 function Fetch : boolean; virtual;
LoadFieldnull619 function LoadField(FieldDef : TFieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; virtual;
620 procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField); virtual; abstract;
DoLocatenull621 function DoLocate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoEvents : Boolean) : boolean;
622 Property Refreshing : Boolean Read FRefreshing;
623 public
624 constructor Create(AOwner: TComponent); override;
GetFieldDatanull625 function GetFieldData(Field: TField; Buffer: Pointer;
626 NativeFormat: Boolean): Boolean; override;
GetFieldDatanull627 function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
628 procedure SetFieldData(Field: TField; Buffer: Pointer;
629 NativeFormat: Boolean); override;
630 procedure SetFieldData(Field: TField; Buffer: Pointer); override;
631 procedure ApplyUpdates; virtual; overload;
632 procedure ApplyUpdates(MaxErrors: Integer); virtual; overload;
633 procedure MergeChangeLog;
634 procedure RevertRecord;
635 procedure CancelUpdates; virtual;
636 destructor Destroy; override;
Locatenull637 function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean; override;
Lookupnull638 function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant; override;
UpdateStatusnull639 function UpdateStatus: TUpdateStatus; override;
CreateBlobStreamnull640 function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
641 procedure AddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string = '';
642 const ACaseInsFields: string = ''); virtual;
643 procedure ClearIndexes;
644
645 procedure SetDatasetPacket(AReader : TDataPacketReader);
646 procedure GetDatasetPacket(AWriter : TDataPacketReader);
647 procedure LoadFromStream(AStream : TStream; Format: TDataPacketFormat = dfDefault);
648 procedure SaveToStream(AStream : TStream; Format: TDataPacketFormat = dfBinary);
649 procedure LoadFromFile(AFileName: string = ''; Format: TDataPacketFormat = dfDefault);
650 procedure SaveToFile(AFileName: string = ''; Format: TDataPacketFormat = dfBinary);
651 procedure CreateDataset;
652 Procedure Clear; // Will close and remove all field definitions.
BookmarkValidnull653 function BookmarkValid(ABookmark: TBookmark): Boolean; override;
CompareBookmarksnull654 function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint; override;
655 Procedure CopyFromDataset(DataSet : TDataSet;CopyData : Boolean=True);
656 property ChangeCount : Integer read GetChangeCount;
657 property MaxIndexesCount : Integer read FMaxIndexesCount write SetMaxIndexesCount default 2;
658 property ReadOnly : Boolean read FReadOnly write SetReadOnly default false;
659 property ManualMergeChangeLog : Boolean read FManualMergeChangeLog write FManualMergeChangeLog default False;
660 published
661 property FileName : TFileName read FFileName write FFileName;
662 property PacketRecords : Integer read FPacketRecords write SetPacketRecords default 10;
663 property OnUpdateError: TResolverErrorEvent read FOnUpdateError write SetOnUpdateError;
664 property IndexDefs : TIndexDefs read GetIndexDefs;
665 property IndexName : String read GetIndexName write SetIndexName;
666 property IndexFieldNames : String read GetIndexFieldNames write SetIndexFieldNames;
667 property UniDirectional: boolean read GetBufUniDirectional write SetBufUniDirectional default False;
668 end;
669
670 TBufDataset = class(TCustomBufDataset)
671 published
672 property MaxIndexesCount;
673 // TDataset stuff
674 property FieldDefs;
675 Property Active;
676 Property AutoCalcFields;
677 Property Filter;
678 Property Filtered;
679 Property ReadOnly;
680 Property AfterCancel;
681 Property AfterClose;
682 Property AfterDelete;
683 Property AfterEdit;
684 Property AfterInsert;
685 Property AfterOpen;
686 Property AfterPost;
687 Property AfterScroll;
688 Property BeforeCancel;
689 Property BeforeClose;
690 Property BeforeDelete;
691 Property BeforeEdit;
692 Property BeforeInsert;
693 Property BeforeOpen;
694 Property BeforePost;
695 Property BeforeScroll;
696 Property OnCalcFields;
697 Property OnDeleteError;
698 Property OnEditError;
699 Property OnFilterRecord;
700 Property OnNewRecord;
701 Property OnPostError;
702 end;
703
704
705 procedure RegisterDatapacketReader(ADatapacketReaderClass : TDatapacketReaderClass; AFormat : TDataPacketFormat);
706
707 implementation
708
709 uses variants, dbconst, FmtBCD, strutils;
710
711 Const
712 SDefaultIndex = 'DEFAULT_ORDER';
713 SCustomIndex = 'CUSTOM_ORDER';
714 Desc=' DESC'; //leading space is important
715 LenDesc : integer = Length(Desc);
716 Limiter=';';
717
718 Type
719 TDatapacketReaderRegistration = record
720 ReaderClass : TDatapacketReaderClass;
721 Format : TDataPacketFormat;
722 end;
723
724 var
725 RegisteredDatapacketReaders : Array of TDatapacketReaderRegistration;
726
727 procedure RegisterDatapacketReader(ADatapacketReaderClass : TDatapacketReaderClass; AFormat : TDataPacketFormat);
728
729 begin
730 setlength(RegisteredDatapacketReaders,length(RegisteredDatapacketReaders)+1);
731 with RegisteredDatapacketReaders[length(RegisteredDatapacketReaders)-1] do
732 begin
733 Readerclass := ADatapacketReaderClass;
734 Format := AFormat;
735 end;
736 end;
737
GetRegisterDatapacketReadernull738 function GetRegisterDatapacketReader(AStream : TStream; AFormat : TDataPacketFormat; out ADataReaderClass : TDatapacketReaderRegistration) : boolean;
739
740 var
741 i : integer;
742
743 begin
744 Result := False;
745 for i := 0 to length(RegisteredDatapacketReaders)-1 do
746 if ((AFormat=dfAny) or (AFormat=RegisteredDatapacketReaders[i].Format)) then
747 begin
748 if (AStream=nil) or (RegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then
749 begin
750 ADataReaderClass := RegisteredDatapacketReaders[i];
751 Result := True;
752 if (AStream <> nil) then AStream.Seek(0,soFromBeginning);
753 break;
754 end;
755 AStream.Seek(0,soFromBeginning);
756 end;
757 end;
758
DBCompareTextnull759 function DBCompareText(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
760
761 begin
762 if [loCaseInsensitive,loPartialKey]=options then
763 Result := AnsiStrLIComp(pchar(subValue),pchar(aValue),length(pchar(subValue)))
764 else if [loPartialKey] = options then
765 Result := AnsiStrLComp(pchar(subValue),pchar(aValue),length(pchar(subValue)))
766 else if [loCaseInsensitive] = options then
767 Result := AnsiCompareText(pchar(subValue),pchar(aValue))
768 else
769 Result := AnsiCompareStr(pchar(subValue),pchar(aValue));
770 end;
771
DBCompareWideTextnull772 function DBCompareWideText(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
773
774 begin
775 if [loCaseInsensitive,loPartialKey]=options then
776 Result := WideCompareText(pwidechar(subValue),LeftStr(pwidechar(aValue), Length(pwidechar(subValue))))
777 else if [loPartialKey] = options then
778 Result := WideCompareStr(pwidechar(subValue),LeftStr(pwidechar(aValue), Length(pwidechar(subValue))))
779 else if [loCaseInsensitive] = options then
780 Result := WideCompareText(pwidechar(subValue),pwidechar(aValue))
781 else
782 Result := WideCompareStr(pwidechar(subValue),pwidechar(aValue));
783 end;
784
DBCompareBytenull785 function DBCompareByte(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
786
787 begin
788 Result := PByte(subValue)^-PByte(aValue)^;
789 end;
790
DBCompareSmallIntnull791 function DBCompareSmallInt(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
792
793 begin
794 Result := PSmallInt(subValue)^-PSmallInt(aValue)^;
795 end;
796
DBCompareIntnull797 function DBCompareInt(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
798
799 begin
800 Result := PInteger(subValue)^-PInteger(aValue)^;
801 end;
802
DBCompareLargeIntnull803 function DBCompareLargeInt(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
804
805 begin
806 // A simple subtraction doesn't work, since it could be that the result
807 // doesn't fit into a LargeInt
808 if PLargeInt(subValue)^ < PLargeInt(aValue)^ then
809 result := -1
810 else if PLargeInt(subValue)^ > PLargeInt(aValue)^ then
811 result := 1
812 else
813 result := 0;
814 end;
815
DBCompareWordnull816 function DBCompareWord(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
817
818 begin
819 Result := PWord(subValue)^-PWord(aValue)^;
820 end;
821
DBCompareQWordnull822 function DBCompareQWord(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
823
824 begin
825 // A simple subtraction doesn't work, since it could be that the result
826 // doesn't fit into a LargeInt
827 if PQWord(subValue)^ < PQWord(aValue)^ then
828 result := -1
829 else if PQWord(subValue)^ > PQWord(aValue)^ then
830 result := 1
831 else
832 result := 0;
833 end;
834
DBCompareDoublenull835 function DBCompareDouble(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
836 begin
837 // A simple subtraction doesn't work, since it could be that the result
838 // doesn't fit into a LargeInt
839 if PDouble(subValue)^ < PDouble(aValue)^ then
840 result := -1
841 else if PDouble(subValue)^ > PDouble(aValue)^ then
842 result := 1
843 else
844 result := 0;
845 end;
846
DBCompareBCDnull847 function DBCompareBCD(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
848 begin
849 result:=BCDCompare(PBCD(subValue)^, PBCD(aValue)^);
850 end;
851
DBCompareBytesnull852 function DBCompareBytes(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
853 begin
854 Result := CompareByte(subValue^, aValue^, size);
855 end;
856
DBCompareVarBytesnull857 function DBCompareVarBytes(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
858 var len1, len2: LongInt;
859 begin
860 len1 := PWord(subValue)^;
861 len2 := PWord(aValue)^;
862 inc(subValue, sizeof(Word));
863 inc(aValue, sizeof(Word));
864 if len1 > len2 then
865 Result := CompareByte(subValue^, aValue^, len2)
866 else
867 Result := CompareByte(subValue^, aValue^, len1);
868 if Result = 0 then
869 Result := len1 - len2;
870 end;
871
872 procedure unSetFieldIsNull(NullMask : pbyte;x : longint); //inline;
873 begin
874 NullMask[x div 8] := (NullMask[x div 8]) and not (1 shl (x mod 8));
875 end;
876
877 procedure SetFieldIsNull(NullMask : pbyte;x : longint); //inline;
878 begin
879 NullMask[x div 8] := (NullMask[x div 8]) or (1 shl (x mod 8));
880 end;
881
GetFieldIsNullnull882 function GetFieldIsNull(NullMask : pbyte;x : longint) : boolean; //inline;
883 begin
884 result := ord(NullMask[x div 8]) and (1 shl (x mod 8)) > 0
885 end;
886
IndexCompareRecordsnull887 function IndexCompareRecords(Rec1,Rec2 : pointer; ADBCompareRecs : TDBCompareStruct) : LargeInt;
888 var IndexFieldNr : Integer;
889 IsNull1, IsNull2 : boolean;
890 begin
891 for IndexFieldNr:=0 to length(ADBCompareRecs)-1 do with ADBCompareRecs[IndexFieldNr] do
892 begin
893 IsNull1:=GetFieldIsNull(rec1+NullBOff,FieldInd);
894 IsNull2:=GetFieldIsNull(rec2+NullBOff,FieldInd);
895 if IsNull1 and IsNull2 then
896 Result := 0
897 else if IsNull1 then
898 Result := -1
899 else if IsNull2 then
900 Result := 1
901 else
902 Result := CompareFunc(Rec1+Off, Rec2+Off, Size, Options);
903
904 if Result <> 0 then
905 begin
906 if Desc then
907 Result := -Result;
908 break;
909 end;
910 end;
911 end;
912
913 { TCustomBufDataset.TBufDatasetIndex }
914
915 destructor TCustomBufDataset.TBufDatasetIndex.Destroy;
916 begin
917 ClearIndex;
918 inherited Destroy;
919 end;
920
921 procedure TCustomBufDataset.TBufDatasetIndex.Clearindex;
922 begin
923 FreeAndNil(FBufferIndex);
924 end;
925
926 procedure TCustomBufDataset.TBufDatasetIndex.SetIndexProperties;
927 begin
928 If not Assigned(FBufferIndex) then
929 exit;
930 FBufferIndex.IndNr:=Index;
931 FBufferIndex.Name:=Name;
932 FBufferIndex.FieldsName:=Fields;
933 FBufferIndex.DescFields:=DescFields;
934 FBufferIndex.CaseinsFields:=CaseInsFields;
935 FBufferIndex.Options:=Options;
936 end;
937
TCustomBufDataset.TBufDatasetIndex.MustBuildnull938 function TCustomBufDataset.TBufDatasetIndex.MustBuild(aCurrent: TBufDatasetIndex): Boolean;
939 begin
940 Result:=(FIndexType<>itDefault) and IsActiveIndex(aCurrent);
941 end;
942
TCustomBufDataset.TBufDatasetIndex.IsActiveIndexnull943 function TCustomBufDataset.TBufDatasetIndex.IsActiveIndex(aCurrent: TBufDatasetIndex): Boolean;
944 begin
945 Result:=(FIndexType<>itCustom) or (Self=aCurrent);
946 end;
947
948
949 { TCustomBufDataset.TBufDatasetIndexDefs }
950
TBufDatasetIndexDefsnull951 function TCustomBufDataset.TBufDatasetIndexDefs.GetBufDatasetIndex(AIndex : Integer): TBufDatasetIndex;
952 begin
953 Result:=Items[Aindex] as TBufDatasetIndex;
954 end;
955
TBufDatasetIndexDefsnull956 function TCustomBufDataset.TBufDatasetIndexDefs.GetBufferIndex(AIndex : Integer): TBufIndex;
957 begin
958 Result:=BufIndexdefs[AIndex].BufferIndex;
959 end;
960
961 constructor TCustomBufDataset.TBufDatasetIndexDefs.Create(aDataset: TDataset);
962 begin
963 inherited Create(aDataset,aDataset,TBufDatasetIndex);
964 end;
965
TCustomBufDataset.TBufDatasetIndexDefs.FindIndexnull966 function TCustomBufDataset.TBufDatasetIndexDefs.FindIndex(const IndexName: string): TBufDatasetIndex;
967
968 Var
969 I: Integer;
970
971 begin
972 I:=IndexOf(IndexName);
973 if I<>-1 then
974 Result:=BufIndexdefs[I]
975 else
976 Result:=Nil;
977 end;
978
979 { ---------------------------------------------------------------------
980 TCustomBufDataset
981 ---------------------------------------------------------------------}
982
983 constructor TCustomBufDataset.Create(AOwner : TComponent);
984 begin
985 Inherited Create(AOwner);
986 FManualMergeChangeLog := False;
987 FMaxIndexesCount:=2;
988 FIndexes:=TBufDatasetIndexDefs.Create(Self);
989 FAutoIncValue:=-1;
990 SetLength(FUpdateBuffer,0);
991 SetLength(FBlobBuffers,0);
992 SetLength(FUpdateBlobBuffers,0);
993 FParser := nil;
994 FPacketRecords := 10;
995 end;
996
997 procedure TCustomBufDataset.SetPacketRecords(aValue : integer);
998 begin
999 if (aValue = -1) or (aValue > 0) then
1000 begin
1001 if (IndexFieldNames<>'') and (aValue<>-1) then
1002 DatabaseError(SInvPacketRecordsValueFieldNames)
1003 else
1004 if UniDirectional and (aValue=-1) then
1005 DatabaseError(SInvPacketRecordsValueUniDirectional)
1006 else
1007 FPacketRecords := aValue
1008 end
1009 else
1010 DatabaseError(SInvPacketRecordsValue);
1011 end;
1012
1013 destructor TCustomBufDataset.Destroy;
1014
1015 begin
1016 if Active then Close;
1017 SetLength(FUpdateBuffer,0);
1018 SetLength(FBlobBuffers,0);
1019 SetLength(FUpdateBlobBuffers,0);
1020 ClearIndexes;
1021 FreeAndNil(FIndexes);
1022 inherited destroy;
1023 end;
1024
1025 procedure TCustomBufDataset.FetchAll;
1026 begin
1027 repeat
1028 until (getnextpacket < FPacketRecords) or (FPacketRecords = -1);
1029 end;
1030
1031 {
1032 // Code to dump raw dataset data, including indexes information, useful for debugging
1033 procedure DumpRawMem(const Data: pointer; ALength: PtrInt);
1034 var
1035 b: integer;
1036 s1,s2: string;
1037 begin
1038 s1 := '';
1039 s2 := '';
1040 for b := 0 to ALength-1 do
1041 begin
1042 s1 := s1 + ' ' + hexStr(pbyte(Data)[b],2);
1043 if pchar(Data)[b] in ['a'..'z','A'..'Z','1'..'9',' '..'/',':'..'@'] then
1044 s2 := s2 + pchar(Data)[b]
1045 else
1046 s2 := s2 + '.';
1047 if length(s2)=16 then
1048 begin
1049 write(' ',s1,' ');
1050 writeln(s2);
1051 s1 := '';
1052 s2 := '';
1053 end;
1054 end;
1055 write(' ',s1,' ');
1056 writeln(s2);
1057 end;
1058
1059 procedure DumpRecord(Dataset: TCustomBufDataset; RecBuf: PBufRecLinkItem; RawData: boolean = false);
1060 var ptr: pointer;
1061 NullMask: pointer;
1062 FieldData: pointer;
1063 NullMaskSize: integer;
1064 i: integer;
1065 begin
1066 if RawData then
1067 DumpRawMem(RecBuf,Dataset.RecordSize)
1068 else
1069 begin
1070 ptr := RecBuf;
1071 NullMask:= ptr + (sizeof(TBufRecLinkItem)*Dataset.MaxIndexesCount);
1072 NullMaskSize := 1+(Dataset.Fields.Count-1) div 8;
1073 FieldData:= ptr + (sizeof(TBufRecLinkItem)*Dataset.MaxIndexesCount) +NullMaskSize;
1074 write('record: $',hexstr(ptr),' nullmask: $');
1075 for i := 0 to NullMaskSize-1 do
1076 write(hexStr(byte((NullMask+i)^),2));
1077 write('=');
1078 for i := 0 to NullMaskSize-1 do
1079 write(binStr(byte((NullMask+i)^),8));
1080 writeln('%');
1081 for i := 0 to Dataset.MaxIndexesCount-1 do
1082 writeln(' ','Index ',inttostr(i),' Prior rec: ' + hexstr(pointer((ptr+(i*2)*sizeof(ptr))^)) + ' Next rec: ' + hexstr(pointer((ptr+((i*2)+1)*sizeof(ptr))^)));
1083 DumpRawMem(FieldData,Dataset.RecordSize-((sizeof(TBufRecLinkItem)*Dataset.MaxIndexesCount) +NullMaskSize));
1084 end;
1085 end;
1086
1087 procedure DumpDataset(AIndex: TBufIndex;RawData: boolean = false);
1088 var RecBuf: PBufRecLinkItem;
1089 begin
1090 writeln('Dump records, order based on index ',AIndex.IndNr);
1091 writeln('Current record:',hexstr(AIndex.CurrentRecord));
1092
1093 RecBuf:=(AIndex as TDoubleLinkedBufIndex).FFirstRecBuf;
1094 while RecBuf<>(AIndex as TDoubleLinkedBufIndex).FLastRecBuf do
1095 begin
1096 DumpRecord(AIndex.FDataset,RecBuf,RawData);
1097 RecBuf:=RecBuf[(AIndex as TDoubleLinkedBufIndex).IndNr].next;
1098 end;
1099 end;
1100 }
1101
1102 procedure TCustomBufDataset.BuildIndex(AIndex: TBufIndex);
1103
1104 var PCurRecLinkItem : PBufRecLinkItem;
1105 p,l,q : PBufRecLinkItem;
1106 i,k,psize,qsize : integer;
1107 myIdx,defIdx : Integer;
1108 MergeAmount : integer;
1109 PlaceQRec : boolean;
1110
1111 IndexFields : TList;
1112 DescIndexFields : TList;
1113 CInsIndexFields : TList;
1114
1115 Index0,
1116 DblLinkIndex : TDoubleLinkedBufIndex;
1117
1118 procedure PlaceNewRec(var e: PBufRecLinkItem; var esize: integer);
1119 begin
1120 if DblLinkIndex.FFirstRecBuf=nil then
1121 begin
1122 DblLinkIndex.FFirstRecBuf:=e;
1123 e[myIdx].prior:=nil;
1124 l:=e;
1125 end
1126 else
1127 begin
1128 l[myIdx].next:=e;
1129 e[myIdx].prior:=l;
1130 l:=e;
1131 end;
1132 e := e[myIdx].next;
1133 dec(esize);
1134 end;
1135
1136 begin
1137 // Build the DBCompareStructure
1138 // One AS is enough, and makes debugging easier.
1139 DblLinkIndex:=(AIndex as TDoubleLinkedBufIndex);
1140 Index0:=DefaultIndex.BufferIndex as TDoubleLinkedBufIndex;
1141 myIdx:=DblLinkIndex.IndNr;
1142 defIdx:=Index0.IndNr;
1143 with DblLinkIndex do
1144 begin
1145 IndexFields := TList.Create;
1146 DescIndexFields := TList.Create;
1147 CInsIndexFields := TList.Create;
1148 try
1149 GetFieldList(IndexFields,FieldsName);
1150 GetFieldList(DescIndexFields,DescFields);
1151 GetFieldList(CInsIndexFields,CaseinsFields);
1152 if IndexFields.Count=0 then
1153 DatabaseErrorFmt(SNoIndexFieldNameGiven,[DblLinkIndex.Name],Self);
1154 ProcessFieldsToCompareStruct(IndexFields, DescIndexFields, CInsIndexFields, Options, [], DBCompareStruct);
1155 finally
1156 CInsIndexFields.Free;
1157 DescIndexFields.Free;
1158 IndexFields.Free;
1159 end;
1160 end;
1161
1162 // This simply copies the index...
1163 PCurRecLinkItem:=Index0.FFirstRecBuf;
1164 PCurRecLinkItem[myIdx].next := PCurRecLinkItem[defIdx].next;
1165 PCurRecLinkItem[myIdx].prior := PCurRecLinkItem[defIdx].prior;
1166
1167 if PCurRecLinkItem <> Index0.FLastRecBuf then
1168 begin
1169 while PCurRecLinkItem[defIdx].next<>Index0.FLastRecBuf do
1170 begin
1171 PCurRecLinkItem:=PCurRecLinkItem[defIdx].next;
1172
1173 PCurRecLinkItem[myIdx].next := PCurRecLinkItem[defIdx].next;
1174 PCurRecLinkItem[myIdx].prior := PCurRecLinkItem[defIdx].prior;
1175 end;
1176 end
1177 else
1178 // Empty dataset
1179 Exit;
1180
1181 // Set FirstRecBuf and FCurrentRecBuf
1182 DblLinkIndex.FFirstRecBuf:=Index0.FFirstRecBuf;
1183 DblLinkIndex.FCurrentRecBuf:=DblLinkIndex.FFirstRecBuf;
1184 // Link in the FLastRecBuf that belongs to this index
1185 PCurRecLinkItem[myIdx].next:=DblLinkIndex.FLastRecBuf;
1186 DblLinkIndex.FLastRecBuf[myIdx].prior:=PCurRecLinkItem;
1187
1188 // Mergesort. Used the algorithm as described here by Simon Tatham
1189 // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
1190 // The comments in the code are from this website.
1191
1192 // In each pass, we are merging lists of size K into lists of size 2K.
1193 // (Initially K equals 1.)
1194 k:=1;
1195
1196 repeat
1197
1198 // So we start by pointing a temporary pointer p at the head of the list,
1199 // and also preparing an empty list L which we will add elements to the end
1200 // of as we finish dealing with them.
1201 p := DblLinkIndex.FFirstRecBuf;
1202 DblLinkIndex.FFirstRecBuf := nil;
1203 q := p;
1204 MergeAmount := 0;
1205
1206 // Then:
1207 // * If p is null, terminate this pass.
1208 while p <> DblLinkIndex.FLastRecBuf do
1209 begin
1210
1211 // * Otherwise, there is at least one element in the next pair of length-K
1212 // lists, so increment the number of merges performed in this pass.
1213 inc(MergeAmount);
1214
1215 // * Point another temporary pointer, q, at the same place as p. Step q along
1216 // the list by K places, or until the end of the list, whichever comes
1217 // first. Let psize be the number of elements you managed to step q past.
1218 i:=0;
1219 while (i<k) and (q<>DblLinkIndex.FLastRecBuf) do
1220 begin
1221 inc(i);
1222 q := q[myIDx].next;
1223 end;
1224 psize :=i;
1225
1226 // * Let qsize equal K. Now we need to merge a list starting at p, of length
1227 // psize, with a list starting at q of length at most qsize.
1228 qsize:=k;
1229
1230 // * So, as long as either the p-list is non-empty (psize > 0) or the q-list
1231 // is non-empty (qsize > 0 and q points to something non-null):
1232 while (psize>0) or ((qsize>0) and (q <> DblLinkIndex.FLastRecBuf)) do
1233 begin
1234 // * Choose which list to take the next element from. If either list
1235 // is empty, we must choose from the other one. (By assumption, at
1236 // least one is non-empty at this point.) If both lists are
1237 // non-empty, compare the first element of each and choose the lower
1238 // one. If the first elements compare equal, choose from the p-list.
1239 // (This ensures that any two elements which compare equal are never
1240 // swapped, so stability is guaranteed.)
1241 if (psize=0) then
1242 PlaceQRec := true
1243 else if (qsize=0) or (q = DblLinkIndex.FLastRecBuf) then
1244 PlaceQRec := False
1245 else if IndexCompareRecords(p,q,DblLinkIndex.DBCompareStruct) <= 0 then
1246 PlaceQRec := False
1247 else
1248 PlaceQRec := True;
1249
1250 // * Remove that element, e, from the start of its list, by advancing
1251 // p or q to the next element along, and decrementing psize or qsize.
1252 // * Add e to the end of the list L we are building up.
1253 if PlaceQRec then
1254 PlaceNewRec(q,qsize)
1255 else
1256 PlaceNewRec(p,psize);
1257 end;
1258
1259 // * Now we have advanced p until it is where q started out, and we have
1260 // advanced q until it is pointing at the next pair of length-K lists to
1261 // merge. So set p to the value of q, and go back to the start of this loop.
1262 p:=q;
1263 end;
1264
1265 // As soon as a pass like this is performed and only needs to do one merge, the
1266 // algorithm terminates, and the output list L is sorted. Otherwise, double the
1267 // value of K, and go back to the beginning.
1268
1269 l[myIdx].next:=DblLinkIndex.FLastRecBuf;
1270
1271 k:=k*2;
1272
1273 until MergeAmount = 1;
1274 DblLinkIndex.FLastRecBuf[myIdx].next:=DblLinkIndex.FFirstRecBuf;
1275 DblLinkIndex.FLastRecBuf[myIdx].prior:=l;
1276 end;
1277
1278 procedure TCustomBufDataset.BuildIndexes;
1279
1280 var
1281 i: integer;
1282
1283 begin
1284 for i:=0 to FIndexes.Count-1 do
1285 if BufIndexDefs[i].MustBuild(FCurrentIndexDef) then
1286 BuildIndex(BufIndexes[i]);
1287 end;
1288
1289 procedure TCustomBufDataset.ClearIndexes;
1290
1291 var
1292 i:integer;
1293
1294 begin
1295 CheckInactive;
1296 For I:=0 to FIndexes.Count-1 do
1297 BufIndexDefs[i].Clearindex;
1298 end;
1299
1300 procedure TCustomBufDataset.RemoveRecordFromIndexes(const ABookmark: TBufBookmark);
1301
1302 var
1303 i: integer;
1304 F : TBufDatasetIndex;
1305
1306 begin
1307 for i:=0 to FIndexes.Count-1 do
1308 begin
1309 F:=BufIndexDefs[i];
1310 if F.IsActiveIndex(FCurrentIndexDef) then
1311 F.BufferIndex.RemoveRecordFromIndex(ABookmark);
1312 end;
1313 end;
1314
GetIndexDefsnull1315 function TCustomBufDataset.GetIndexDefs : TIndexDefs;
1316
1317 begin
1318 Result:=FIndexes;
1319 end;
1320
GetCanModifynull1321 function TCustomBufDataset.GetCanModify: Boolean;
1322 begin
1323 Result:=not (UniDirectional or ReadOnly);
1324 end;
1325
BufferOffsetnull1326 function TCustomBufDataset.BufferOffset: integer;
1327 begin
1328 // Returns the offset of data buffer in bufdataset record
1329 Result := sizeof(TBufRecLinkItem) * FMaxIndexesCount;
1330 end;
1331
TCustomBufDataset.IntAllocRecordBuffernull1332 function TCustomBufDataset.IntAllocRecordBuffer: TRecordBuffer;
1333 begin
1334 // Note: Only the internal buffers of TDataset provide bookmark information
1335 result := AllocMem(FRecordSize+BufferOffset);
1336 end;
1337
TCustomBufDataset.AllocRecordBuffernull1338 function TCustomBufDataset.AllocRecordBuffer: TRecordBuffer;
1339 begin
1340 result := AllocMem(FRecordSize + BookmarkSize + CalcFieldsSize);
1341 // The records are initialised, or else the fields of an empty, just-opened dataset
1342 // are not null
1343 InitRecord(result);
1344 end;
1345
1346 procedure TCustomBufDataset.FreeRecordBuffer(var Buffer: TRecordBuffer);
1347 begin
1348 ReAllocMem(Buffer,0);
1349 end;
1350
1351 procedure TCustomBufDataset.ClearCalcFields(Buffer: TRecordBuffer);
1352 begin
1353 if CalcFieldsSize > 0 then
1354 FillByte((Buffer+RecordSize)^,CalcFieldsSize,0);
1355 end;
1356
1357 procedure TCustomBufDataset.InternalInitFieldDefs;
1358 begin
1359 if FileName<>'' then
1360 begin
1361 IntLoadFieldDefsFromFile;
1362 FreeAndNil(FDatasetReader);
1363 FreeAndNil(FFileStream);
1364 end;
1365 end;
1366
1367 procedure TCustomBufDataset.InitUserIndexes;
1368
1369 var
1370 i : integer;
1371
1372 begin
1373 For I:=0 to FIndexes.Count-1 do
1374 if BufIndexDefs[i].IndexType=itNormal then
1375 InternalCreateIndex(BufIndexDefs[i]);
1376 end;
1377
1378 procedure TCustomBufDataset.InternalOpen;
1379
1380 var IndexNr : integer;
1381 i : integer;
1382
1383 begin
1384 if assigned(FDatasetReader) or (FileName<>'') then
1385 IntLoadFieldDefsFromFile;
1386
1387 // This checks if the dataset is actually created (by calling CreateDataset,
1388 // or reading from a stream in some other way implemented by a descendent)
1389 // If there are less fields than FieldDefs we know for sure that the dataset
1390 // is not (correctly) created.
1391
1392 // If there are constant expressions in the select statement (for PostgreSQL)
1393 // they are of type ftUnknown (in FieldDefs), and are not created (in Fields).
1394 // So Fields.Count < FieldDefs.Count in this case
1395 // See mantis #22030
1396
1397 // if Fields.Count<FieldDefs.Count then
1398 if (Fields.Count = 0) or (FieldDefs.Count=0) then
1399 DatabaseError(SErrNoDataset);
1400
1401 // search for autoinc field
1402 FAutoIncField:=nil;
1403 if FAutoIncValue>-1 then
1404 begin
1405 for i := 0 to Fields.Count-1 do
1406 if Fields[i] is TAutoIncField then
1407 begin
1408 FAutoIncField := TAutoIncField(Fields[i]);
1409 Break;
1410 end;
1411 end;
1412
1413 InitDefaultIndexes;
1414 InitUserIndexes;
1415 If FIndexName<>'' then
1416 FCurrentIndexDef:=TBufDatasetIndex(FIndexes.Find(FIndexName))
1417 else if (FIndexFieldNames<>'') then
1418 BuildCustomIndex;
1419
1420 CalcRecordSize;
1421
1422 FBRecordCount := 0;
1423
1424 for IndexNr:=0 to FIndexes.Count-1 do
1425 if Assigned(BufIndexdefs[IndexNr]) then
1426 With BufIndexes[IndexNr] do
1427 InitialiseSpareRecord(IntAllocRecordBuffer);
1428
1429 FAllPacketsFetched := False;
1430
1431 FOpen:=True;
1432
1433 // parse filter expression
1434 ParseFilter(Filter);
1435
1436 if assigned(FDatasetReader) then IntLoadRecordsFromFile;
1437 end;
1438
1439 procedure TCustomBufDataset.DoBeforeClose;
1440 begin
1441 inherited DoBeforeClose;
1442 if (FFileName<>'') then
1443 SaveToFile(FFileName,dfDefault);
1444 end;
1445
1446 procedure TCustomBufDataset.InternalClose;
1447
1448 var
1449 i,r : integer;
1450 iGetResult : TGetResult;
1451 pc : TRecordBuffer;
1452 CurBufIndex: TBufDatasetIndex;
1453
1454 begin
1455 FOpen:=False;
1456 FReadFromFile:=False;
1457 FBRecordCount:=0;
1458 if (FIndexes.Count>0) then
1459 with DefaultBufferIndex do
1460 if IsInitialized then
1461 begin
1462 iGetResult:=ScrollFirst;
1463 while iGetResult = grOK do
1464 begin
1465 pc:=pointer(CurrentRecord);
1466 iGetResult:=ScrollForward;
1467 FreeRecordBuffer(pc);
1468 end;
1469 end;
1470
1471 for r := 0 to FIndexes.Count-1 do
1472 with FIndexes.BufIndexes[r] do
1473 if IsInitialized then
1474 begin
1475 pc:=SpareRecord;
1476 ReleaseSpareRecord;
1477 FreeRecordBuffer(pc);
1478 end;
1479
1480 if Length(FUpdateBuffer) > 0 then
1481 begin
1482 for r := 0 to length(FUpdateBuffer)-1 do with FUpdateBuffer[r] do
1483 begin
1484 if assigned(OldValuesBuffer) then
1485 FreeRecordBuffer(OldValuesBuffer);
1486 if (UpdateKind = ukDelete) and assigned(BookmarkData.BookmarkData) then
1487 FreeRecordBuffer(TRecordBuffer(BookmarkData.BookmarkData));
1488 end;
1489 end;
1490 SetLength(FUpdateBuffer,0);
1491
1492 for r := 0 to High(FBlobBuffers) do
1493 FreeBlobBuffer(FBlobBuffers[r]);
1494 for r := 0 to High(FUpdateBlobBuffers) do
1495 FreeBlobBuffer(FUpdateBlobBuffers[r]);
1496 SetLength(FBlobBuffers,0);
1497 SetLength(FUpdateBlobBuffers,0);
1498 SetLength(FFieldBufPositions,0);
1499 if FAutoIncValue>-1 then FAutoIncValue:=1;
1500 if assigned(FParser) then FreeAndNil(FParser);
1501 For I:=FIndexes.Count-1 downto 0 do
1502 begin
1503 CurBufIndex:=BufIndexDefs[i];
1504 if (CurBufIndex.IndexType in [itDefault,itCustom]) or (CurBufIndex.DiscardOnClose) then
1505 begin
1506 if FCurrentIndexDef=CurBufIndex then
1507 FCurrentIndexDef:=nil;
1508 CurBufIndex.Free;
1509 end
1510 else
1511 FreeAndNil(CurBufIndex.FBufferIndex);
1512 end;
1513 end;
1514
1515 procedure TCustomBufDataset.InternalFirst;
1516
1517 begin
1518 with CurrentIndexBuf do
1519 // if FCurrentRecBuf = FLastRecBuf then the dataset is just opened and empty
1520 // in which case InternalFirst should do nothing (bug 7211)
1521 SetToFirstRecord;
1522 end;
1523
1524 procedure TCustomBufDataset.InternalLast;
1525 begin
1526 FetchAll;
1527 with CurrentIndexBuf do
1528 SetToLastRecord;
1529 end;
1530
1531 procedure TCustomBufDataset.CopyFromDataset(DataSet: TDataSet; CopyData: Boolean);
1532
1533 Const
1534 UseStreams = ftBlobTypes;
1535
1536 Var
1537 I : Integer;
1538 F,F1,F2 : TField;
1539 L1,L2 : TList;
1540 N : String;
1541 OriginalPosition: TBookMark;
1542 S : TMemoryStream;
1543
1544 begin
1545 Close;
1546 Fields.Clear;
1547 FieldDefs.Clear;
1548 For I:=0 to Dataset.FieldCount-1 do
1549 begin
1550 F:=Dataset.Fields[I];
1551 TFieldDef.Create(FieldDefs,F.FieldName,F.DataType,F.Size,F.Required,F.FieldNo);
1552 end;
1553 CreateDataset;
1554 L1:=Nil;
1555 L2:=Nil;
1556 S:=Nil;
1557 If CopyData then
1558 try
1559 L1:=TList.Create;
1560 L2:=TList.Create;
1561 Open;
1562 For I:=0 to FieldDefs.Count-1 do
1563 begin
1564 N:=FieldDefs[I].Name;
1565 F1:=FieldByName(N);
1566 F2:=DataSet.FieldByName(N);
1567 L1.Add(F1);
1568 L2.Add(F2);
1569 If (FieldDefs[I].DataType in UseStreams) and (S=Nil) then
1570 S:=TMemoryStream.Create;
1571 end;
1572 DisableControls;
1573 Dataset.DisableControls;
1574 OriginalPosition:=Dataset.GetBookmark;
1575 Try
1576 Dataset.Open;
1577 Dataset.First;
1578 While not Dataset.EOF do
1579 begin
1580 Append;
1581 For I:=0 to L1.Count-1 do
1582 begin
1583 F1:=TField(L1[i]);
1584 F2:=TField(L2[I]);
1585 If Not F2.IsNull then
1586 Case F1.DataType of
1587 ftFixedChar,
1588 ftString : F1.AsString:=F2.AsString;
1589 ftFixedWideChar,
1590 ftWideString : F1.AsWideString:=F2.AsWideString;
1591 ftBoolean : F1.AsBoolean:=F2.AsBoolean;
1592 ftFloat : F1.AsFloat:=F2.AsFloat;
1593 ftAutoInc,
1594 ftSmallInt,
1595 ftInteger : F1.AsInteger:=F2.AsInteger;
1596 ftLargeInt : F1.AsLargeInt:=F2.AsLargeInt;
1597 ftDate : F1.AsDateTime:=F2.AsDateTime;
1598 ftTime : F1.AsDateTime:=F2.AsDateTime;
1599 ftTimestamp,
1600 ftDateTime : F1.AsDateTime:=F2.AsDateTime;
1601 ftCurrency : F1.AsCurrency:=F2.AsCurrency;
1602 ftBCD,
1603 ftFmtBCD : F1.AsBCD:=F2.AsBCD;
1604 else
1605 if (F1.DataType in UseStreams) then
1606 begin
1607 S.Clear;
1608 TBlobField(F2).SaveToStream(S);
1609 S.Position:=0;
1610 TBlobField(F1).LoadFromStream(S);
1611 end
1612 else
1613 F1.AsString:=F2.AsString;
1614 end;
1615 end;
1616 Try
1617 Post;
1618 except
1619 Cancel;
1620 Raise;
1621 end;
1622 Dataset.Next;
1623 end;
1624 Finally
1625 DataSet.GotoBookmark(OriginalPosition); //Return to original record
1626 Dataset.EnableControls;
1627 EnableControls;
1628 end;
1629 finally
1630 L2.Free;
1631 l1.Free;
1632 S.Free;
1633 end;
1634 end;
1635
1636 { TBufIndex }
1637
1638 constructor TBufIndex.Create(const ADataset: TCustomBufDataset);
1639 begin
1640 inherited create;
1641 FDataset := ADataset;
1642 end;
1643
TBufIndex.BookmarkValidnull1644 function TBufIndex.BookmarkValid(const ABookmark: PBufBookmark): boolean;
1645 begin
1646 Result := assigned(ABookmark) and assigned(ABookmark^.BookmarkData);
1647 end;
1648
TBufIndex.CompareBookmarksnull1649 function TBufIndex.CompareBookmarks(const ABookmark1, ABookmark2: PBufBookmark): integer;
1650 begin
1651 Result := 0;
1652 end;
1653
SameBookmarksnull1654 function TBufIndex.SameBookmarks(const ABookmark1, ABookmark2: PBufBookmark): boolean;
1655 begin
1656 Result := Assigned(ABookmark1) and Assigned(ABookmark2) and (CompareBookmarks(ABookmark1, ABookmark2) = 0);
1657 end;
1658
TBufIndex.GetRecordnull1659 function TBufIndex.GetRecord(ABookmark: PBufBookmark; GetMode: TGetMode): TGetResult;
1660 begin
1661 Result := grError;
1662 end;
1663
1664 { TDoubleLinkedBufIndex }
1665
TDoubleLinkedBufIndex.GetBookmarkSizenull1666 function TDoubleLinkedBufIndex.GetBookmarkSize: integer;
1667 begin
1668 Result:=sizeof(TBufBookmark);
1669 end;
1670
GetCurrentBuffernull1671 function TDoubleLinkedBufIndex.GetCurrentBuffer: Pointer;
1672 begin
1673 Result := pointer(FCurrentRecBuf) + FDataset.BufferOffset;
1674 end;
1675
GetCurrentRecordnull1676 function TDoubleLinkedBufIndex.GetCurrentRecord: TRecordBuffer;
1677 begin
1678 Result := TRecordBuffer(FCurrentRecBuf);
1679 end;
1680
TDoubleLinkedBufIndex.GetIsInitializednull1681 function TDoubleLinkedBufIndex.GetIsInitialized: boolean;
1682 begin
1683 Result := (FFirstRecBuf<>nil);
1684 end;
1685
GetSpareBuffernull1686 function TDoubleLinkedBufIndex.GetSpareBuffer: TRecordBuffer;
1687 begin
1688 Result := pointer(FLastRecBuf) + FDataset.BufferOffset;
1689 end;
1690
TDoubleLinkedBufIndex.GetSpareRecordnull1691 function TDoubleLinkedBufIndex.GetSpareRecord: TRecordBuffer;
1692 begin
1693 Result := TRecordBuffer(FLastRecBuf);
1694 end;
1695
TDoubleLinkedBufIndex.ScrollBackwardnull1696 function TDoubleLinkedBufIndex.ScrollBackward: TGetResult;
1697 begin
1698 if not assigned(FCurrentRecBuf[IndNr].prior) then
1699 begin
1700 Result := grBOF;
1701 end
1702 else
1703 begin
1704 Result := grOK;
1705 FCurrentRecBuf := FCurrentRecBuf[IndNr].prior;
1706 end;
1707 end;
1708
TDoubleLinkedBufIndex.ScrollForwardnull1709 function TDoubleLinkedBufIndex.ScrollForward: TGetResult;
1710 begin
1711 if (FCurrentRecBuf = FLastRecBuf) or // just opened
1712 (FCurrentRecBuf[IndNr].next = FLastRecBuf) then
1713 result := grEOF
1714 else
1715 begin
1716 FCurrentRecBuf := FCurrentRecBuf[IndNr].next;
1717 Result := grOK;
1718 end;
1719 end;
1720
TDoubleLinkedBufIndex.GetCurrentnull1721 function TDoubleLinkedBufIndex.GetCurrent: TGetResult;
1722 begin
1723 if FFirstRecBuf = FLastRecBuf then
1724 Result := grError
1725 else
1726 begin
1727 Result := grOK;
1728 if FCurrentRecBuf = FLastRecBuf then
1729 FCurrentRecBuf:=FLastRecBuf[IndNr].prior;
1730 end;
1731 end;
1732
ScrollFirstnull1733 function TDoubleLinkedBufIndex.ScrollFirst: TGetResult;
1734 begin
1735 FCurrentRecBuf:=FFirstRecBuf;
1736 if (FCurrentRecBuf = FLastRecBuf) then
1737 result := grEOF
1738 else
1739 result := grOK;
1740 end;
1741
1742 procedure TDoubleLinkedBufIndex.ScrollLast;
1743 begin
1744 FCurrentRecBuf:=FLastRecBuf;
1745 end;
1746
TDoubleLinkedBufIndex.GetRecordnull1747 function TDoubleLinkedBufIndex.GetRecord(ABookmark: PBufBookmark; GetMode: TGetMode): TGetResult;
1748 var ARecord : PBufRecLinkItem;
1749 begin
1750 Result := grOK;
1751 case GetMode of
1752 gmPrior:
1753 begin
1754 if assigned(ABookmark^.BookmarkData) then
1755 ARecord := ABookmark^.BookmarkData[IndNr].prior
1756 else
1757 ARecord := nil;
1758 if not assigned(ARecord) then
1759 Result := grBOF;
1760 end;
1761 gmNext:
1762 begin
1763 if assigned(ABookmark^.BookmarkData) then
1764 ARecord := ABookmark^.BookmarkData[IndNr].next
1765 else
1766 ARecord := FFirstRecBuf;
1767 end;
1768 else
1769 Result := grError;
1770 end;
1771
1772 if ARecord = FLastRecBuf then
1773 Result := grEOF;
1774 // store into BookmarkData pointer to prior/next record
1775 ABookmark^.BookmarkData:=ARecord;
1776 end;
1777
1778 procedure TDoubleLinkedBufIndex.SetToFirstRecord;
1779 begin
1780 FLastRecBuf[IndNr].next:=FFirstRecBuf;
1781 FCurrentRecBuf := FLastRecBuf;
1782 end;
1783
1784 procedure TDoubleLinkedBufIndex.SetToLastRecord;
1785 begin
1786 if FLastRecBuf <> FFirstRecBuf then FCurrentRecBuf := FLastRecBuf;
1787 end;
1788
1789 procedure TDoubleLinkedBufIndex.StoreCurrentRecord;
1790 begin
1791 FStoredRecBuf:=FCurrentRecBuf;
1792 end;
1793
1794 procedure TDoubleLinkedBufIndex.RestoreCurrentRecord;
1795 begin
1796 FCurrentRecBuf:=FStoredRecBuf;
1797 end;
1798
1799 procedure TDoubleLinkedBufIndex.DoScrollForward;
1800 begin
1801 FCurrentRecBuf := FCurrentRecBuf[IndNr].next;
1802 end;
1803
1804 procedure TDoubleLinkedBufIndex.StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark);
1805 begin
1806 ABookmark^.BookmarkData:=FCurrentRecBuf;
1807 end;
1808
1809 procedure TDoubleLinkedBufIndex.StoreSpareRecIntoBookmark(
1810 const ABookmark: PBufBookmark);
1811 begin
1812 ABookmark^.BookmarkData:=FLastRecBuf;
1813 end;
1814
1815 procedure TDoubleLinkedBufIndex.GotoBookmark(const ABookmark : PBufBookmark);
1816 begin
1817 FCurrentRecBuf := ABookmark^.BookmarkData;
1818 end;
1819
TDoubleLinkedBufIndex.CompareBookmarksnull1820 function TDoubleLinkedBufIndex.CompareBookmarks(const ABookmark1,ABookmark2: PBufBookmark): integer;
1821 var ARecord1, ARecord2 : PBufRecLinkItem;
1822 begin
1823 // valid bookmarks expected
1824 // estimate result using memory addresses of records
1825 Result := ABookmark1^.BookmarkData - ABookmark2^.BookmarkData;
1826 if Result = 0 then
1827 Exit
1828 else if Result < 0 then
1829 begin
1830 Result := -1;
1831 ARecord1 := ABookmark1^.BookmarkData;
1832 ARecord2 := ABookmark2^.BookmarkData;
1833 end
1834 else
1835 begin
1836 Result := +1;
1837 ARecord1 := ABookmark2^.BookmarkData;
1838 ARecord2 := ABookmark1^.BookmarkData;
1839 end;
1840 // if we need relative position of records with given bookmarks we must
1841 // traverse through index until we reach lower bookmark or 1st record
1842 while assigned(ARecord2) and (ARecord2 <> ARecord1) and (ARecord2 <> FFirstRecBuf) do
1843 ARecord2 := ARecord2[IndNr].prior;
1844 // if we found lower bookmark as first, then estimated position is correct
1845 if ARecord1 <> ARecord2 then
1846 Result := -Result;
1847 end;
1848
SameBookmarksnull1849 function TDoubleLinkedBufIndex.SameBookmarks(const ABookmark1, ABookmark2: PBufBookmark): boolean;
1850 begin
1851 Result := Assigned(ABookmark1) and Assigned(ABookmark2) and (ABookmark1^.BookmarkData = ABookmark2^.BookmarkData);
1852 end;
1853
1854 procedure TDoubleLinkedBufIndex.InitialiseIndex;
1855 begin
1856 // Do nothing
1857 end;
1858
CanScrollForwardnull1859 function TDoubleLinkedBufIndex.CanScrollForward: Boolean;
1860 begin
1861 if (FCurrentRecBuf[IndNr].next = FLastRecBuf) then
1862 Result := False
1863 else
1864 Result := True;
1865 end;
1866
1867 procedure TDoubleLinkedBufIndex.InitialiseSpareRecord(const ASpareRecord : TRecordBuffer);
1868 begin
1869 FFirstRecBuf := pointer(ASpareRecord);
1870 FLastRecBuf := FFirstRecBuf;
1871 FLastRecBuf[IndNr].prior:=nil;
1872 FLastRecBuf[IndNr].next:=FLastRecBuf;
1873 FCurrentRecBuf := FLastRecBuf;
1874 end;
1875
1876 procedure TDoubleLinkedBufIndex.ReleaseSpareRecord;
1877 begin
1878 FFirstRecBuf:= nil;
1879 end;
1880
TDoubleLinkedBufIndex.GetRecNonull1881 function TDoubleLinkedBufIndex.GetRecNo: Longint;
1882 var ARecord : PBufRecLinkItem;
1883 begin
1884 ARecord := FCurrentRecBuf;
1885 Result := 1;
1886 while ARecord <> FFirstRecBuf do
1887 begin
1888 inc(Result);
1889 ARecord := ARecord[IndNr].prior;
1890 end;
1891 end;
1892
1893 procedure TDoubleLinkedBufIndex.SetRecNo(ARecNo: Longint);
1894 var ARecord : PBufRecLinkItem;
1895 begin
1896 ARecord := FFirstRecBuf;
1897 while (ARecNo > 1) and (ARecord <> FLastRecBuf) do
1898 begin
1899 dec(ARecNo);
1900 ARecord := ARecord[IndNr].next;
1901 end;
1902 FCurrentRecBuf := ARecord;
1903 end;
1904
1905 procedure TDoubleLinkedBufIndex.BeginUpdate;
1906 begin
1907 if FCurrentRecBuf = FLastRecBuf then
1908 FCursOnFirstRec := True
1909 else
1910 FCursOnFirstRec := False;
1911 end;
1912
1913 procedure TDoubleLinkedBufIndex.AddRecord;
1914 var ARecord: TRecordBuffer;
1915 begin
1916 ARecord := FDataset.IntAllocRecordBuffer;
1917 FLastRecBuf[IndNr].next := pointer(ARecord);
1918 FLastRecBuf[IndNr].next[IndNr].prior := FLastRecBuf;
1919
1920 FLastRecBuf := FLastRecBuf[IndNr].next;
1921 end;
1922
1923 procedure TDoubleLinkedBufIndex.InsertRecordBeforeCurrentRecord(const ARecord: TRecordBuffer);
1924 var ANewRecord : PBufRecLinkItem;
1925 begin
1926 ANewRecord:=PBufRecLinkItem(ARecord);
1927 ANewRecord[IndNr].prior:=FCurrentRecBuf[IndNr].prior;
1928 ANewRecord[IndNr].Next:=FCurrentRecBuf;
1929
1930 if FCurrentRecBuf=FFirstRecBuf then
1931 begin
1932 FFirstRecBuf:=ANewRecord;
1933 ANewRecord[IndNr].prior:=nil;
1934 end
1935 else
1936 ANewRecord[IndNr].Prior[IndNr].next:=ANewRecord;
1937 ANewRecord[IndNr].next[IndNr].prior:=ANewRecord;
1938 end;
1939
1940 procedure TDoubleLinkedBufIndex.RemoveRecordFromIndex(const ABookmark : TBufBookmark);
1941 var ARecord : PBufRecLinkItem;
1942 begin
1943 ARecord := ABookmark.BookmarkData;
1944 if ARecord = FCurrentRecBuf then DoScrollForward;
1945 if ARecord <> FFirstRecBuf then
1946 ARecord[IndNr].prior[IndNr].next := ARecord[IndNr].next
1947 else
1948 begin
1949 FFirstRecBuf := ARecord[IndNr].next;
1950 FLastRecBuf[IndNr].next := FFirstRecBuf;
1951 end;
1952 ARecord[IndNr].next[IndNr].prior := ARecord[IndNr].prior;
1953 end;
1954
1955 procedure TDoubleLinkedBufIndex.OrderCurrentRecord;
1956 var ARecord: PBufRecLinkItem;
1957 ABookmark: TBufBookmark;
1958 begin
1959 // all records except current are already sorted
1960 // check prior records
1961 ARecord := FCurrentRecBuf;
1962 repeat
1963 ARecord := ARecord[IndNr].prior;
1964 until not assigned(ARecord) or (IndexCompareRecords(ARecord, FCurrentRecBuf, DBCompareStruct) <= 0);
1965 if assigned(ARecord) then
1966 ARecord := ARecord[IndNr].next
1967 else
1968 ARecord := FFirstRecBuf;
1969 if ARecord = FCurrentRecBuf then
1970 begin
1971 // prior record is less equal than current
1972 // check next records
1973 repeat
1974 ARecord := ARecord[IndNr].next;
1975 until (ARecord=FLastRecBuf) or (IndexCompareRecords(ARecord, FCurrentRecBuf, DBCompareStruct) >= 0);
1976 if ARecord = FCurrentRecBuf[IndNr].next then
1977 Exit; // current record is on proper position
1978 end;
1979 StoreCurrentRecIntoBookmark(@ABookmark);
1980 RemoveRecordFromIndex(ABookmark);
1981 FCurrentRecBuf := ARecord;
1982 InsertRecordBeforeCurrentRecord(TRecordBuffer(ABookmark.BookmarkData));
1983 GotoBookmark(@ABookmark);
1984 end;
1985
1986 procedure TDoubleLinkedBufIndex.EndUpdate;
1987 begin
1988 FLastRecBuf[IndNr].next := FFirstRecBuf;
1989 if FCursOnFirstRec then FCurrentRecBuf:=FLastRecBuf;
1990 end;
1991
1992 procedure TCustomBufDataset.CurrentRecordToBuffer(Buffer: TRecordBuffer);
1993 var ABookMark : PBufBookmark;
1994 begin
1995 with CurrentIndexBuf do
1996 begin
1997 move(CurrentBuffer^,buffer^,FRecordSize);
1998 ABookMark:=PBufBookmark(Buffer + FRecordSize);
1999 ABookmark^.BookmarkFlag:=bfCurrent;
2000 StoreCurrentRecIntoBookmark(ABookMark);
2001 end;
2002
2003 GetCalcFields(Buffer);
2004 end;
2005
2006 procedure TCustomBufDataset.SetBufUniDirectional(const AValue: boolean);
2007 begin
2008 CheckInactive;
2009 if (AValue<>IsUniDirectional) then
2010 begin
2011 SetUniDirectional(AValue);
2012 ClearIndexes;
2013 FPacketRecords := 1; // temporary
2014 end;
2015 end;
2016
DefaultIndexnull2017 function TCustomBufDataset.DefaultIndex: TBufDatasetIndex;
2018 begin
2019 Result:=FDefaultIndex;
2020 if Result=Nil then
2021 Result:=FIndexes.FindIndex(SDefaultIndex);
2022 end;
2023
DefaultBufferIndexnull2024 function TCustomBufDataset.DefaultBufferIndex: TBufIndex;
2025 begin
2026 if Assigned(DefaultIndex) then
2027 Result:=DefaultIndex.BufferIndex
2028 else
2029 Result:=Nil;
2030 end;
2031
2032 procedure TCustomBufDataset.SetReadOnly(AValue: Boolean);
2033 begin
2034 FReadOnly:=AValue;
2035 end;
2036
GetRecordnull2037 function TCustomBufDataset.GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
2038
2039 var Acceptable : Boolean;
2040 SavedState : TDataSetState;
2041
2042 begin
2043 Result := grOK;
2044 with CurrentIndexBuf do
2045 repeat
2046 Acceptable := True;
2047 case GetMode of
2048 gmPrior : Result := ScrollBackward;
2049 gmCurrent : Result := GetCurrent;
2050 gmNext : begin
2051 if not CanScrollForward and (getnextpacket = 0) then
2052 Result := grEOF
2053 else
2054 begin
2055 Result := grOK;
2056 DoScrollForward;
2057 end;
2058 end;
2059 end;
2060
2061 if Result = grOK then
2062 begin
2063 CurrentRecordToBuffer(Buffer);
2064
2065 if Filtered then
2066 begin
2067 FFilterBuffer := Buffer;
2068 SavedState := SetTempState(dsFilter);
2069 DoFilterRecord(Acceptable);
2070 if (GetMode = gmCurrent) and not Acceptable then
2071 begin
2072 Acceptable := True;
2073 Result := grError;
2074 end;
2075 RestoreState(SavedState);
2076 end;
2077 end
2078 else if (Result = grError) and DoCheck then
2079 DatabaseError('No record');
2080 until Acceptable;
2081 end;
2082
TCustomBufDataset.GetActiveRecordUpdateBuffernull2083 function TCustomBufDataset.GetActiveRecordUpdateBuffer : boolean;
2084
2085 var ABookmark : TBufBookmark;
2086
2087 begin
2088 GetBookmarkData(ActiveBuffer,@ABookmark);
2089 result := GetRecordUpdateBufferCached(ABookmark);
2090 end;
2091
TCustomBufDataset.GetCurrentIndexBufnull2092 function TCustomBufDataset.GetCurrentIndexBuf: TBufIndex;
2093 begin
2094 if Assigned(FCurrentIndexDef) then
2095 Result:=FCurrentIndexDef.BufferIndex
2096 else
2097 Result:=Nil;
2098 end;
2099
GetBufIndexnull2100 function TCustomBufDataset.GetBufIndex(Aindex : Integer): TBufIndex;
2101 begin
2102 Result:=FIndexes.BufIndexes[AIndex]
2103 end;
2104
TCustomBufDataset.GetBufIndexDefnull2105 function TCustomBufDataset.GetBufIndexDef(Aindex : Integer): TBufDatasetIndex;
2106 begin
2107 Result:=FIndexes.BufIndexdefs[AIndex]
2108 end;
2109
2110 procedure TCustomBufDataset.ProcessFieldsToCompareStruct(const AFields, ADescFields, ACInsFields: TList;
2111 const AIndexOptions: TIndexOptions; const ALocateOptions: TLocateOptions; out ACompareStruct: TDBCompareStruct);
2112 var i: integer;
2113 AField: TField;
2114 ACompareRec: TDBCompareRec;
2115 begin
2116 SetLength(ACompareStruct, AFields.Count);
2117 for i:=0 to high(ACompareStruct) do
2118 begin
2119 AField := TField(AFields[i]);
2120
2121 case AField.DataType of
2122 ftString, ftFixedChar, ftGuid:
2123 ACompareRec.CompareFunc := @DBCompareText;
2124 ftWideString, ftFixedWideChar:
2125 ACompareRec.CompareFunc := @DBCompareWideText;
2126 ftSmallint:
2127 ACompareRec.CompareFunc := @DBCompareSmallInt;
2128 ftInteger, ftAutoInc:
2129 ACompareRec.CompareFunc := @DBCompareInt;
2130 ftLargeint, ftBCD:
2131 ACompareRec.CompareFunc := @DBCompareLargeInt;
2132 ftWord:
2133 ACompareRec.CompareFunc := @DBCompareWord;
2134 ftBoolean:
2135 ACompareRec.CompareFunc := @DBCompareByte;
2136 ftDate, ftTime, ftDateTime,
2137 ftFloat, ftCurrency:
2138 ACompareRec.CompareFunc := @DBCompareDouble;
2139 ftFmtBCD:
2140 ACompareRec.CompareFunc := @DBCompareBCD;
2141 ftVarBytes:
2142 ACompareRec.CompareFunc := @DBCompareVarBytes;
2143 ftBytes:
2144 ACompareRec.CompareFunc := @DBCompareBytes;
2145 else
2146 DatabaseErrorFmt(SErrIndexBasedOnInvField, [AField.FieldName,Fieldtypenames[AField.DataType]]);
2147 end;
2148
2149 ACompareRec.Off:=BufferOffset + FFieldBufPositions[AField.FieldNo-1];
2150 ACompareRec.NullBOff:=BufferOffset;
2151
2152 ACompareRec.FieldInd:=AField.FieldNo-1;
2153 ACompareRec.Size:=GetFieldSize(FieldDefs[ACompareRec.FieldInd]);
2154
2155 ACompareRec.Desc := ixDescending in AIndexOptions;
2156 if assigned(ADescFields) then
2157 ACompareRec.Desc := ACompareRec.Desc or (ADescFields.IndexOf(AField)>-1);
2158
2159 ACompareRec.Options := ALocateOptions;
2160 if assigned(ACInsFields) and (ACInsFields.IndexOf(AField)>-1) then
2161 ACompareRec.Options := ACompareRec.Options + [loCaseInsensitive];
2162
2163 ACompareStruct[i] := ACompareRec;
2164 end;
2165 end;
2166
2167
2168 procedure TCustomBufDataset.InitDefaultIndexes;
2169
2170 {
2171 This procedure makes sure there are 2 default indexes:
2172 DEFAULT_ORDER, which is simply the order in which the server records arrived.
2173 CUSTOM_ORDER, which is an internal index to accomodate the 'IndexFieldNames' property.
2174 }
2175
2176 Var
2177 FD,FC : TBufDatasetIndex;
2178
2179 begin
2180 // Default index
2181 FD:=FIndexes.FindIndex(SDefaultIndex);
2182 if (FD=Nil) then
2183 begin
2184 FD:=InternalAddIndex(SDefaultIndex,'',[],'','');
2185 FD.IndexType:=itDefault;
2186 FD.FDiscardOnClose:=True;
2187 end
2188 // Not sure about this. For the moment we leave it in comment
2189 { else if FD.BufferIndex=Nil then
2190 InternalCreateIndex(FD)}
2191 ;
2192
2193 FCurrentIndexDef:=FD;
2194 // Custom index
2195 if not IsUniDirectional then
2196 begin
2197 FC:=Findexes.FindIndex(SCustomIndex);
2198 if (FC=Nil) then
2199 begin
2200 FC:=InternalAddIndex(SCustomIndex,'',[],'','');
2201 FC.IndexType:=itCustom;
2202 FC.FDiscardOnClose:=True;
2203 end
2204 // Not sure about this. For the moment we leave it in comment
2205 { else if FD.BufferIndex=Nil then
2206 InternalCreateIndex(FD)}
2207 ;
2208 end;
2209 BookmarkSize:=CurrentIndexBuf.BookmarkSize;
2210 end;
2211
2212 procedure TCustomBufDataset.AddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string = '';
2213 const ACaseInsFields: string = '');
2214
2215 Var
2216 F : TBufDatasetIndex;
2217
2218 begin
2219 CheckBiDirectional;
2220 if (AFields='') then
2221 DatabaseError(SNoIndexFieldNameGiven,Self);
2222 if Active and (FIndexes.Count=FMaxIndexesCount) then
2223 DatabaseError(SMaxIndexes,Self);
2224 // If not all packets are fetched, you can not sort properly.
2225 if not Active then
2226 FPacketRecords:=-1;
2227 F:=InternalAddIndex(AName,AFields,AOptions,ADescFields,ACaseInsFields);
2228 F.FDiscardOnClose:=Active;
2229 end;
2230
TCustomBufDataset.InternalAddIndexnull2231 Function TCustomBufDataset.InternalAddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string;
2232 const ACaseInsFields: string) : TBufDatasetIndex;
2233
2234 Var
2235 F : TBufDatasetIndex;
2236
2237 begin
2238 F:=FIndexes.AddIndexDef as TBufDatasetIndex;
2239 F.Name:=AName;
2240 F.Fields:=AFields;
2241 F.Options:=AOptions;
2242 F.DescFields:=ADescFields;
2243 F.CaseInsFields:=ACaseInsFields;
2244 InternalCreateIndex(F);
2245 Result:=F;
2246 end;
2247
2248 procedure TCustomBufDataset.InternalCreateIndex(F : TBufDataSetIndex);
2249
2250 Var
2251 B : TBufIndex;
2252 begin
2253 if Active and not Refreshing then
2254 FetchAll;
2255 if IsUniDirectional then
2256 B:=TUniDirectionalBufIndex.Create(self)
2257 else
2258 B:=TDoubleLinkedBufIndex.Create(self);
2259 F.FBufferIndex:=B;
2260 with B do
2261 begin
2262 InitialiseIndex;
2263 F.SetIndexProperties;
2264 end;
2265 if Active then
2266 begin
2267 if not Refreshing then
2268 B.InitialiseSpareRecord(IntAllocRecordBuffer);
2269 if (F.Fields<>'') then
2270 BuildIndex(B);
2271 end
2272 else
2273 if (FIndexes.Count+2>FMaxIndexesCount) then
2274 FMaxIndexesCount:=FIndexes.Count+2; // Custom+Default order
2275 end;
2276
TCustomBufDataset.DefaultReadFileFormatnull2277 class function TCustomBufDataset.DefaultReadFileFormat: TDataPacketFormat;
2278 begin
2279 Result:=dfAny;
2280 end;
2281
TCustomBufDataset.DefaultWriteFileFormatnull2282 class function TCustomBufDataset.DefaultWriteFileFormat: TDataPacketFormat;
2283 begin
2284 Result:=dfBinary;
2285 end;
2286
TCustomBufDataset.DefaultPacketClassnull2287 class function TCustomBufDataset.DefaultPacketClass: TDataPacketReaderClass;
2288 begin
2289 Result:=TFpcBinaryDatapacketReader;
2290 end;
2291
CreateDefaultPacketReadernull2292 function TCustomBufDataset.CreateDefaultPacketReader(aStream : TStream): TDataPacketReader;
2293 begin
2294 Result:=DefaultPacketClass.Create(Self,aStream);
2295 end;
2296
2297
2298 procedure TCustomBufDataset.SetIndexFieldNames(const AValue: String);
2299
2300 begin
2301 FIndexFieldNames:=AValue;
2302 if (AValue='') then
2303 begin
2304 FCurrentIndexDef:=FIndexes.FindIndex(SDefaultIndex);
2305 Exit;
2306 end;
2307 if Active then
2308 BuildCustomIndex;
2309 end;
2310
2311 procedure TCustomBufDataset.BuildCustomIndex;
2312
2313 var
2314 i, p: integer;
2315 s: string;
2316 SortFields, DescFields: string;
2317 F : TBufDatasetIndex;
2318
2319 begin
2320 F:=FIndexes.FindIndex(SCustomIndex);
2321 if (F=Nil) then
2322 InitDefaultIndexes;
2323 F:=FIndexes.FindIndex(SCustomIndex);
2324 SortFields := '';
2325 DescFields := '';
2326 for i := 1 to WordCount(FIndexFieldNames, [Limiter]) do
2327 begin
2328 s := ExtractDelimited(i, FIndexFieldNames, [Limiter]);
2329 p := Pos(Desc, s);
2330 if p>0 then
2331 begin
2332 system.Delete(s, p, LenDesc);
2333 DescFields := DescFields + Limiter + s;
2334 end;
2335 SortFields := SortFields + Limiter + s;
2336 end;
2337 if (Length(SortFields)>0) and (SortFields[1]=Limiter) then
2338 system.Delete(SortFields,1,1);
2339 if (Length(DescFields)>0) and (DescFields[1]=Limiter) then
2340 system.Delete(DescFields,1,1);
2341 F.Fields:=SortFields;
2342 F.Options:=[];
2343 F.DescFields:=DescFields;
2344 FCurrentIndexDef:=F;
2345 F.SetIndexProperties;
2346 if Active then
2347 begin
2348 FetchAll;
2349 BuildIndex(F.BufferIndex);
2350 Resync([rmCenter]);
2351 end;
2352 FPacketRecords:=-1;
2353 end;
2354
2355 procedure TCustomBufDataset.SetIndexName(AValue: String);
2356
2357 var
2358 F : TBufDatasetIndex;
2359 B : TDoubleLinkedBufIndex;
2360 N : String;
2361
2362 begin
2363 N:=AValue;
2364 If (N='') then
2365 N:=SDefaultIndex;
2366 F:=FIndexes.FindIndex(N);
2367 if (F=Nil) and (AValue<>'') and not (csLoading in ComponentState) then
2368 DatabaseErrorFmt(SIndexNotFound,[AValue],Self);
2369 FIndexName:=AValue;
2370 if Assigned(F) then
2371 begin
2372 B:=F.BufferIndex as TDoubleLinkedBufIndex;
2373 if Assigned(CurrentIndexBuf) then
2374 B.FCurrentRecBuf:=(CurrentIndexBuf as TDoubleLinkedBufIndex).FCurrentRecBuf;
2375 FCurrentIndexDef:=F;
2376 if Active then
2377 Resync([rmCenter]);
2378 end
2379 else
2380 FCurrentIndexDef:=Nil;
2381 end;
2382
2383 procedure TCustomBufDataset.SetMaxIndexesCount(const AValue: Integer);
2384 begin
2385 CheckInactive;
2386 if AValue > 1 then
2387 FMaxIndexesCount:=AValue
2388 else
2389 DatabaseError(SMinIndexes,Self);
2390 end;
2391
2392 procedure TCustomBufDataset.InternalSetToRecord(Buffer: TRecordBuffer);
2393 begin
2394 CurrentIndexBuf.GotoBookmark(PBufBookmark(Buffer+FRecordSize));
2395 end;
2396
2397 procedure TCustomBufDataset.SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
2398 begin
2399 PBufBookmark(Buffer + FRecordSize)^ := PBufBookmark(Data)^;
2400 end;
2401
2402 procedure TCustomBufDataset.SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag);
2403 begin
2404 PBufBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
2405 end;
2406
2407 procedure TCustomBufDataset.GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
2408 begin
2409 PBufBookmark(Data)^ := PBufBookmark(Buffer + FRecordSize)^;
2410 end;
2411
GetBookmarkFlagnull2412 function TCustomBufDataset.GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag;
2413 begin
2414 Result := PBufBookmark(Buffer + FRecordSize)^.BookmarkFlag;
2415 end;
2416
2417 procedure TCustomBufDataset.InternalGotoBookmark(ABookmark: Pointer);
2418 begin
2419 // note that ABookMark should be a PBufBookmark. But this way it can also be
2420 // a pointer to a TBufRecLinkItem
2421 CurrentIndexBuf.GotoBookmark(ABookmark);
2422 end;
2423
getnextpacketnull2424 function TCustomBufDataset.getnextpacket : integer;
2425
2426 var i : integer;
2427 pb : TRecordBuffer;
2428 T : TBufIndex;
2429
2430 begin
2431 if FAllPacketsFetched then
2432 begin
2433 result := 0;
2434 exit;
2435 end;
2436 T:=CurrentIndexBuf;
2437 T.BeginUpdate;
2438
2439 i := 0;
2440 pb := DefaultBufferIndex.SpareBuffer;
2441 while ((i < FPacketRecords) or (FPacketRecords = -1)) and (LoadBuffer(pb) = grOk) do
2442 begin
2443 with DefaultBufferIndex do
2444 begin
2445 AddRecord;
2446 pb := SpareBuffer;
2447 end;
2448 inc(i);
2449 end;
2450
2451 T.EndUpdate;
2452 FBRecordCount := FBRecordCount + i;
2453 result := i;
2454 end;
2455
TCustomBufDataset.GetFieldSizenull2456 function TCustomBufDataset.GetFieldSize(FieldDef : TFieldDef) : longint;
2457
2458 begin
2459 case FieldDef.DataType of
2460 ftUnknown : result := 0;
2461 ftString,
2462 ftGuid,
2463 ftFixedChar: result := FieldDef.Size*FieldDef.CharSize + 1;
2464 ftFixedWideChar,
2465 ftWideString:result := (FieldDef.Size + 1)*FieldDef.CharSize;
2466 ftSmallint,
2467 ftInteger,
2468 ftAutoInc,
2469 ftword : result := sizeof(longint);
2470 ftBoolean : result := sizeof(wordbool);
2471 ftBCD : result := sizeof(currency);
2472 ftFmtBCD : result := sizeof(TBCD);
2473 ftFloat,
2474 ftCurrency : result := sizeof(double);
2475 ftLargeInt : result := sizeof(largeint);
2476 ftTime,
2477 ftDate,
2478 ftDateTime : result := sizeof(TDateTime);
2479 ftBytes : result := FieldDef.Size;
2480 ftVarBytes : result := FieldDef.Size + 2;
2481 ftVariant : result := sizeof(variant);
2482 ftBlob,
2483 ftMemo,
2484 ftGraphic,
2485 ftFmtMemo,
2486 ftParadoxOle,
2487 ftDBaseOle,
2488 ftTypedBinary,
2489 ftOraBlob,
2490 ftOraClob,
2491 ftWideMemo : result := sizeof(TBufBlobField)
2492 else
2493 DatabaseErrorFmt(SUnsupportedFieldType,[Fieldtypenames[FieldDef.DataType]]);
2494 end;
2495 {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
2496 result:=Align(result,4);
2497 {$ENDIF}
2498 end;
2499
GetRecordUpdateBuffernull2500 function TCustomBufDataset.GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludePrior : boolean = false; AFindNext : boolean = false): boolean;
2501
2502 var x : integer;
2503 StartBuf : integer;
2504
2505 begin
2506 if AFindNext then
2507 StartBuf := FCurrentUpdateBuffer + 1
2508 else
2509 StartBuf := 0;
2510 Result := False;
2511 for x := StartBuf to high(FUpdateBuffer) do
2512 if CurrentIndexBuf.SameBookmarks(@FUpdateBuffer[x].BookmarkData,@ABookmark) or
2513 (IncludePrior and (FUpdateBuffer[x].UpdateKind=ukDelete) and CurrentIndexBuf.SameBookmarks(@FUpdateBuffer[x].NextBookmarkData,@ABookmark)) then
2514 begin
2515 FCurrentUpdateBuffer := x;
2516 Result := True;
2517 break;
2518 end;
2519 end;
2520
GetRecordUpdateBufferCachednull2521 function TCustomBufDataset.GetRecordUpdateBufferCached(const ABookmark: TBufBookmark;
2522 IncludePrior: boolean): boolean;
2523 begin
2524 // if the current update buffer matches, immediately return true
2525 if (FCurrentUpdateBuffer < length(FUpdateBuffer)) and (
2526 CurrentIndexBuf.SameBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData,@ABookmark) or
2527 (IncludePrior
2528 and (FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind=ukDelete)
2529 and CurrentIndexBuf.SameBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData,@ABookmark))) then
2530 begin
2531 Result := True;
2532 end
2533 else
2534 Result := GetRecordUpdateBuffer(ABookmark,IncludePrior);
2535 end;
2536
TCustomBufDataset.LoadBuffernull2537 function TCustomBufDataset.LoadBuffer(Buffer : TRecordBuffer): TGetResult;
2538
2539 var NullMask : pbyte;
2540 x : longint;
2541 CreateBlobField : boolean;
2542 BufBlob : PBufBlobField;
2543
2544 begin
2545 if not Fetch then
2546 begin
2547 Result := grEOF;
2548 FAllPacketsFetched := True;
2549 // This code has to be placed elsewhere. At least it should also run when
2550 // the datapacket is loaded from file ... see IntLoadRecordsFromFile
2551 BuildIndexes;
2552 Exit;
2553 end;
2554
2555 NullMask := pointer(buffer);
2556 fillchar(Nullmask^,FNullmaskSize,0);
2557 inc(buffer,FNullmaskSize);
2558
2559 for x := 0 to FieldDefs.Count-1 do
2560 begin
2561 if not LoadField(FieldDefs[x],buffer,CreateBlobField) then
2562 SetFieldIsNull(NullMask,x)
2563 else if CreateBlobField then
2564 begin
2565 BufBlob := PBufBlobField(Buffer);
2566 BufBlob^.BlobBuffer := GetNewBlobBuffer;
2567 LoadBlobIntoBuffer(FieldDefs[x],BufBlob);
2568 end;
2569 inc(buffer,GetFieldSize(FieldDefs[x]));
2570 end;
2571 Result := grOK;
2572 end;
2573
GetCurrentBuffernull2574 function TCustomBufDataset.GetCurrentBuffer: TRecordBuffer;
2575 begin
2576 case State of
2577 dsFilter: Result := FFilterBuffer;
2578 dsCalcFields: Result := CalcBuffer;
2579 dsRefreshFields: Result := CurrentIndexBuf.CurrentBuffer
2580 else Result := ActiveBuffer;
2581 end;
2582 end;
2583
2584
TCustomBufDataset.GetFieldDatanull2585 function TCustomBufDataset.GetFieldData(Field: TField; Buffer: Pointer;
2586 NativeFormat: Boolean): Boolean;
2587 begin
2588 Result := GetFieldData(Field, Buffer);
2589 end;
2590
TCustomBufDataset.GetFieldDatanull2591 function TCustomBufDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
2592
2593 var
2594 CurrBuff : TRecordBuffer;
2595
2596 begin
2597 Result := False;
2598 if State = dsOldValue then
2599 begin
2600 if FSavedState = dsInsert then
2601 CurrBuff := nil // old values = null
2602 else if GetActiveRecordUpdateBuffer then
2603 CurrBuff := FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer
2604 else
2605 // There is no UpdateBuffer for ActiveRecord, so there are no explicit old values available
2606 // then we can assume, that old values = current values
2607 CurrBuff := CurrentIndexBuf.CurrentBuffer;
2608 end
2609 else
2610 CurrBuff := GetCurrentBuffer;
2611
2612 if not assigned(CurrBuff) then Exit; //Null value
2613
2614 If Field.FieldNo > 0 then // If =-1, then calculated/lookup field or =0 unbound field
2615 begin
2616 if GetFieldIsNull(pbyte(CurrBuff),Field.FieldNo-1) then
2617 Exit;
2618 if assigned(Buffer) then
2619 begin
2620 inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]);
2621 if Field.IsBlob then // we need GetFieldSize for BLOB but Field.DataSize for others - #36747
2622 Move(CurrBuff^, Buffer^, GetFieldSize(FieldDefs[Field.FieldNo-1]))
2623 else
2624 Move(CurrBuff^, Buffer^, Field.DataSize);
2625 end;
2626 Result := True;
2627 end
2628 else
2629 begin
2630 Inc(CurrBuff, GetRecordSize + Field.Offset);
2631 Result := Boolean(CurrBuff^);
2632 if Result and assigned(Buffer) then
2633 begin
2634 inc(CurrBuff);
2635 Move(CurrBuff^, Buffer^, Field.DataSize);
2636 end;
2637 end;
2638 end;
2639
2640 procedure TCustomBufDataset.SetFieldData(Field: TField; Buffer: Pointer;
2641 NativeFormat: Boolean);
2642 begin
2643 SetFieldData(Field,Buffer);
2644 end;
2645
2646 procedure TCustomBufDataset.SetFieldData(Field: TField; Buffer: Pointer);
2647
2648 var CurrBuff : pointer;
2649 NullMask : pbyte;
2650
2651 begin
2652 if not (State in dsWriteModes) then
2653 DatabaseErrorFmt(SNotEditing, [Name], Self);
2654 CurrBuff := GetCurrentBuffer;
2655 If Field.FieldNo > 0 then // If =-1, then calculated/lookup field or =0 unbound field
2656 begin
2657 if Field.ReadOnly and not (State in [dsSetKey, dsFilter, dsRefreshFields]) then
2658 DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName]);
2659 if State in [dsEdit, dsInsert, dsNewValue] then
2660 Field.Validate(Buffer);
2661 NullMask := CurrBuff;
2662
2663 inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]);
2664 if assigned(buffer) then
2665 begin
2666 if Field.IsBlob then // we need GetFieldSize for BLOB but Field.DataSize for others - #36747
2667 Move(Buffer^, CurrBuff^, GetFieldSize(FieldDefs[Field.FieldNo-1]))
2668 else
2669 Move(Buffer^, CurrBuff^, Field.DataSize);
2670 unSetFieldIsNull(NullMask,Field.FieldNo-1);
2671 end
2672 else
2673 SetFieldIsNull(NullMask,Field.FieldNo-1);
2674 end
2675 else
2676 begin
2677 Inc(CurrBuff, GetRecordSize + Field.Offset);
2678 Boolean(CurrBuff^) := Buffer <> nil;
2679 inc(CurrBuff);
2680 if assigned(Buffer) then
2681 Move(Buffer^, CurrBuff^, Field.DataSize);
2682 end;
2683 if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
2684 DataEvent(deFieldChange, PtrInt(Field));
2685 end;
2686
2687 procedure TCustomBufDataset.InternalDelete;
2688 var RemRec : pointer;
2689 RemRecBookmrk : TBufBookmark;
2690 begin
2691 InternalSetToRecord(ActiveBuffer);
2692 // Remove the record from all active indexes
2693 CurrentIndexBuf.StoreCurrentRecIntoBookmark(@RemRecBookmrk);
2694 RemRec := CurrentIndexBuf.CurrentBuffer;
2695 RemoveRecordFromIndexes(RemRecBookmrk);
2696
2697 if not GetActiveRecordUpdateBuffer then
2698 begin
2699 FCurrentUpdateBuffer := length(FUpdateBuffer);
2700 SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
2701 FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := IntAllocRecordBuffer;
2702 move(RemRec^, FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
2703 end
2704 else
2705 begin
2706 if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind <> ukModify then
2707 begin
2708 FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := nil; //this 'disables' the updatebuffer
2709 // Do NOT release record buffer (pointed to by RemRecBookmrk.BookmarkData) here
2710 // - When record is inserted and deleted (and memory released) and again inserted then the same memory block can be returned
2711 // which leads to confusion, because we get the same BookmarkData for distinct records
2712 // - In CancelUpdates when records are restored, it is expected that deleted records still exist in memory
2713 // There also could be record(s) in the update buffer that is linked to this record.
2714 end;
2715 end;
2716 CurrentIndexBuf.StoreCurrentRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData);
2717 FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := RemRecBookmrk;
2718 FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukDelete;
2719 dec(FBRecordCount);
2720 end;
2721
2722
2723 procedure TCustomBufDataset.ApplyRecUpdate(UpdateKind : TUpdateKind);
2724
2725 begin
2726 raise EDatabaseError.Create(SApplyRecNotSupported);
2727 end;
2728
2729 procedure TCustomBufDataset.CancelRecordUpdateBuffer(AUpdateBufferIndex: integer; var ABookmark: TBufBookmark);
2730 var
2731 ARecordBuffer: TRecordBuffer;
2732 NBookmark : TBufBookmark;
2733 i : integer;
2734 begin
2735 with FUpdateBuffer[AUpdateBufferIndex] do
2736 if Assigned(BookmarkData.BookmarkData) then // this is used to exclude buffers which are already handled
2737 begin
2738 case UpdateKind of
2739 ukModify:
2740 begin
2741 CurrentIndexBuf.GotoBookmark(@BookmarkData);
2742 move(TRecordBuffer(OldValuesBuffer)^, TRecordBuffer(CurrentIndexBuf.CurrentBuffer)^, FRecordSize);
2743 FreeRecordBuffer(OldValuesBuffer);
2744 end;
2745 ukDelete:
2746 if (assigned(OldValuesBuffer)) then
2747 begin
2748 CurrentIndexBuf.GotoBookmark(@NextBookmarkData);
2749 CurrentIndexBuf.InsertRecordBeforeCurrentRecord(TRecordBuffer(BookmarkData.BookmarkData));
2750 CurrentIndexBuf.ScrollBackward;
2751 move(TRecordBuffer(OldValuesBuffer)^, TRecordBuffer(CurrentIndexBuf.CurrentBuffer)^, FRecordSize);
2752 FreeRecordBuffer(OldValuesBuffer);
2753 inc(FBRecordCount);
2754 end;
2755 ukInsert:
2756 begin
2757 CurrentIndexBuf.GotoBookmark(@BookmarkData);
2758 ARecordBuffer := CurrentIndexBuf.CurrentRecord;
2759
2760 // Find next record's bookmark
2761 CurrentIndexBuf.DoScrollForward;
2762 CurrentIndexBuf.StoreCurrentRecIntoBookmark(@NBookmark);
2763 // Process (re-link) all update buffers linked to this record before this record is removed
2764 // Modified record #1, which is later deleted can be linked to another inserted record #2. In this case deleted record #1 precedes inserted #2 in update buffer.
2765 // Deleted records, which are deleted after this record is inserted are in update buffer after this record.
2766 // if we need revert inserted record which is linked from another deleted records, then we must re-link these records
2767 for i:=0 to high(FUpdateBuffer) do
2768 if (FUpdateBuffer[i].UpdateKind = ukDelete) and
2769 (FUpdateBuffer[i].NextBookmarkData.BookmarkData = BookmarkData.BookmarkData) then
2770 FUpdateBuffer[i].NextBookmarkData := NBookmark;
2771
2772 // ReSync won't work if the CurrentBuffer is freed ... so in this case move to next/prior record
2773 if CurrentIndexBuf.SameBookmarks(@BookmarkData,@ABookmark) then
2774 with CurrentIndexBuf do
2775 begin
2776 GotoBookmark(@ABookmark);
2777 if ScrollForward = grEOF then
2778 if ScrollBackward = grBOF then
2779 ScrollLast; // last record will be removed from index, so move to spare record
2780 StoreCurrentRecIntoBookmark(@ABookmark);
2781 end;
2782
2783 RemoveRecordFromIndexes(BookmarkData);
2784 FreeRecordBuffer(ARecordBuffer);
2785 dec(FBRecordCount);
2786 end;
2787 end;
2788 BookmarkData.BookmarkData := nil;
2789 end;
2790 end;
2791
2792 procedure TCustomBufDataset.RevertRecord;
2793 var
2794 ABookmark : TBufBookmark;
2795 begin
2796 CheckBrowseMode;
2797
2798 if GetActiveRecordUpdateBuffer then
2799 begin
2800 CurrentIndexBuf.StoreCurrentRecIntoBookmark(@ABookmark);
2801
2802 CancelRecordUpdateBuffer(FCurrentUpdateBuffer, ABookmark);
2803
2804 // remove update record of current record from update-buffer array
2805 Move(FUpdateBuffer[FCurrentUpdateBuffer+1], FUpdateBuffer[FCurrentUpdateBuffer], (High(FUpdateBuffer)-FCurrentUpdateBuffer)*SizeOf(TRecUpdateBuffer));
2806 SetLength(FUpdateBuffer, High(FUpdateBuffer));
2807
2808 CurrentIndexBuf.GotoBookmark(@ABookmark);
2809
2810 Resync([]);
2811 end;
2812 end;
2813
2814 procedure TCustomBufDataset.CancelUpdates;
2815 var
2816 ABookmark : TBufBookmark;
2817 r : Integer;
2818 begin
2819 CheckBrowseMode;
2820
2821 if Length(FUpdateBuffer) > 0 then
2822 begin
2823 CurrentIndexBuf.StoreCurrentRecIntoBookmark(@ABookmark);
2824
2825 for r := High(FUpdateBuffer) downto 0 do
2826 CancelRecordUpdateBuffer(r, ABookmark);
2827 SetLength(FUpdateBuffer, 0);
2828
2829 CurrentIndexBuf.GotoBookmark(@ABookmark);
2830
2831 Resync([]);
2832 end;
2833 end;
2834
2835 procedure TCustomBufDataset.SetOnUpdateError(const AValue: TResolverErrorEvent);
2836
2837 begin
2838 FOnUpdateError := AValue;
2839 end;
2840
2841 procedure TCustomBufDataset.ApplyUpdates; // For backward compatibility
2842
2843 begin
2844 ApplyUpdates(0);
2845 end;
2846
2847 procedure TCustomBufDataset.ApplyUpdates(MaxErrors: Integer);
2848
2849 var r : Integer;
2850 FailedCount : integer;
2851 Response : TResolverResponse;
2852 StoreCurrRec : TBufBookmark;
2853 AUpdateError : EUpdateError;
2854
2855 begin
2856 CheckBrowseMode;
2857
2858 CurrentIndexBuf.StoreCurrentRecIntoBookmark(@StoreCurrRec);
2859
2860 r := 0;
2861 FailedCount := 0;
2862 Response := rrApply;
2863 DisableControls;
2864 try
2865 while (r < Length(FUpdateBuffer)) and (Response <> rrAbort) do
2866 begin
2867 // If the record is first inserted and afterwards deleted, do nothing
2868 if not ((FUpdateBuffer[r].UpdateKind=ukDelete) and not (assigned(FUpdateBuffer[r].OldValuesBuffer))) then
2869 begin
2870 CurrentIndexBuf.GotoBookmark(@FUpdateBuffer[r].BookmarkData);
2871 // Synchronise the CurrentBuffer to the ActiveBuffer
2872 CurrentRecordToBuffer(ActiveBuffer);
2873 Response := rrApply;
2874 try
2875 ApplyRecUpdate(FUpdateBuffer[r].UpdateKind);
2876 except
2877 on E: EDatabaseError do
2878 begin
2879 Inc(FailedCount);
2880 if FailedCount > word(MaxErrors) then
2881 Response := rrAbort
2882 else
2883 Response := rrSkip;
2884 if assigned(FOnUpdateError) then
2885 begin
2886 AUpdateError := PSGetUpdateException(Exception(AcquireExceptionObject), nil);
2887 FOnUpdateError(Self, Self, AUpdateError, FUpdateBuffer[r].UpdateKind, Response);
2888 AUpdateError.Free;
2889 if Response in [rrApply, rrIgnore] then dec(FailedCount);
2890 if Response = rrApply then dec(r);
2891 end
2892 else if Response = rrAbort then
2893 begin
2894 AUpdateError := PSGetUpdateException(Exception(AcquireExceptionObject), nil);
2895 raise AUpdateError;
2896 end;
2897 end
2898 else
2899 raise;
2900 end;
2901 if Response in [rrApply, rrIgnore] then
2902 begin
2903 FreeRecordBuffer(FUpdateBuffer[r].OldValuesBuffer);
2904 if FUpdateBuffer[r].UpdateKind = ukDelete then
2905 FreeRecordBuffer( TRecordBuffer(FUpdateBuffer[r].BookmarkData.BookmarkData));
2906 FUpdateBuffer[r].BookmarkData.BookmarkData := nil;
2907 end
2908 end;
2909 inc(r);
2910 end;
2911 finally
2912 if (FailedCount=0) and Not ManualMergeChangeLog then
2913 MergeChangeLog;
2914 InternalGotoBookmark(@StoreCurrRec);
2915 Resync([]);
2916 EnableControls;
2917 end;
2918 end;
2919
2920 procedure TCustomBufDataset.MergeChangeLog;
2921
2922 var r : Integer;
2923
2924 begin
2925 for r:=0 to length(FUpdateBuffer)-1 do
2926 if assigned(FUpdateBuffer[r].OldValuesBuffer) then
2927 FreeMem(FUpdateBuffer[r].OldValuesBuffer);
2928 SetLength(FUpdateBuffer,0);
2929
2930 if assigned(FUpdateBlobBuffers) then for r:=0 to length(FUpdateBlobBuffers)-1 do
2931 if assigned(FUpdateBlobBuffers[r]) then
2932 begin
2933 // update blob buffer is already referenced from record buffer (see InternalPost)
2934 if FUpdateBlobBuffers[r]^.OrgBufID >= 0 then
2935 begin
2936 FreeBlobBuffer(FBlobBuffers[FUpdateBlobBuffers[r]^.OrgBufID]);
2937 FBlobBuffers[FUpdateBlobBuffers[r]^.OrgBufID] := FUpdateBlobBuffers[r];
2938 end
2939 else
2940 begin
2941 setlength(FBlobBuffers,length(FBlobBuffers)+1);
2942 FUpdateBlobBuffers[r]^.OrgBufID := high(FBlobBuffers);
2943 FBlobBuffers[high(FBlobBuffers)] := FUpdateBlobBuffers[r];
2944 end;
2945 end;
2946 SetLength(FUpdateBlobBuffers,0);
2947 end;
2948
2949
2950 procedure TCustomBufDataset.InternalCancel;
2951
2952 Var i : integer;
2953
2954 begin
2955 if assigned(FUpdateBlobBuffers) then for i:=0 to high(FUpdateBlobBuffers) do
2956 if assigned(FUpdateBlobBuffers[i]) and (FUpdateBlobBuffers[i]^.FieldNo>0) then
2957 FreeBlobBuffer(FUpdateBlobBuffers[i]);
2958 end;
2959
2960 procedure TCustomBufDataset.InternalPost;
2961
2962 Var ABuff : TRecordBuffer;
2963 i : integer;
2964 ABookmark : PBufBookmark;
2965
2966 begin
2967 inherited InternalPost;
2968
2969 if assigned(FUpdateBlobBuffers) then for i:=0 to high(FUpdateBlobBuffers) do
2970 if assigned(FUpdateBlobBuffers[i]) and (FUpdateBlobBuffers[i]^.FieldNo>0) then
2971 FUpdateBlobBuffers[i]^.FieldNo := -1;
2972
2973 if State = dsInsert then
2974 begin
2975 if assigned(FAutoIncField) then
2976 begin
2977 FAutoIncField.AsInteger := FAutoIncValue;
2978 inc(FAutoIncValue);
2979 end;
2980 // The active buffer is the newly created TDataSet record,
2981 // from which the bookmark is set to the record where the new record should be
2982 // inserted
2983 ABookmark := PBufBookmark(ActiveBuffer + FRecordSize);
2984 // Create the new record buffer
2985 ABuff := IntAllocRecordBuffer;
2986
2987 // Add new record to all active indexes
2988 for i := 0 to FIndexes.Count-1 do
2989 if BufIndexdefs[i].IsActiveIndex(FCurrentIndexDef) then
2990 begin
2991 if ABookmark^.BookmarkFlag = bfEOF then
2992 // append at end
2993 BufIndexes[i].ScrollLast
2994 else
2995 // insert (before current record)
2996 BufIndexes[i].GotoBookmark(ABookmark);
2997
2998 // insert new record before current record
2999 BufIndexes[i].InsertRecordBeforeCurrentRecord(ABuff);
3000 // newly inserted record becomes current record
3001 BufIndexes[i].ScrollBackward;
3002 end;
3003
3004 // Link the newly created record buffer to the newly created TDataSet record
3005 CurrentIndexBuf.StoreCurrentRecIntoBookmark(ABookmark);
3006 ABookmark^.BookmarkFlag := bfInserted;
3007
3008 inc(FBRecordCount);
3009 end
3010 else
3011 InternalSetToRecord(ActiveBuffer);
3012
3013 // If there is no updatebuffer already, add one
3014 if not GetActiveRecordUpdateBuffer then
3015 begin
3016 // Add a new updatebuffer
3017 FCurrentUpdateBuffer := length(FUpdateBuffer);
3018 SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
3019
3020 // Store a bookmark of the current record into the updatebuffer's bookmark
3021 CurrentIndexBuf.StoreCurrentRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
3022
3023 if State = dsEdit then
3024 begin
3025 // Create an OldValues buffer with the old values of the record
3026 FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukModify;
3027 FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := IntAllocRecordBuffer;
3028 // Move only the real data
3029 move(CurrentIndexBuf.CurrentBuffer^, FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^, FRecordSize);
3030 end
3031 else
3032 begin
3033 FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukInsert;
3034 FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := nil;
3035 end;
3036 end;
3037
3038 Move(ActiveBuffer^, CurrentIndexBuf.CurrentBuffer^, FRecordSize);
3039
3040 // new data are now in current record so reorder current record if needed
3041 for i := 0 to FIndexes.Count-1 do
3042 if BufIndexDefs[i].MustBuild(FCurrentIndexDef) then
3043 BufIndexes[i].OrderCurrentRecord;
3044 end;
3045
3046 procedure TCustomBufDataset.CalcRecordSize;
3047
3048 var x : longint;
3049
3050 begin
3051 FNullmaskSize := (FieldDefs.Count+7) div 8;
3052 {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
3053 FNullmaskSize:=Align(FNullmaskSize,4);
3054 {$ENDIF}
3055 FRecordSize := FNullmaskSize;
3056 SetLength(FFieldBufPositions,FieldDefs.count);
3057 for x := 0 to FieldDefs.count-1 do
3058 begin
3059 FFieldBufPositions[x] := FRecordSize;
3060 inc(FRecordSize, GetFieldSize(FieldDefs[x]));
3061 end;
3062 end;
3063
GetIndexFieldNamesnull3064 function TCustomBufDataset.GetIndexFieldNames: String;
3065
3066 var
3067 i, p: integer;
3068 s: string;
3069 IndexBuf: TBufIndex;
3070
3071 begin
3072 Result := FIndexFieldNames;
3073 IndexBuf:=CurrentIndexBuf;
3074 if (IndexBuf=Nil) then
3075 Exit;
3076 Result:='';
3077 for i := 1 to WordCount(IndexBuf.FieldsName, [Limiter]) do
3078 begin
3079 s := ExtractDelimited(i, IndexBuf.FieldsName, [Limiter]);
3080 p := Pos(s, IndexBuf.DescFields);
3081 if p>0 then
3082 s := s + Desc;
3083 Result := Result + Limiter + s;
3084 end;
3085 if (Length(Result)>0) and (Result[1]=Limiter) then
3086 system.Delete(Result, 1, 1);
3087 end;
3088
GetIndexNamenull3089 function TCustomBufDataset.GetIndexName: String;
3090 begin
3091 if (FIndexes.Count>0) and (CurrentIndexBuf <> nil) then
3092 result := CurrentIndexBuf.Name
3093 else
3094 result := FIndexName;
3095 end;
3096
GetBufUniDirectionalnull3097 function TCustomBufDataset.GetBufUniDirectional: boolean;
3098 begin
3099 result := IsUniDirectional;
3100 end;
3101
GetPacketReadernull3102 function TCustomBufDataset.GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader;
3103
3104 var
3105 APacketReader: TDataPacketReader;
3106 APacketReaderReg: TDatapacketReaderRegistration;
3107 Fmt : TDataPacketFormat;
3108 begin
3109 fmt:=Format;
3110 if (Fmt=dfDefault) then
3111 fmt:=DefaultReadFileFormat;
3112 if fmt=dfDefault then
3113 APacketReader := CreateDefaultPacketReader(AStream)
3114 else if GetRegisterDatapacketReader(AStream, fmt, APacketReaderReg) then
3115 APacketReader := APacketReaderReg.ReaderClass.Create(Self, AStream)
3116 else if TFpcBinaryDatapacketReader.RecognizeStream(AStream) then
3117 begin
3118 AStream.Seek(0, soFromBeginning);
3119 APacketReader := TFpcBinaryDatapacketReader.Create(Self, AStream)
3120 end
3121 else
3122 DatabaseError(SStreamNotRecognised,Self);
3123 Result:=APacketReader;
3124 end;
3125
GetRecordSizenull3126 function TCustomBufDataset.GetRecordSize : Word;
3127
3128 begin
3129 result := FRecordSize + BookmarkSize;
3130 end;
3131
GetChangeCountnull3132 function TCustomBufDataset.GetChangeCount: integer;
3133
3134 begin
3135 result := length(FUpdateBuffer);
3136 end;
3137
3138
3139 procedure TCustomBufDataset.InternalInitRecord(Buffer: TRecordBuffer);
3140
3141 begin
3142 FillChar(Buffer^, FRecordSize, #0);
3143
3144 fillchar(Buffer^,FNullmaskSize,255);
3145 end;
3146
3147 procedure TCustomBufDataset.SetRecNo(Value: Longint);
3148
3149 var ABookmark : TBufBookmark;
3150
3151 begin
3152 CheckBrowseMode;
3153 if Value > RecordCount then
3154 repeat until (getnextpacket < FPacketRecords) or (Value <= RecordCount) or (FPacketRecords = -1);
3155
3156 if (Value > RecordCount) or (Value < 1) then
3157 begin
3158 DatabaseError(SNoSuchRecord, Self);
3159 exit;
3160 end;
3161
3162 CurrentIndexBuf.RecNo:=Value;
3163 CurrentIndexBuf.StoreCurrentRecIntoBookmark(@ABookmark);
3164 GotoBookmark(@ABookmark);
3165 end;
3166
GetRecNonull3167 function TCustomBufDataset.GetRecNo: Longint;
3168
3169 begin
3170 if IsUniDirectional then
3171 Result := -1
3172 else if (FBRecordCount = 0) or (State = dsInsert) then
3173 Result := 0
3174 else
3175 begin
3176 UpdateCursorPos;
3177 Result := CurrentIndexBuf.RecNo;
3178 end;
3179 end;
3180
IsCursorOpennull3181 function TCustomBufDataset.IsCursorOpen: Boolean;
3182
3183 begin
3184 Result := FOpen;
3185 end;
3186
GetRecordCountnull3187 function TCustomBufDataset.GetRecordCount: Longint;
3188 begin
3189 if Active then
3190 Result := FBRecordCount
3191 else
3192 Result:=0;
3193 end;
3194
UpdateStatusnull3195 function TCustomBufDataset.UpdateStatus: TUpdateStatus;
3196
3197 begin
3198 Result:=usUnmodified;
3199 if GetActiveRecordUpdateBuffer then
3200 case FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind of
3201 ukModify : Result := usModified;
3202 ukInsert : Result := usInserted;
3203 ukDelete : Result := usDeleted;
3204 end;
3205 end;
3206
GetNewBlobBuffernull3207 function TCustomBufDataset.GetNewBlobBuffer : PBlobBuffer;
3208
3209 var ABlobBuffer : PBlobBuffer;
3210
3211 begin
3212 setlength(FBlobBuffers,length(FBlobBuffers)+1);
3213 new(ABlobBuffer);
3214 fillbyte(ABlobBuffer^,sizeof(ABlobBuffer^),0);
3215 ABlobBuffer^.OrgBufID := high(FBlobBuffers);
3216 FBlobBuffers[high(FBlobBuffers)] := ABlobBuffer;
3217 result := ABlobBuffer;
3218 end;
3219
GetNewWriteBlobBuffernull3220 function TCustomBufDataset.GetNewWriteBlobBuffer : PBlobBuffer;
3221
3222 var ABlobBuffer : PBlobBuffer;
3223
3224 begin
3225 setlength(FUpdateBlobBuffers,length(FUpdateBlobBuffers)+1);
3226 new(ABlobBuffer);
3227 fillbyte(ABlobBuffer^,sizeof(ABlobBuffer^),0);
3228 FUpdateBlobBuffers[high(FUpdateBlobBuffers)] := ABlobBuffer;
3229 result := ABlobBuffer;
3230 end;
3231
3232 procedure TCustomBufDataset.FreeBlobBuffer(var ABlobBuffer: PBlobBuffer);
3233
3234 begin
3235 if not Assigned(ABlobBuffer) then Exit;
3236 FreeMem(ABlobBuffer^.Buffer, ABlobBuffer^.Size);
3237 Dispose(ABlobBuffer);
3238 ABlobBuffer := Nil;
3239 end;
3240
3241 { TBufBlobStream }
3242
Seeknull3243 function TBufBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
3244
3245 begin
3246 Case Origin of
3247 soFromBeginning : FPosition:=Offset;
3248 soFromEnd : FPosition:=FBlobBuffer^.Size+Offset;
3249 soFromCurrent : FPosition:=FPosition+Offset;
3250 end;
3251 Result:=FPosition;
3252 end;
3253
3254
Readnull3255 function TBufBlobStream.Read(var Buffer; Count: Longint): Longint;
3256
3257 var ptr : pointer;
3258
3259 begin
3260 if FPosition + Count > FBlobBuffer^.Size then
3261 Count := FBlobBuffer^.Size-FPosition;
3262 ptr := FBlobBuffer^.Buffer+FPosition;
3263 move(ptr^, Buffer, Count);
3264 inc(FPosition, Count);
3265 result := Count;
3266 end;
3267
Writenull3268 function TBufBlobStream.Write(const Buffer; Count: Longint): Longint;
3269
3270 var ptr : pointer;
3271
3272 begin
3273 ReAllocMem(FBlobBuffer^.Buffer, FPosition+Count);
3274 ptr := FBlobBuffer^.Buffer+FPosition;
3275 move(buffer, ptr^, Count);
3276 inc(FBlobBuffer^.Size, Count);
3277 inc(FPosition, Count);
3278 FModified := True;
3279 Result := Count;
3280 end;
3281
3282 constructor TBufBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
3283
3284 var bufblob : TBufBlobField;
3285 CurrBuff : TRecordBuffer;
3286
3287 begin
3288 FField := Field;
3289 FDataSet := Field.DataSet as TCustomBufDataset;
3290 with FDataSet do
3291 if Mode = bmRead then
3292 begin
3293 if not Field.GetData(@bufblob) then
3294 DatabaseError(SFieldIsNull);
3295 if not assigned(bufblob.BlobBuffer) then
3296 begin
3297 bufblob.BlobBuffer := GetNewBlobBuffer;
3298 LoadBlobIntoBuffer(FieldDefs[Field.FieldNo-1], @bufblob);
3299 end;
3300 FBlobBuffer := bufblob.BlobBuffer;
3301 end
3302 else if Mode=bmWrite then
3303 begin
3304 FBlobBuffer := GetNewWriteBlobBuffer;
3305 FBlobBuffer^.FieldNo := Field.FieldNo;
3306 if Field.GetData(@bufblob) and assigned(bufblob.BlobBuffer) then
3307 FBlobBuffer^.OrgBufID := bufblob.BlobBuffer^.OrgBufID
3308 else
3309 FBlobBuffer^.OrgBufID := -1;
3310 bufblob.BlobBuffer := FBlobBuffer;
3311
3312 CurrBuff := GetCurrentBuffer;
3313 // unset null flag for blob field
3314 unSetFieldIsNull(PByte(CurrBuff), Field.FieldNo-1);
3315 // redirect pointer in current record buffer to new write blob buffer
3316 inc(CurrBuff, FDataSet.FFieldBufPositions[Field.FieldNo-1]);
3317 Move(bufblob, CurrBuff^, FDataSet.GetFieldSize(FDataSet.FieldDefs[Field.FieldNo-1]));
3318 FModified := True;
3319 end;
3320 end;
3321
3322 destructor TBufBlobStream.Destroy;
3323 begin
3324 if FModified then
3325 begin
3326 // if TBufBlobStream was requested, but no data was written, then Size = 0;
3327 // used by TBlobField.Clear, so in this case set Field to null
3328 //FField.Modified := True; // should be set to True, but TBlobField.Modified is never reset
3329
3330 if not (FDataSet.State in [dsFilter, dsCalcFields, dsNewValue]) then
3331 begin
3332 if FBlobBuffer^.Size = 0 then // empty blob = IsNull
3333 // blob stream should be destroyed while DataSet is in write state
3334 SetFieldIsNull(PByte(FDataSet.GetCurrentBuffer), FField.FieldNo-1);
3335 FDataSet.DataEvent(deFieldChange, PtrInt(FField));
3336 end;
3337 end;
3338 inherited Destroy;
3339 end;
3340
CreateBlobStreamnull3341 function TCustomBufDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
3342
3343 var bufblob : TBufBlobField;
3344
3345 begin
3346 Result := nil;
3347 case Mode of
3348 bmRead:
3349 if not Field.GetData(@bufblob) then Exit;
3350 bmWrite:
3351 begin
3352 if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then
3353 DatabaseErrorFmt(SNotEditing, [Name], Self);
3354 if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
3355 DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName]);
3356 end;
3357 end;
3358 Result := TBufBlobStream.Create(Field as TBlobField, Mode);
3359 end;
3360
3361 procedure TCustomBufDataset.SetDatasetPacket(AReader: TDataPacketReader);
3362 begin
3363 FDatasetReader := AReader;
3364 try
3365 Open;
3366 finally
3367 FDatasetReader := nil;
3368 end;
3369 end;
3370
3371 procedure TCustomBufDataset.GetDatasetPacket(AWriter: TDataPacketReader);
3372
3373 procedure StoreUpdateBuffer(AUpdBuffer : TRecUpdateBuffer; var ARowState: TRowState);
3374 var AThisRowState : TRowState;
3375 AStoreUpdBuf : Integer;
3376 begin
3377 if AUpdBuffer.UpdateKind = ukModify then
3378 begin
3379 AThisRowState := [rsvOriginal];
3380 ARowState:=[rsvUpdated];
3381 end
3382 else if AUpdBuffer.UpdateKind = ukDelete then
3383 begin
3384 AStoreUpdBuf:=FCurrentUpdateBuffer;
3385 if GetRecordUpdateBuffer(AUpdBuffer.BookmarkData,True,False) then
3386 repeat
3387 if CurrentIndexBuf.SameBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData, @AUpdBuffer.BookmarkData) then
3388 StoreUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer], ARowState);
3389 until not GetRecordUpdateBuffer(AUpdBuffer.BookmarkData,True,True);
3390 FCurrentUpdateBuffer:=AStoreUpdBuf;
3391 AThisRowState := [rsvDeleted];
3392 end
3393 else // ie: UpdateKind = ukInsert
3394 ARowState := [rsvInserted];
3395
3396 FFilterBuffer:=AUpdBuffer.OldValuesBuffer;
3397 // OldValuesBuffer is nil if the record is either inserted or inserted and then deleted
3398 if assigned(FFilterBuffer) then
3399 FDatasetReader.StoreRecord(AThisRowState,FCurrentUpdateBuffer);
3400 end;
3401
3402 procedure HandleUpdateBuffersFromRecord(AFindNext : boolean; ARecBookmark : TBufBookmark; var ARowState: TRowState);
3403 var StoreUpdBuf1,StoreUpdBuf2 : Integer;
3404 begin
3405 if not AFindNext then ARowState:=[];
3406 if GetRecordUpdateBuffer(ARecBookmark,True,AFindNext) then
3407 begin
3408 if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind=ukDelete then
3409 begin
3410 StoreUpdBuf1:=FCurrentUpdateBuffer;
3411 HandleUpdateBuffersFromRecord(True,ARecBookmark,ARowState);
3412 StoreUpdBuf2:=FCurrentUpdateBuffer;
3413 FCurrentUpdateBuffer:=StoreUpdBuf1;
3414 StoreUpdateBuffer(FUpdateBuffer[StoreUpdBuf1], ARowState);
3415 FCurrentUpdateBuffer:=StoreUpdBuf2;
3416 end
3417 else
3418 begin
3419 StoreUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer], ARowState);
3420 HandleUpdateBuffersFromRecord(True,ARecBookmark,ARowState);
3421 end;
3422 end
3423 end;
3424
3425 var ScrollResult : TGetResult;
3426 SavedState : TDataSetState;
3427 ABookMark : PBufBookmark;
3428 ATBookmark : TBufBookmark;
3429 RowState : TRowState;
3430
3431 begin
3432 FDatasetReader := AWriter;
3433 try
3434 // CheckActive;
3435 ABookMark:=@ATBookmark;
3436 FDatasetReader.StoreFieldDefs(FAutoIncValue);
3437
3438 SavedState:=SetTempState(dsFilter);
3439 ScrollResult:=CurrentIndexBuf.ScrollFirst;
3440 while ScrollResult=grOK do
3441 begin
3442 RowState:=[];
3443 CurrentIndexBuf.StoreCurrentRecIntoBookmark(ABookmark);
3444 // updates related to current record are stored first
3445 HandleUpdateBuffersFromRecord(False,ABookmark^,RowState);
3446 // now store current record
3447 FFilterBuffer:=CurrentIndexBuf.CurrentBuffer;
3448 if RowState=[] then
3449 FDatasetReader.StoreRecord([])
3450 else
3451 FDatasetReader.StoreRecord(RowState,FCurrentUpdateBuffer);
3452
3453 ScrollResult:=CurrentIndexBuf.ScrollForward;
3454 if ScrollResult<>grOK then
3455 begin
3456 if getnextpacket>0 then
3457 ScrollResult := CurrentIndexBuf.ScrollForward;
3458 end;
3459 end;
3460 // There could be an update buffer linked to the last (spare) record
3461 CurrentIndexBuf.StoreSpareRecIntoBookmark(ABookmark);
3462 HandleUpdateBuffersFromRecord(False,ABookmark^,RowState);
3463
3464 RestoreState(SavedState);
3465
3466 FDatasetReader.FinalizeStoreRecords;
3467 finally
3468 FDatasetReader := nil;
3469 end;
3470 end;
3471
3472 procedure TCustomBufDataset.LoadFromStream(AStream: TStream; Format: TDataPacketFormat);
3473 var APacketReader : TDataPacketReader;
3474 begin
3475 CheckBiDirectional;
3476 APacketReader:=GetPacketReader(Format, AStream);
3477 try
3478 SetDatasetPacket(APacketReader);
3479 finally
3480 APacketReader.Free;
3481 end;
3482 end;
3483
3484 procedure TCustomBufDataset.SaveToStream(AStream: TStream; Format: TDataPacketFormat);
3485 var APacketReaderReg : TDatapacketReaderRegistration;
3486 APacketWriter : TDataPacketReader;
3487 Fmt : TDataPacketFormat;
3488 begin
3489 CheckBiDirectional;
3490 fmt:=Format;
3491 if Fmt=dfDefault then
3492 fmt:=DefaultWriteFileFormat;
3493 if fmt=dfDefault then
3494 APacketWriter := CreateDefaultPacketReader(AStream)
3495 else if GetRegisterDatapacketReader(Nil,fmt,APacketReaderReg) then
3496 APacketWriter := APacketReaderReg.ReaderClass.Create(Self, AStream)
3497 else if fmt = dfBinary then
3498 APacketWriter := TFpcBinaryDatapacketReader.Create(Self, AStream)
3499 else
3500 DatabaseError(SNoReaderClassRegistered,Self);
3501 try
3502 GetDatasetPacket(APacketWriter);
3503 finally
3504 APacketWriter.Free;
3505 end;
3506 end;
3507
3508 procedure TCustomBufDataset.LoadFromFile(AFileName: string; Format: TDataPacketFormat);
3509
3510 var
3511 AFileStream : TFileStream;
3512
3513 begin
3514 if AFileName='' then
3515 AFileName := FFileName;
3516 AFileStream := TFileStream.Create(AFileName,fmOpenRead);
3517 try
3518 LoadFromStream(AFileStream, Format);
3519 finally
3520 AFileStream.Free;
3521 end;
3522 end;
3523
3524 procedure TCustomBufDataset.SaveToFile(AFileName: string; Format: TDataPacketFormat);
3525
3526 var
3527 AFileStream : TFileStream;
3528
3529 begin
3530 if AFileName='' then
3531 AFileName := FFileName;
3532 AFileStream := TFileStream.Create(AFileName,fmCreate);
3533 try
3534 SaveToStream(AFileStream, Format);
3535 finally
3536 AFileStream.Free;
3537 end;
3538 end;
3539
3540 procedure TCustomBufDataset.CreateDataset;
3541
3542 var
3543 AStoreFileName: string;
3544
3545 begin
3546 CheckInactive;
3547 if ((Fields.Count=0) or (FieldDefs.Count=0)) then
3548 begin
3549 if (FieldDefs.Count>0) then
3550 CreateFields
3551 else if (Fields.Count>0) then
3552 begin
3553 InitFieldDefsFromFields;
3554 BindFields(True);
3555 end
3556 else
3557 raise Exception.Create(SErrNoFieldsDefined);
3558 end;
3559 if FAutoIncValue<0 then
3560 FAutoIncValue:=1;
3561 // When a FileName is set, do not read from this file; we want empty dataset
3562 AStoreFileName:=FFileName;
3563 FFileName := '';
3564 try
3565 Open;
3566 finally
3567 FFileName:=AStoreFileName;
3568 end;
3569 end;
3570
3571 procedure TCustomBufDataset.Clear;
3572 begin
3573 Close;
3574 FieldDefs.Clear;
3575 Fields.Clear;
3576 end;
3577
BookmarkValidnull3578 function TCustomBufDataset.BookmarkValid(ABookmark: TBookmark): Boolean;
3579 begin
3580 Result:=Assigned(CurrentIndexBuf) and CurrentIndexBuf.BookmarkValid(pointer(ABookmark));
3581 end;
3582
CompareBookmarksnull3583 function TCustomBufDataset.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint;
3584 begin
3585 if Bookmark1 = Bookmark2 then
3586 Result := 0
3587 else if not assigned(Bookmark1) then
3588 Result := 1
3589 else if not assigned(Bookmark2) then
3590 Result := -1
3591 else if assigned(CurrentIndexBuf) then
3592 Result := CurrentIndexBuf.CompareBookmarks(pointer(Bookmark1),pointer(Bookmark2))
3593 else
3594 Result := -1;
3595 end;
3596
3597 procedure TCustomBufDataset.IntLoadFieldDefsFromFile;
3598
3599 begin
3600 FReadFromFile := True;
3601 if not assigned(FDatasetReader) then
3602 begin
3603 FFileStream := TFileStream.Create(FileName, fmOpenRead);
3604 FDatasetReader := GetPacketReader(dfDefault, FFileStream);
3605 end;
3606
3607 FieldDefs.Clear;
3608 FDatasetReader.LoadFieldDefs(FAutoIncValue);
3609 if DefaultFields then
3610 CreateFields
3611 else
3612 BindFields(true);
3613 end;
3614
3615 procedure TCustomBufDataset.IntLoadRecordsFromFile;
3616
3617 var
3618 SavedState : TDataSetState;
3619 ARowState : TRowState;
3620 AUpdOrder : integer;
3621 i : integer;
3622 DefIdx : TBufIndex;
3623
3624 begin
3625 CheckBiDirectional;
3626 DefIdx:=DefaultBufferIndex;
3627 FDatasetReader.InitLoadRecords;
3628 SavedState:=SetTempState(dsFilter);
3629
3630 while FDatasetReader.GetCurrentRecord do
3631 begin
3632 ARowState := FDatasetReader.GetRecordRowState(AUpdOrder);
3633 if rsvOriginal in ARowState then
3634 begin
3635 if length(FUpdateBuffer) < (AUpdOrder+1) then
3636 SetLength(FUpdateBuffer,AUpdOrder+1);
3637
3638 FCurrentUpdateBuffer:=AUpdOrder;
3639
3640 FFilterBuffer:=IntAllocRecordBuffer;
3641 fillchar(FFilterBuffer^,FNullmaskSize,0);
3642 FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := FFilterBuffer;
3643 FDatasetReader.RestoreRecord;
3644
3645 FDatasetReader.GotoNextRecord;
3646 if not FDatasetReader.GetCurrentRecord then
3647 DatabaseError(SStreamNotRecognised,Self);
3648 ARowState := FDatasetReader.GetRecordRowState(AUpdOrder);
3649 if rsvUpdated in ARowState then
3650 FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukModify
3651 else
3652 DatabaseError(SStreamNotRecognised,Self);
3653
3654 FFilterBuffer:=DefIdx.SpareBuffer;
3655 DefIdx.StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
3656 fillchar(FFilterBuffer^,FNullmaskSize,0);
3657
3658 FDatasetReader.RestoreRecord;
3659 DefIdx.AddRecord;
3660 inc(FBRecordCount);
3661 end
3662 else if rsvDeleted in ARowState then
3663 begin
3664 if length(FUpdateBuffer) < (AUpdOrder+1) then
3665 SetLength(FUpdateBuffer,AUpdOrder+1);
3666
3667 FCurrentUpdateBuffer:=AUpdOrder;
3668
3669 FFilterBuffer:=IntAllocRecordBuffer;
3670 fillchar(FFilterBuffer^,FNullmaskSize,0);
3671
3672 FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := FFilterBuffer;
3673 FDatasetReader.RestoreRecord;
3674
3675 FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukDelete;
3676 DefIdx.StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
3677 DefIdx.AddRecord;
3678 DefIdx.RemoveRecordFromIndex(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
3679 DefIdx.StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData);
3680
3681 for i := FCurrentUpdateBuffer+1 to high(FUpdateBuffer) do
3682 if DefIdx.SameBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData, @FUpdateBuffer[i].NextBookmarkData) then
3683 DefIdx.StoreSpareRecIntoBookmark(@FUpdateBuffer[i].NextBookmarkData);
3684 end
3685 else
3686 begin
3687 FFilterBuffer:=DefIdx.SpareBuffer;
3688 fillchar(FFilterBuffer^,FNullmaskSize,0);
3689 FDatasetReader.RestoreRecord;
3690 if rsvInserted in ARowState then
3691 begin
3692 if length(FUpdateBuffer) < (AUpdOrder+1) then
3693 SetLength(FUpdateBuffer,AUpdOrder+1);
3694 FCurrentUpdateBuffer:=AUpdOrder;
3695 FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukInsert;
3696 DefIdx.StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
3697 end;
3698
3699 DefIdx.AddRecord;
3700 inc(FBRecordCount);
3701 end;
3702
3703 FDatasetReader.GotoNextRecord;
3704 end;
3705
3706 RestoreState(SavedState);
3707 DefIdx.SetToFirstRecord;
3708 FAllPacketsFetched:=True;
3709 if assigned(FFileStream) then
3710 begin
3711 FreeAndNil(FFileStream);
3712 FreeAndNil(FDatasetReader);
3713 end;
3714
3715 // rebuild indexes
3716 BuildIndexes;
3717 end;
3718
3719 procedure TCustomBufDataset.DoFilterRecord(out Acceptable: Boolean);
3720 begin
3721 Acceptable := true;
3722 // check user filter
3723 if Assigned(OnFilterRecord) then
3724 OnFilterRecord(Self, Acceptable);
3725
3726 // check filtertext
3727 if Acceptable and (Length(Filter) > 0) then
3728 Acceptable := Boolean((FParser.ExtractFromBuffer(GetCurrentBuffer))^);
3729 end;
3730
3731 procedure TCustomBufDataset.SetFilterText(const Value: String);
3732 begin
3733 if Value = Filter then
3734 exit;
3735
3736 // parse
3737 ParseFilter(Value);
3738
3739 // call dataset method
3740 inherited;
3741
3742 // refilter dataset if filtered
3743 if IsCursorOpen and Filtered then Resync([]);
3744 end;
3745
3746 procedure TCustomBufDataset.SetFiltered(Value: Boolean); {override;}
3747 begin
3748 if Value = Filtered then
3749 exit;
3750
3751 // pass on to ancestor
3752 inherited;
3753
3754 // only refresh if active
3755 if IsCursorOpen then
3756 Resync([]);
3757 end;
3758
3759 procedure TCustomBufDataset.InternalRefresh;
3760
3761 var
3762 StoreDefaultFields: boolean;
3763
3764 begin
3765 if length(FUpdateBuffer)>0 then
3766 DatabaseError(SErrApplyUpdBeforeRefresh,Self);
3767 FRefreshing:=True;
3768 try
3769 StoreDefaultFields:=DefaultFields;
3770 SetDefaultFields(False);
3771 FreeFieldBuffers;
3772 ClearBuffers;
3773 InternalClose;
3774 BeforeRefreshOpenCursor;
3775 InternalOpen;
3776 SetDefaultFields(StoreDefaultFields);
3777 Finally
3778 FRefreshing:=False;
3779 end;
3780 end;
3781
3782 procedure TCustomBufDataset.BeforeRefreshOpenCursor;
3783 begin
3784 // Do nothing
3785 end;
3786
3787 procedure TCustomBufDataset.DataEvent(Event: TDataEvent; Info: PtrInt);
3788 begin
3789 if Event = deUpdateState then
3790 // Save DataSet.State set by DataSet.SetState (filter out State set by DataSet.SetTempState)
3791 FSavedState := State;
3792 inherited;
3793 end;
3794
Fetchnull3795 function TCustomBufDataset.Fetch: boolean;
3796 begin
3797 // Empty procedure to make it possible to use TCustomBufDataset as a memory dataset
3798 Result := False;
3799 end;
3800
LoadFieldnull3801 function TCustomBufDataset.LoadField(FieldDef: TFieldDef; buffer: pointer; out
3802 CreateBlob: boolean): boolean;
3803 begin
3804 // Empty procedure to make it possible to use TCustomBufDataset as a memory dataset
3805 CreateBlob := False;
3806 Result := False;
3807 end;
3808
IsReadFromPacketnull3809 function TCustomBufDataset.IsReadFromPacket: Boolean;
3810 begin
3811 Result := (FDatasetReader<>nil) or (FFileName<>'') or FReadFromFile;
3812 end;
3813
3814 procedure TCustomBufDataset.ParseFilter(const AFilter: string);
3815 begin
3816 // parser created?
3817 if Length(AFilter) > 0 then
3818 begin
3819 if (FParser = nil) and IsCursorOpen then
3820 begin
3821 FParser := TBufDatasetParser.Create(Self);
3822 end;
3823 // is there a parser now?
3824 if FParser <> nil then
3825 begin
3826 // set options
3827 FParser.PartialMatch := not (foNoPartialCompare in FilterOptions);
3828 FParser.CaseInsensitive := foCaseInsensitive in FilterOptions;
3829 // parse expression
3830 FParser.ParseExpression(AFilter);
3831 end;
3832 end;
3833 end;
3834
Locatenull3835 function TCustomBufDataset.Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions): boolean;
3836
3837 begin
3838 Result:=DoLocate(keyfields,KeyValues,Options,True);
3839 end;
3840
DoLocatenull3841 function TCustomBufDataset.DoLocate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoEvents : Boolean) : boolean;
3842
3843
3844 var SearchFields : TList;
3845 DBCompareStruct : TDBCompareStruct;
3846 ABookmark : TBufBookmark;
3847 SavedState : TDataSetState;
3848 FilterRecord : TRecordBuffer;
3849 FilterAcceptable: boolean;
3850
3851 begin
3852 // Call inherited to make sure the dataset is bi-directional
3853 Result := inherited Locate(KeyFields,KeyValues,Options);
3854 CheckActive;
3855 if IsEmpty then exit;
3856
3857 // Build the DBCompare structure
3858 SearchFields := TList.Create;
3859 try
3860 GetFieldList(SearchFields,KeyFields);
3861 if SearchFields.Count=0 then exit;
3862 ProcessFieldsToCompareStruct(SearchFields, nil, nil, [], Options, DBCompareStruct);
3863 finally
3864 SearchFields.Free;
3865 end;
3866
3867 // Set the filter buffer
3868 SavedState:=SetTempState(dsFilter);
3869 FilterRecord:=IntAllocRecordBuffer;
3870 FFilterBuffer:=FilterRecord + BufferOffset;
3871 SetFieldValues(KeyFields,KeyValues);
3872
3873 // Iterate through the records until a match is found
3874 ABookmark.BookmarkData:=nil;
3875 while true do
3876 begin
3877 // try get next record
3878 if CurrentIndexBuf.GetRecord(@ABookmark, gmNext) <> grOK then
3879 // for grEOF ABookmark points to SpareRecord, which is used for storing next record(s)
3880 if getnextpacket = 0 then
3881 break;
3882 if IndexCompareRecords(FilterRecord, ABookmark.BookmarkData, DBCompareStruct) = 0 then
3883 begin
3884 if Filtered then
3885 begin
3886 FFilterBuffer:=pointer(ABookmark.BookmarkData) + BufferOffset;
3887 // The dataset state is still dsFilter at this point, so we don't have to set it.
3888 DoFilterRecord(FilterAcceptable);
3889 if FilterAcceptable then
3890 begin
3891 Result := True;
3892 break;
3893 end;
3894 end
3895 else
3896 begin
3897 Result := True;
3898 break;
3899 end;
3900 end;
3901 end;
3902
3903 RestoreState(SavedState);
3904 FreeRecordBuffer(FilterRecord);
3905
3906 // If a match is found, jump to the found record
3907 if Result then
3908 begin
3909 ABookmark.BookmarkFlag := bfCurrent;
3910 if DoEvents then
3911 GotoBookmark(@ABookmark)
3912 else
3913 begin
3914 InternalGotoBookMark(@ABookmark);
3915 Resync([rmExact,rmCenter]);
3916 end;
3917 end;
3918 end;
3919
Lookupnull3920 function TCustomBufDataset.Lookup(const KeyFields: string;
3921 const KeyValues: Variant; const ResultFields: string): Variant;
3922 var
3923 bm:TBookmark;
3924 begin
3925 result:=Null;
3926 if IsEmpty then
3927 exit;
3928 bm:=GetBookmark;
3929 DisableControls;
3930 try
3931 if DoLocate(KeyFields,KeyValues,[],False) then
3932 begin
3933 // CalculateFields(ActiveBuffer); // not needed, done by Locate more than once
3934 result:=FieldValues[ResultFields];
3935 end;
3936 InternalGotoBookMark(pointer(bm));
3937 Resync([rmExact,rmCenter]);
3938 FreeBookmark(bm);
3939 finally
3940 EnableControls;
3941 end;
3942 end;
3943
3944 { TArrayBufIndex }
3945
TArrayBufIndex.GetBookmarkSizenull3946 function TArrayBufIndex.GetBookmarkSize: integer;
3947 begin
3948 Result:=Sizeof(TBufBookmark);
3949 end;
3950
GetCurrentBuffernull3951 function TArrayBufIndex.GetCurrentBuffer: Pointer;
3952 begin
3953 Result:=TRecordBuffer(FRecordArray[FCurrentRecInd]);
3954 end;
3955
TArrayBufIndex.GetCurrentRecordnull3956 function TArrayBufIndex.GetCurrentRecord: TRecordBuffer;
3957 begin
3958 Result:=GetCurrentBuffer;
3959 end;
3960
GetIsInitializednull3961 function TArrayBufIndex.GetIsInitialized: boolean;
3962 begin
3963 Result:=Length(FRecordArray)>0;
3964 end;
3965
GetSpareBuffernull3966 function TArrayBufIndex.GetSpareBuffer: TRecordBuffer;
3967 begin
3968 if FLastRecInd>-1 then
3969 Result:= TRecordBuffer(FRecordArray[FLastRecInd])
3970 else
3971 Result := nil;
3972 end;
3973
TArrayBufIndex.GetSpareRecordnull3974 function TArrayBufIndex.GetSpareRecord: TRecordBuffer;
3975 begin
3976 Result := GetSpareBuffer;
3977 end;
3978
3979 constructor TArrayBufIndex.Create(const ADataset: TCustomBufDataset);
3980 begin
3981 Inherited create(ADataset);
3982 FInitialBuffers:=10000;
3983 FGrowBuffer:=1000;
3984 end;
3985
ScrollBackwardnull3986 function TArrayBufIndex.ScrollBackward: TGetResult;
3987 begin
3988 if FCurrentRecInd>0 then
3989 begin
3990 dec(FCurrentRecInd);
3991 Result := grOK;
3992 end
3993 else
3994 Result := grBOF;
3995 end;
3996
TArrayBufIndex.ScrollForwardnull3997 function TArrayBufIndex.ScrollForward: TGetResult;
3998 begin
3999 if FCurrentRecInd = FLastRecInd-1 then
4000 result := grEOF
4001 else
4002 begin
4003 Result:=grOK;
4004 inc(FCurrentRecInd);
4005 end;
4006 end;
4007
TArrayBufIndex.GetCurrentnull4008 function TArrayBufIndex.GetCurrent: TGetResult;
4009 begin
4010 if FLastRecInd=0 then
4011 Result := grError
4012 else
4013 begin
4014 Result := grOK;
4015 if FCurrentRecInd = FLastRecInd then
4016 dec(FCurrentRecInd);
4017 end;
4018 end;
4019
ScrollFirstnull4020 function TArrayBufIndex.ScrollFirst: TGetResult;
4021 begin
4022 FCurrentRecInd:=0;
4023 if (FCurrentRecInd = FLastRecInd) then
4024 result := grEOF
4025 else
4026 result := grOk;
4027 end;
4028
4029 procedure TArrayBufIndex.ScrollLast;
4030 begin
4031 FCurrentRecInd:=FLastRecInd;
4032 end;
4033
4034 procedure TArrayBufIndex.SetToFirstRecord;
4035 begin
4036 // if FCurrentRecBuf = FLastRecBuf then the dataset is just opened and empty
4037 // in which case InternalFirst should do nothing (bug 7211)
4038 if FCurrentRecInd <> FLastRecInd then
4039 FCurrentRecInd := -1;
4040 end;
4041
4042 procedure TArrayBufIndex.SetToLastRecord;
4043 begin
4044 if FLastRecInd <> 0 then FCurrentRecInd := FLastRecInd;
4045 end;
4046
4047 procedure TArrayBufIndex.StoreCurrentRecord;
4048 begin
4049 FStoredRecBuf := FCurrentRecInd;
4050 end;
4051
4052 procedure TArrayBufIndex.RestoreCurrentRecord;
4053 begin
4054 FCurrentRecInd := FStoredRecBuf;
4055 end;
4056
TArrayBufIndex.CanScrollForwardnull4057 function TArrayBufIndex.CanScrollForward: Boolean;
4058 begin
4059 Result := (FCurrentRecInd < FLastRecInd-1);
4060 end;
4061
4062 procedure TArrayBufIndex.DoScrollForward;
4063 begin
4064 inc(FCurrentRecInd);
4065 end;
4066
4067 procedure TArrayBufIndex.StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark);
4068 begin
4069 with ABookmark^ do
4070 begin
4071 BookmarkInt := FCurrentRecInd;
4072 BookmarkData := FRecordArray[FCurrentRecInd];
4073 end;
4074 end;
4075
4076 procedure TArrayBufIndex.StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark
4077 );
4078 begin
4079 with ABookmark^ do
4080 begin
4081 BookmarkInt := FLastRecInd;
4082 BookmarkData := FRecordArray[FLastRecInd];
4083 end;
4084 end;
4085
TArrayBufIndex.GetRecordFromBookmarknull4086 function TArrayBufIndex.GetRecordFromBookmark(ABookmark: TBufBookmark): integer;
4087 begin
4088 // ABookmark.BookMarkBuf is nil if SetRecNo calls GotoBookmark
4089 if (ABookmark.BookmarkData<>nil) and (FRecordArray[ABookmark.BookmarkInt]<>ABookmark.BookmarkData) then
4090 begin
4091 // Start searching two records before the expected record
4092 if ABookmark.BookmarkInt > 2 then
4093 Result := ABookmark.BookmarkInt-2
4094 else
4095 Result := 0;
4096
4097 while (Result<FLastRecInd) do
4098 begin
4099 if (FRecordArray[Result] = ABookmark.BookmarkData) then exit;
4100 inc(Result);
4101 end;
4102
4103 Result:=0;
4104 while (Result<ABookmark.BookmarkInt) do
4105 begin
4106 if (FRecordArray[Result] = ABookmark.BookmarkData) then exit;
4107 inc(Result);
4108 end;
4109
4110 DatabaseError(SInvalidBookmark,Self.FDataset)
4111 end
4112 else
4113 Result := ABookmark.BookmarkInt;
4114 end;
4115
4116 procedure TArrayBufIndex.GotoBookmark(const ABookmark : PBufBookmark);
4117 begin
4118 FCurrentRecInd:=GetRecordFromBookmark(ABookmark^);
4119 end;
4120
4121 procedure TArrayBufIndex.InitialiseIndex;
4122 begin
4123 // FRecordArray:=nil;
4124 setlength(FRecordArray,FInitialBuffers);
4125 FCurrentRecInd:=-1;
4126 FLastRecInd:=-1;
4127 end;
4128
4129 procedure TArrayBufIndex.InitialiseSpareRecord(const ASpareRecord: TRecordBuffer);
4130 begin
4131 FLastRecInd := 0;
4132 // FCurrentRecInd := 0;
4133 FRecordArray[0] := ASpareRecord;
4134 end;
4135
4136 procedure TArrayBufIndex.ReleaseSpareRecord;
4137 begin
4138 SetLength(FRecordArray,FInitialBuffers);
4139 end;
4140
GetRecNonull4141 function TArrayBufIndex.GetRecNo: integer;
4142 begin
4143 Result := FCurrentRecInd+1;
4144 end;
4145
4146 procedure TArrayBufIndex.SetRecNo(ARecNo: Longint);
4147 begin
4148 FCurrentRecInd := ARecNo-1;
4149 end;
4150
4151 procedure TArrayBufIndex.InsertRecordBeforeCurrentRecord(const ARecord: TRecordBuffer);
4152 begin
4153 inc(FLastRecInd);
4154 if FLastRecInd >= length(FRecordArray) then
4155 SetLength(FRecordArray,length(FRecordArray)+FGrowBuffer);
4156
4157 Move(FRecordArray[FCurrentRecInd],FRecordArray[FCurrentRecInd+1],sizeof(Pointer)*(FLastRecInd-FCurrentRecInd));
4158 FRecordArray[FCurrentRecInd]:=ARecord;
4159 inc(FCurrentRecInd);
4160 end;
4161
4162 procedure TArrayBufIndex.RemoveRecordFromIndex(const ABookmark : TBufBookmark);
4163 var ARecordInd : integer;
4164 begin
4165 ARecordInd:=GetRecordFromBookmark(ABookmark);
4166 Move(FRecordArray[ARecordInd+1],FRecordArray[ARecordInd],sizeof(Pointer)*(FLastRecInd-ARecordInd));
4167 dec(FLastRecInd);
4168 end;
4169
4170 procedure TArrayBufIndex.BeginUpdate;
4171 begin
4172 // inherited BeginUpdate;
4173 end;
4174
4175 procedure TArrayBufIndex.AddRecord;
4176 var ARecord: TRecordBuffer;
4177 begin
4178 ARecord := FDataset.IntAllocRecordBuffer;
4179 inc(FLastRecInd);
4180 if FLastRecInd >= length(FRecordArray) then
4181 SetLength(FRecordArray,length(FRecordArray)+FGrowBuffer);
4182 FRecordArray[FLastRecInd]:=ARecord;
4183 end;
4184
4185 procedure TArrayBufIndex.EndUpdate;
4186 begin
4187 // inherited EndUpdate;
4188 end;
4189
4190
4191 { TDataPacketReader }
4192
TDataPacketReader.RowStateToBytenull4193 class function TDataPacketReader.RowStateToByte(const ARowState: TRowState
4194 ): byte;
4195 var RowStateInt : Byte;
4196 begin
4197 RowStateInt:=0;
4198 if rsvOriginal in ARowState then RowStateInt := RowStateInt+1;
4199 if rsvDeleted in ARowState then RowStateInt := RowStateInt+2;
4200 if rsvInserted in ARowState then RowStateInt := RowStateInt+4;
4201 if rsvUpdated in ARowState then RowStateInt := RowStateInt+8;
4202 Result := RowStateInt;
4203 end;
4204
TDataPacketReader.ByteToRowStatenull4205 class function TDataPacketReader.ByteToRowState(const AByte: Byte): TRowState;
4206 begin
4207 result := [];
4208 if (AByte and 1)=1 then Result := Result+[rsvOriginal];
4209 if (AByte and 2)=2 then Result := Result+[rsvDeleted];
4210 if (AByte and 4)=4 then Result := Result+[rsvInserted];
4211 if (AByte and 8)=8 then Result := Result+[rsvUpdated];
4212 end;
4213
4214 procedure TDataPacketReader.RestoreBlobField(AField: TField; ASource: pointer; ASize: integer);
4215 var
4216 ABufBlobField: TBufBlobField;
4217 begin
4218 ABufBlobField.BlobBuffer:=FDataSet.GetNewBlobBuffer;
4219 ABufBlobField.BlobBuffer^.Size:=ASize;
4220 ReAllocMem(ABufBlobField.BlobBuffer^.Buffer, ASize);
4221 move(ASource^, ABufBlobField.BlobBuffer^.Buffer^, ASize);
4222 AField.SetData(@ABufBlobField);
4223 end;
4224
4225 constructor TDataPacketReader.Create(ADataSet: TCustomBufDataset; AStream: TStream);
4226 begin
4227 FDataSet := ADataSet;
4228 FStream := AStream;
4229 end;
4230
4231
4232 { TFpcBinaryDatapacketReader }
4233
4234 constructor TFpcBinaryDatapacketReader.Create(ADataSet: TCustomBufDataset; AStream: TStream);
4235 begin
4236 inherited;
4237 FVersion := 20; // default version 2.0
4238 end;
4239
4240 procedure TFpcBinaryDatapacketReader.LoadFieldDefs(var AnAutoIncValue: integer);
4241
4242 var FldCount : word;
4243 i : integer;
4244 s : string;
4245
4246 begin
4247 // Identify version
4248 SetLength(s, 13);
4249 if (Stream.Read(s[1], 13) = 13) then
4250 case s of
4251 FpcBinaryIdent1:
4252 FVersion := 10;
4253 FpcBinaryIdent2:
4254 FVersion := Stream.ReadByte;
4255 else
4256 DatabaseError(SStreamNotRecognised,Self.FDataset);
4257 end;
4258
4259 // Read FieldDefs
4260 FldCount := Stream.ReadWord;
4261 DataSet.FieldDefs.Clear;
4262 for i := 0 to FldCount - 1 do with DataSet.FieldDefs.AddFieldDef do
4263 begin
4264 Name := Stream.ReadAnsiString;
4265 Displayname := Stream.ReadAnsiString;
4266 Size := Stream.ReadWord;
4267 DataType := TFieldType(Stream.ReadWord);
4268
4269 if Stream.ReadByte = 1 then
4270 Attributes := Attributes + [faReadonly];
4271 end;
4272 Stream.ReadBuffer(i,sizeof(i));
4273 AnAutoIncValue := i;
4274
4275 FNullBitmapSize := (FldCount + 7) div 8;
4276 SetLength(FNullBitmap, FNullBitmapSize);
4277 end;
4278
4279 procedure TFpcBinaryDatapacketReader.StoreFieldDefs(AnAutoIncValue: integer);
4280 var i : integer;
4281 begin
4282 Stream.Write(FpcBinaryIdent2[1], length(FpcBinaryIdent2));
4283 Stream.WriteByte(FVersion);
4284
4285 Stream.WriteWord(DataSet.FieldDefs.Count);
4286 for i := 0 to DataSet.FieldDefs.Count - 1 do with DataSet.FieldDefs[i] do
4287 begin
4288 Stream.WriteAnsiString(Name);
4289 Stream.WriteAnsiString(DisplayName);
4290 Stream.WriteWord(Size);
4291 Stream.WriteWord(ord(DataType));
4292
4293 if faReadonly in Attributes then
4294 Stream.WriteByte(1)
4295 else
4296 Stream.WriteByte(0);
4297 end;
4298 i := AnAutoIncValue;
4299 Stream.WriteBuffer(i,sizeof(i));
4300
4301 FNullBitmapSize := (DataSet.FieldDefs.Count + 7) div 8;
4302 SetLength(FNullBitmap, FNullBitmapSize);
4303 end;
4304
4305 procedure TFpcBinaryDatapacketReader.InitLoadRecords;
4306 begin
4307 // Do nothing
4308 end;
4309
TFpcBinaryDatapacketReader.GetCurrentRecordnull4310 function TFpcBinaryDatapacketReader.GetCurrentRecord: boolean;
4311 var Buf : byte;
4312 begin
4313 Result := (Stream.Read(Buf,1)=1) and (Buf=$fe);
4314 end;
4315
TFpcBinaryDatapacketReader.GetRecordRowStatenull4316 function TFpcBinaryDatapacketReader.GetRecordRowState(out AUpdOrder : Integer) : TRowState;
4317 var Buf : byte;
4318 begin
4319 Stream.Read(Buf,1);
4320 Result := ByteToRowState(Buf);
4321 if Result<>[] then
4322 Stream.ReadBuffer(AUpdOrder,sizeof(integer))
4323 else
4324 AUpdOrder := 0;
4325 end;
4326
4327 procedure TFpcBinaryDatapacketReader.GotoNextRecord;
4328 begin
4329 // Do Nothing
4330 end;
4331
4332 procedure TFpcBinaryDatapacketReader.RestoreRecord;
4333 var
4334 AField: TField;
4335 i: integer;
4336 L: cardinal;
4337 B: TBytes;
4338 begin
4339 with DataSet do
4340 case FVersion of
4341 10:
4342 Stream.ReadBuffer(GetCurrentBuffer^, FRecordSize); // Ugly because private members of ADataset are used...
4343 20:
4344 begin
4345 // Restore field's Null bitmap
4346 Stream.ReadBuffer(FNullBitmap[0], FNullBitmapSize);
4347 // Restore field's data
4348 for i:=0 to FieldDefs.Count-1 do
4349 begin
4350 AField := Fields.FieldByNumber(FieldDefs[i].FieldNo);
4351 if AField=nil then continue;
4352 if GetFieldIsNull(PByte(FNullBitmap), i) then
4353 AField.SetData(nil)
4354 else if AField.DataType in StringFieldTypes then
4355 AField.AsString := Stream.ReadAnsiString
4356 else
4357 begin
4358 if AField.DataType in VarLenFieldTypes then
4359 L := Stream.ReadDWord
4360 else
4361 L := AField.DataSize;
4362 SetLength(B, L);
4363 if L > 0 then
4364 Stream.ReadBuffer(B[0], L);
4365 if AField.DataType in BlobFieldTypes then
4366 RestoreBlobField(AField, @B[0], L)
4367 else
4368 AField.SetData(@B[0], False); // set it to the FilterBuffer
4369 end;
4370 end;
4371 end;
4372 end;
4373 end;
4374
4375 procedure TFpcBinaryDatapacketReader.StoreRecord(ARowState: TRowState; AUpdOrder : integer);
4376 var
4377 AField: TField;
4378 i: integer;
4379 L: cardinal;
4380 B: TBytes;
4381 begin
4382 // Record header
4383 Stream.WriteByte($fe);
4384 Stream.WriteByte(RowStateToByte(ARowState));
4385 if ARowState<>[] then
4386 Stream.WriteBuffer(AUpdOrder,sizeof(integer));
4387
4388 // Record data
4389 with DataSet do
4390 case FVersion of
4391 10:
4392 Stream.WriteBuffer(GetCurrentBuffer^, FRecordSize); // Old 1.0 version
4393 20:
4394 begin
4395 // store fields Null bitmap
4396 FillByte(FNullBitmap[0], FNullBitmapSize, 0);
4397 for i:=0 to FieldDefs.Count-1 do
4398 begin
4399 AField := Fields.FieldByNumber(FieldDefs[i].FieldNo);
4400 if assigned(AField) and AField.IsNull then
4401 SetFieldIsNull(PByte(FNullBitmap), i);
4402 end;
4403 Stream.WriteBuffer(FNullBitmap[0], FNullBitmapSize);
4404
4405 for i:=0 to FieldDefs.Count-1 do
4406 begin
4407 AField := Fields.FieldByNumber(FieldDefs[i].FieldNo);
4408 if not assigned(AField) or AField.IsNull then continue;
4409 if AField.DataType in StringFieldTypes then
4410 Stream.WriteAnsiString(AField.AsString)
4411 else
4412 begin
4413 B := AField.AsBytes;
4414 L := length(B);
4415 if AField.DataType in VarLenFieldTypes then
4416 Stream.WriteDWord(L);
4417 if L > 0 then
4418 Stream.WriteBuffer(B[0], L);
4419 end;
4420 end;
4421 end;
4422 end;
4423 end;
4424
4425 procedure TFpcBinaryDatapacketReader.FinalizeStoreRecords;
4426 begin
4427 // Do nothing
4428 end;
4429
TFpcBinaryDatapacketReader.RecognizeStreamnull4430 class function TFpcBinaryDatapacketReader.RecognizeStream(AStream: TStream): boolean;
4431 var s : string;
4432 begin
4433 SetLength(s, 13);
4434 if (AStream.Read(s[1], 13) = 13) then
4435 case s of
4436 FpcBinaryIdent1,
4437 FpcBinaryIdent2:
4438 Result := True;
4439 else
4440 Result := False;
4441 end;
4442 end;
4443
4444 { TUniDirectionalBufIndex }
4445
TUniDirectionalBufIndex.GetBookmarkSizenull4446 function TUniDirectionalBufIndex.GetBookmarkSize: integer;
4447 begin
4448 // In principle there are no bookmarks, and the size should be 0.
4449 // But there is quite some code in TCustomBufDataset that relies on
4450 // an existing bookmark of the TBufBookmark type.
4451 // This code could be moved to the TBufIndex but that would make things
4452 // more complicated and probably slower. So use a 'fake' bookmark of
4453 // size TBufBookmark.
4454 // When there are other TBufIndexes which also need special bookmark code
4455 // this can be adapted.
4456 Result:=sizeof(TBufBookmark);
4457 end;
4458
GetCurrentBuffernull4459 function TUniDirectionalBufIndex.GetCurrentBuffer: Pointer;
4460 begin
4461 result := FSPareBuffer;
4462 end;
4463
TUniDirectionalBufIndex.GetCurrentRecordnull4464 function TUniDirectionalBufIndex.GetCurrentRecord: TRecordBuffer;
4465 begin
4466 Result:=Nil;
4467 // Result:=inherited GetCurrentRecord;
4468 end;
4469
GetIsInitializednull4470 function TUniDirectionalBufIndex.GetIsInitialized: boolean;
4471 begin
4472 Result := Assigned(FSPareBuffer);
4473 end;
4474
TUniDirectionalBufIndex.GetSpareBuffernull4475 function TUniDirectionalBufIndex.GetSpareBuffer: TRecordBuffer;
4476 begin
4477 result := FSPareBuffer;
4478 end;
4479
TUniDirectionalBufIndex.GetSpareRecordnull4480 function TUniDirectionalBufIndex.GetSpareRecord: TRecordBuffer;
4481 begin
4482 result := FSPareBuffer;
4483 end;
4484
TUniDirectionalBufIndex.ScrollBackwardnull4485 function TUniDirectionalBufIndex.ScrollBackward: TGetResult;
4486 begin
4487 result := grError;
4488 end;
4489
TUniDirectionalBufIndex.ScrollForwardnull4490 function TUniDirectionalBufIndex.ScrollForward: TGetResult;
4491 begin
4492 result := grOk;
4493 end;
4494
TUniDirectionalBufIndex.GetCurrentnull4495 function TUniDirectionalBufIndex.GetCurrent: TGetResult;
4496 begin
4497 result := grOk;
4498 end;
4499
ScrollFirstnull4500 function TUniDirectionalBufIndex.ScrollFirst: TGetResult;
4501 begin
4502 Result:=grError;
4503 end;
4504
4505 procedure TUniDirectionalBufIndex.ScrollLast;
4506 begin
4507 DatabaseError(SUniDirectional);
4508 end;
4509
4510 procedure TUniDirectionalBufIndex.SetToFirstRecord;
4511 begin
4512 // for UniDirectional datasets should be [Internal]First valid method call
4513 // do nothing
4514 end;
4515
4516 procedure TUniDirectionalBufIndex.SetToLastRecord;
4517 begin
4518 DatabaseError(SUniDirectional);
4519 end;
4520
4521 procedure TUniDirectionalBufIndex.StoreCurrentRecord;
4522 begin
4523 DatabaseError(SUniDirectional);
4524 end;
4525
4526 procedure TUniDirectionalBufIndex.RestoreCurrentRecord;
4527 begin
4528 DatabaseError(SUniDirectional);
4529 end;
4530
CanScrollForwardnull4531 function TUniDirectionalBufIndex.CanScrollForward: Boolean;
4532 begin
4533 // should return true if next record is already fetched
4534 result := false;
4535 end;
4536
4537 procedure TUniDirectionalBufIndex.DoScrollForward;
4538 begin
4539 // do nothing
4540 end;
4541
4542 procedure TUniDirectionalBufIndex.StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark);
4543 begin
4544 // do nothing
4545 end;
4546
4547 procedure TUniDirectionalBufIndex.StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark);
4548 begin
4549 // do nothing
4550 end;
4551
4552 procedure TUniDirectionalBufIndex.GotoBookmark(const ABookmark: PBufBookmark);
4553 begin
4554 DatabaseError(SUniDirectional);
4555 end;
4556
4557 procedure TUniDirectionalBufIndex.InitialiseIndex;
4558 begin
4559 // do nothing
4560 end;
4561
4562 procedure TUniDirectionalBufIndex.InitialiseSpareRecord(const ASpareRecord: TRecordBuffer);
4563 begin
4564 FSPareBuffer:=ASpareRecord;
4565 end;
4566
4567 procedure TUniDirectionalBufIndex.ReleaseSpareRecord;
4568 begin
4569 FSPareBuffer:=nil;
4570 end;
4571
TUniDirectionalBufIndex.GetRecNonull4572 function TUniDirectionalBufIndex.GetRecNo: Longint;
4573 begin
4574 Result := -1;
4575 end;
4576
4577 procedure TUniDirectionalBufIndex.SetRecNo(ARecNo: Longint);
4578 begin
4579 DatabaseError(SUniDirectional);
4580 end;
4581
4582 procedure TUniDirectionalBufIndex.BeginUpdate;
4583 begin
4584 // Do nothing
4585 end;
4586
4587 procedure TUniDirectionalBufIndex.AddRecord;
4588 var
4589 h,i: integer;
4590 begin
4591 // Release unneeded blob buffers, in order to save memory
4592 // TDataSet has own buffer of records, so do not release blobs until they can be referenced
4593 with FDataSet do
4594 begin
4595 h := high(FBlobBuffers) - BufferCount*BlobFieldCount;
4596 if h > 10 then //Free in batches, starting with oldest (at beginning)
4597 begin
4598 for i := 0 to h do
4599 FreeBlobBuffer(FBlobBuffers[i]);
4600 FBlobBuffers := Copy(FBlobBuffers, h+1, high(FBlobBuffers)-h);
4601 end;
4602 end;
4603 end;
4604
4605 procedure TUniDirectionalBufIndex.InsertRecordBeforeCurrentRecord(const ARecord: TRecordBuffer);
4606 begin
4607 // Do nothing
4608 end;
4609
4610 procedure TUniDirectionalBufIndex.RemoveRecordFromIndex(const ABookmark: TBufBookmark);
4611 begin
4612 DatabaseError(SUniDirectional);
4613 end;
4614
4615 procedure TUniDirectionalBufIndex.OrderCurrentRecord;
4616 begin
4617 // Do nothing
4618 end;
4619
4620 procedure TUniDirectionalBufIndex.EndUpdate;
4621 begin
4622 // Do nothing
4623 end;
4624
4625
4626 initialization
4627 setlength(RegisteredDatapacketReaders,0);
4628 finalization
4629 setlength(RegisteredDatapacketReaders,0);
4630 end.
4631