2
This file is part of the Free Pascal run time library.
3
Copyright (c) 1999-2006 by Joost van der Sluis, member of the
4
Free Pascal development team
6
BufDataset implementation
8
See the file COPYING.FPC, included in this distribution,
9
for details about the copyright.
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.
15
**********************************************************************}
24
uses Classes,Sysutils,db,bufdataset_parser;
29
TResolverErrorEvent = procedure(Sender: TObject; DataSet: TBufDataset; E: EUpdateError;
30
UpdateKind: TUpdateKind; var Response: TResolverResponse) of object;
34
PBlobBuffer = ^TBlobBuffer;
42
TBufBlobStream = class(TStream)
44
FBlobBuffer : PBlobBuffer;
46
FDataset : TBufDataset;
48
function Read(var Buffer; Count: Longint): Longint; override;
49
function Write(const Buffer; Count: Longint): Longint; override;
50
function Seek(Offset: Longint; Origin: Word): Longint; override;
52
constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
57
PBufRecLinkItem = ^TBufRecLinkItem;
58
TBufRecLinkItem = record
59
prior : PBufRecLinkItem;
60
next : PBufRecLinkItem;
63
PBufBookmark = ^TBufBookmark;
65
BookmarkData : PBufRecLinkItem;
66
BookmarkFlag : TBookmarkFlag;
69
PRecUpdateBuffer = ^TRecUpdateBuffer;
70
TRecUpdateBuffer = record
71
UpdateKind : TUpdateKind;
72
BookmarkData : pointer;
73
OldValuesBuffer : pchar;
76
PBufBlobField = ^TBufBlobField;
77
TBufBlobField = record
78
ConnBlobBuffer : array[0..11] of byte; // It's here where the db-specific data is stored
79
BlobBuffer : PBlobBuffer;
82
TRecordsUpdateBuffer = array of TRecUpdateBuffer;
84
TBufDataset = class(TDBDataSet)
86
FCurrentRecBuf : PBufRecLinkItem;
87
FLastRecBuf : PBufRecLinkItem;
88
FFirstRecBuf : PBufRecLinkItem;
89
FFilterBuffer : pchar;
90
FBRecordCount : integer;
92
FPacketRecords : integer;
93
FRecordSize : Integer;
96
FUpdateBuffer : TRecordsUpdateBuffer;
97
FCurrentUpdateBuffer : integer;
99
FParser : TBufDatasetParser;
101
FFieldBufPositions : array of longint;
103
FAllPacketsFetched : boolean;
104
FOnUpdateError : TResolverErrorEvent;
106
FBlobBuffers : array of PBlobBuffer;
107
FUpdateBlobBuffers: array of PBlobBuffer;
109
function GetCurrentBuffer: PChar;
110
procedure CalcRecordSize;
111
function LoadBuffer(Buffer : PChar): TGetResult;
112
function GetFieldSize(FieldDef : TFieldDef) : longint;
113
function GetRecordUpdateBuffer : boolean;
114
procedure SetPacketRecords(aValue : integer);
115
function IntAllocRecordBuffer: PChar;
116
procedure DoFilterRecord(var Acceptable: Boolean);
117
procedure ParseFilter(const AFilter: string);
119
function GetNewBlobBuffer : PBlobBuffer;
120
function GetNewWriteBlobBuffer : PBlobBuffer;
121
procedure FreeBlobBuffer(var ABlobBuffer: PBlobBuffer);
122
procedure SetRecNo(Value: Longint); override;
123
function GetRecNo: Longint; override;
124
function GetChangeCount: integer; virtual;
125
function AllocRecordBuffer: PChar; override;
126
procedure FreeRecordBuffer(var Buffer: PChar); override;
127
procedure InternalInitRecord(Buffer: PChar); override;
128
function GetCanModify: Boolean; override;
129
function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
130
procedure InternalOpen; override;
131
procedure InternalClose; override;
132
function getnextpacket : integer;
133
function GetRecordSize: Word; override;
134
procedure InternalPost; override;
135
procedure InternalCancel; Override;
136
procedure InternalDelete; override;
137
procedure InternalFirst; override;
138
procedure InternalLast; override;
139
procedure InternalSetToRecord(Buffer: PChar); override;
140
procedure InternalGotoBookmark(ABookmark: Pointer); override;
141
procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
142
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
143
procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
144
function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
145
function IsCursorOpen: Boolean; override;
146
function GetRecordCount: Longint; override;
147
procedure ApplyRecUpdate(UpdateKind : TUpdateKind); virtual;
148
procedure SetOnUpdateError(const aValue: TResolverErrorEvent);
149
procedure SetFilterText(const Value: String); override; {virtual;}
150
procedure SetFiltered(Value: Boolean); override; {virtual;}
151
{abstracts, must be overidden by descendents}
152
function Fetch : boolean; virtual; abstract;
153
function LoadField(FieldDef : TFieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; virtual; abstract;
154
procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField); virtual; abstract;
157
constructor Create(AOwner: TComponent); override;
158
function GetFieldData(Field: TField; Buffer: Pointer;
159
NativeFormat: Boolean): Boolean; override;
160
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
161
procedure SetFieldData(Field: TField; Buffer: Pointer;
162
NativeFormat: Boolean); override;
163
procedure SetFieldData(Field: TField; Buffer: Pointer); override;
164
procedure ApplyUpdates; virtual; overload;
165
procedure ApplyUpdates(MaxErrors: Integer); virtual; overload;
166
procedure CancelUpdates; virtual;
167
destructor Destroy; override;
168
function Locate(const keyfields: string; const keyvalues: Variant; options: TLocateOptions) : boolean; override;
169
function UpdateStatus: TUpdateStatus; override;
170
function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
171
property ChangeCount : Integer read GetChangeCount;
173
property PacketRecords : Integer read FPacketRecords write SetPacketRecords default 10;
174
property OnUpdateError: TResolverErrorEvent read FOnUpdateError write SetOnUpdateError;
179
uses variants, dbconst;
181
{ ---------------------------------------------------------------------
183
---------------------------------------------------------------------}
185
constructor TBufDataset.Create(AOwner : TComponent);
187
Inherited Create(AOwner);
188
SetLength(FUpdateBuffer,0);
189
SetLength(FBlobBuffers,0);
190
SetLength(FUpdateBlobBuffers,0);
191
BookmarkSize := sizeof(TBufBookmark);
193
FPacketRecords := 10;
196
procedure TBufDataset.SetPacketRecords(aValue : integer);
198
if (aValue = -1) or (aValue > 0) then FPacketRecords := aValue
199
else DatabaseError(SInvPacketRecordsValue);
202
destructor TBufDataset.Destroy;
207
Function TBufDataset.GetCanModify: Boolean;
212
function TBufDataset.intAllocRecordBuffer: PChar;
214
// Note: Only the internal buffers of TDataset provide bookmark information
215
result := AllocMem(FRecordsize+sizeof(TBufRecLinkItem));
218
function TBufDataset.AllocRecordBuffer: PChar;
220
result := AllocMem(FRecordsize + sizeof(TBufBookmark) + CalcfieldsSize);
221
// The records are initialised, or else the fields of an empty, just-opened dataset
226
procedure TBufDataset.FreeRecordBuffer(var Buffer: PChar);
228
ReAllocMem(Buffer,0);
231
procedure TBufDataset.InternalOpen;
238
FFirstRecBuf := pointer(IntAllocRecordBuffer);
239
FLastRecBuf := FFirstRecBuf;
240
FCurrentRecBuf := FLastRecBuf;
242
FAllPacketsFetched := False;
246
// parse filter expression
250
// oops, a problem with parsing, clear filter for now
251
on E: Exception do Filter := EmptyStr;
256
procedure TBufDataset.InternalClose;
263
FCurrentRecBuf := FFirstRecBuf;
264
while assigned(FCurrentRecBuf) do
266
pc := pointer(FCurrentRecBuf);
267
FCurrentRecBuf := FCurrentRecBuf^.next;
268
FreeRecordBuffer(pc);
271
if Length(FUpdateBuffer) > 0 then
273
for r := 0 to length(FUpdateBuffer)-1 do with FUpdateBuffer[r] do
275
if assigned(BookmarkData) then
276
FreeRecordBuffer(OldValuesBuffer);
279
SetLength(FUpdateBuffer,0);
281
for r := 0 to High(FBlobBuffers) do
282
FreeBlobBuffer(FBlobBuffers[r]);
283
for r := 0 to High(FUpdateBlobBuffers) do
284
FreeBlobBuffer(FUpdateBlobBuffers[r]);
286
SetLength(FBlobBuffers,0);
287
SetLength(FUpdateBlobBuffers,0);
290
SetLength(FFieldBufPositions,0);
292
if assigned(FParser) then FreeAndNil(FParser);
295
procedure TBufDataset.InternalFirst;
297
// if FCurrentRecBuf = FLastRecBuf then the dataset is just opened and empty
298
// in which case InternalFirst should do nothing (bug 7211)
299
if FCurrentRecBuf <> FLastRecBuf then
300
FCurrentRecBuf := nil;
303
procedure TBufDataset.InternalLast;
306
until (getnextpacket < FPacketRecords) or (FPacketRecords = -1);
307
if FLastRecBuf <> FFirstRecBuf then
308
FCurrentRecBuf := FLastRecBuf;
311
procedure unSetFieldIsNull(NullMask : pbyte;x : longint); //inline;
313
NullMask[x div 8] := (NullMask[x div 8]) and not (1 shl (x mod 8));
316
procedure SetFieldIsNull(NullMask : pbyte;x : longint); //inline;
318
NullMask[x div 8] := (NullMask[x div 8]) or (1 shl (x mod 8));
321
function GetFieldIsNull(NullMask : pbyte;x : longint) : boolean; //inline;
323
result := ord(NullMask[x div 8]) and (1 shl (x mod 8)) > 0
326
function TBufDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
328
var Acceptable : Boolean;
329
SaveState: TDataSetState;
338
if not assigned(PBufRecLinkItem(FCurrentRecBuf)^.prior) then
344
FCurrentRecBuf := PBufRecLinkItem(FCurrentRecBuf)^.prior;
347
if FCurrentRecBuf = FLastRecBuf then
350
if FCurrentRecBuf = FLastRecBuf then // Dataset is empty (just opened)
352
if getnextpacket = 0 then result := grEOF;
354
else if FCurrentRecBuf = nil then FCurrentRecBuf := FFirstRecBuf
355
else if (PBufRecLinkItem(FCurrentRecBuf)^.next = FLastRecBuf) then
357
if getnextpacket > 0 then
359
FCurrentRecBuf := PBufRecLinkItem(FCurrentRecBuf)^.next;
368
FCurrentRecBuf := PBufRecLinkItem(FCurrentRecBuf)^.next;
372
if Result = grOK then
375
with PBufBookmark(Buffer + FRecordSize)^ do
377
BookmarkData := FCurrentRecBuf;
378
BookmarkFlag := bfCurrent;
380
move((pointer(FCurrentRecBuf)+sizeof(TBufRecLinkItem))^,buffer^,FRecordSize);
381
GetCalcFields(Buffer);
385
FFilterBuffer := Buffer;
386
SaveState := SetTempState(dsFilter);
387
DoFilterRecord(Acceptable);
388
if (GetMode = gmCurrent) and not Acceptable then
393
RestoreState(SaveState);
396
else if (Result = grError) and doCheck then
397
DatabaseError('No record');
401
function TBufDataset.GetRecordUpdateBuffer : boolean;
407
GetBookmarkData(ActiveBuffer,@CurrBuff);
408
if (FCurrentUpdateBuffer >= length(FUpdateBuffer)) or (FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData <> CurrBuff) then
409
for x := 0 to high(FUpdateBuffer) do
410
if FUpdateBuffer[x].BookmarkData = CurrBuff then
412
FCurrentUpdateBuffer := x;
415
Result := (FCurrentUpdateBuffer < length(FUpdateBuffer)) and (FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData = CurrBuff);
418
procedure TBufDataset.InternalSetToRecord(Buffer: PChar);
420
FCurrentRecBuf := PBufBookmark(Buffer + FRecordSize)^.BookmarkData;
423
procedure TBufDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
425
PBufBookmark(Buffer + FRecordSize)^.BookmarkData := pointer(Data^);
428
procedure TBufDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
430
PBufBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
433
procedure TBufDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
435
pointer(Data^) := PBufBookmark(Buffer + FRecordSize)^.BookmarkData;
438
function TBufDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
440
Result := PBufBookmark(Buffer + FRecordSize)^.BookmarkFlag;
443
procedure TBufDataset.InternalGotoBookmark(ABookmark: Pointer);
445
// note that ABookMark should be a PBufBookmark. But this way it can also be
446
// a pointer to a TBufRecLinkItem
447
FCurrentRecBuf := pointer(ABookmark^);
450
function TBufDataset.getnextpacket : integer;
456
if FAllPacketsFetched then
462
pb := pchar(pointer(FLastRecBuf)+sizeof(TBufRecLinkItem));
463
while ((i < FPacketRecords) or (FPacketRecords = -1)) and (loadbuffer(pb) = grOk) do
465
FLastRecBuf^.next := pointer(IntAllocRecordBuffer);
466
FLastRecBuf^.next^.prior := FLastRecBuf;
467
FLastRecBuf := FLastRecBuf^.next;
468
pb := pchar(pointer(FLastRecBuf)+sizeof(TBufRecLinkItem));
471
FBRecordCount := FBRecordCount + i;
475
function TBufDataset.GetFieldSize(FieldDef : TFieldDef) : longint;
478
case FieldDef.DataType of
481
ftFixedChar: result := FieldDef.Size + 1;
483
ftWideString:result := (FieldDef.Size + 1)*2;
486
ftword : result := sizeof(longint);
487
ftBoolean : result := sizeof(wordbool);
488
ftBCD : result := sizeof(currency);
489
ftFloat : result := sizeof(double);
490
ftLargeInt : result := sizeof(largeint);
493
ftDateTime : result := sizeof(TDateTime);
503
ftWideMemo : result := sizeof(TBufBlobField)
509
function TBufDataset.LoadBuffer(Buffer : PChar): TGetResult;
511
var NullMask : pbyte;
513
CreateblobField : boolean;
514
BufBlob : PBufBlobField;
520
FAllPacketsFetched := True;
524
NullMask := pointer(buffer);
525
fillchar(Nullmask^,FNullmaskSize,0);
526
inc(buffer,FNullmaskSize);
528
for x := 0 to FieldDefs.count-1 do
530
if not LoadField(FieldDefs[x],buffer,CreateblobField) then
531
SetFieldIsNull(NullMask,x)
532
else if CreateblobField then
534
BufBlob := PBufBlobField(Buffer);
535
BufBlob^.BlobBuffer := GetNewBlobBuffer;
536
LoadBlobIntoBuffer(FieldDefs[x],BufBlob);
538
inc(buffer,GetFieldSize(FieldDefs[x]));
543
function TBufDataset.GetCurrentBuffer: PChar;
545
if State = dsFilter then Result := FFilterBuffer
546
else if state = dsCalcFields then Result := CalcBuffer
547
else Result := ActiveBuffer;
551
function TBufDataset.GetFieldData(Field: TField; Buffer: Pointer;
552
NativeFormat: Boolean): Boolean;
554
Result := GetFieldData(Field, Buffer);
557
function TBufDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
559
var CurrBuff : pchar;
563
if state = dsOldValue then
565
if not GetRecordUpdateBuffer then
567
// There is no old value available
571
currbuff := FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer+sizeof(TBufRecLinkItem);
575
CurrBuff := GetCurrentBuffer;
576
if not assigned(CurrBuff) then
583
If Field.Fieldno > 0 then // If = 0, then calculated field or something similar
585
if GetFieldIsnull(pbyte(CurrBuff),Field.Fieldno-1) then
590
if assigned(buffer) then
592
inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]);
593
Move(CurrBuff^, Buffer^, GetFieldSize(FieldDefs[Field.FieldNo-1]));
599
Inc(CurrBuff, GetRecordSize + Field.Offset);
600
Result := Boolean(CurrBuff^);
601
if result and assigned(Buffer) then
604
Move(CurrBuff^, Buffer^, Field.Datasize);
609
procedure TBufDataset.SetFieldData(Field: TField; Buffer: Pointer;
610
NativeFormat: Boolean);
612
SetFieldData(Field,Buffer);
615
procedure TBufDataset.SetFieldData(Field: TField; Buffer: Pointer);
617
var CurrBuff : pointer;
621
if not (state in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then
623
DatabaseErrorFmt(SNotEditing,[Name],self);
626
if state = dsFilter then // Set the value into the 'temporary' FLastRecBuf buffer for Locate and Lookup
627
CurrBuff := pointer(FLastRecBuf) + sizeof(TBufRecLinkItem)
629
CurrBuff := GetCurrentBuffer;
630
If Field.Fieldno > 0 then // If = 0, then calculated field or something
632
NullMask := CurrBuff;
634
inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]);
635
if assigned(buffer) then
637
Move(Buffer^, CurrBuff^, GetFieldSize(FieldDefs[Field.FieldNo-1]));
638
unSetFieldIsNull(NullMask,Field.FieldNo-1);
641
SetFieldIsNull(NullMask,Field.FieldNo-1);
645
Inc(CurrBuff, GetRecordSize + Field.Offset);
646
Boolean(CurrBuff^) := Buffer <> nil;
648
if assigned(Buffer) then
649
Move(Buffer^, CurrBuff^, Field.Datasize);
651
if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
652
DataEvent(deFieldChange, Ptrint(Field));
655
procedure TBufDataset.InternalDelete;
658
GetBookmarkData(ActiveBuffer,@FCurrentRecBuf);
660
if FCurrentRecBuf <> FFirstRecBuf then FCurrentRecBuf^.prior^.next := FCurrentRecBuf^.next
661
else FFirstRecBuf := FCurrentRecBuf^.next;
663
FCurrentRecBuf^.next^.prior := FCurrentRecBuf^.prior;
665
if not GetRecordUpdateBuffer then
667
FCurrentUpdateBuffer := length(FUpdateBuffer);
668
SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
670
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := pchar(FCurrentRecBuf);
671
FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := FCurrentRecBuf;
673
FCurrentRecBuf := FCurrentRecBuf^.next;
677
if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind = ukModify then
679
FCurrentRecBuf := FCurrentRecBuf^.next;
680
FreeRecordBuffer(pchar(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData));
681
FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer;
685
FCurrentRecBuf := FCurrentRecBuf^.next;
686
FreeRecordBuffer(pchar(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData));
687
FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := nil; //this 'disables' the updatebuffer
692
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukDelete;
696
procedure TBufDataset.ApplyRecUpdate(UpdateKind : TUpdateKind);
699
raise EDatabaseError.Create(SApplyRecNotSupported);
702
procedure TBufDataset.CancelUpdates;
709
if Length(FUpdateBuffer) > 0 then
711
r := Length(FUpdateBuffer) -1;
712
while r > -1 do with FUpdateBuffer[r] do
714
if assigned(FUpdateBuffer[r].BookmarkData) then
716
if UpdateKind = ukModify then
718
move(pchar(OldValuesBuffer+sizeof(TBufRecLinkItem))^,pchar(BookmarkData+sizeof(TBufRecLinkItem))^,FRecordSize);
719
FreeRecordBuffer(OldValuesBuffer);
721
else if UpdateKind = ukDelete then
723
if assigned(PBufRecLinkItem(BookmarkData)^.prior) then // or else it was the first record
724
PBufRecLinkItem(BookmarkData)^.prior^.next := BookmarkData
726
FFirstRecBuf := BookmarkData;
727
PBufRecLinkItem(BookmarkData)^.next^.prior := BookmarkData;
730
else if UpdateKind = ukInsert then
732
if assigned(PBufRecLinkItem(BookmarkData)^.prior) then // or else it was the first record
733
PBufRecLinkItem(BookmarkData)^.prior^.next := PBufRecLinkItem(BookmarkData)^.next
735
FFirstRecBuf := PBufRecLinkItem(BookmarkData)^.next;
736
PBufRecLinkItem(BookmarkData)^.next^.prior := PBufRecLinkItem(BookmarkData)^.prior;
737
// resync won't work if the currentbuffer is freed...
738
if FCurrentRecBuf = BookmarkData then FCurrentRecBuf := FCurrentRecBuf^.next;
739
FreeRecordBuffer(BookmarkData);
746
SetLength(FUpdateBuffer,0);
751
procedure TBufDataset.SetOnUpdateError(const AValue: TResolverErrorEvent);
754
FOnUpdateError := AValue;
757
procedure TBufDataset.ApplyUpdates; // For backwards-compatibility
763
procedure TBufDataset.ApplyUpdates(MaxErrors: Integer);
766
FailedCount : integer;
767
Response : TResolverResponse;
768
StoreRecBuf : PBufRecLinkItem;
769
AUpdateErr : EUpdateError;
774
StoreRecBuf := FCurrentRecBuf;
780
while (r < Length(FUpdateBuffer)) and (Response <> rrAbort) do
782
if assigned(FUpdateBuffer[r].BookmarkData) then
784
InternalGotoBookmark(@FUpdateBuffer[r].BookmarkData);
785
Resync([rmExact,rmCenter]);
788
ApplyRecUpdate(FUpdateBuffer[r].UpdateKind);
790
on E: EDatabaseError do
793
if failedcount > word(MaxErrors) then Response := rrAbort
794
else Response := rrSkip;
795
if assigned(FOnUpdateError) then
797
AUpdateErr := EUpdateError.Create(SOnUpdateError,E.Message,0,0,Exception(AcquireExceptionObject));
798
FOnUpdateError(Self,Self,AUpdateErr,FUpdateBuffer[r].UpdateKind,Response);
800
if Response in [rrApply, rrIgnore] then dec(FailedCount);
801
if Response = rrApply then dec(r);
803
else if Response = rrAbort then
804
Raise EUpdateError.Create(SOnUpdateError,E.Message,0,0,Exception(AcquireExceptionObject));
809
if response in [rrApply, rrIgnore] then
811
FreeRecordBuffer(FUpdateBuffer[r].OldValuesBuffer);
812
FUpdateBuffer[r].BookmarkData := nil;
818
if failedcount = 0 then
820
SetLength(FUpdateBuffer,0);
822
if assigned(FUpdateBlobBuffers) then for r:=0 to length(FUpdateBlobBuffers)-1 do
823
if assigned(FUpdateBlobBuffers[r]) then
825
if FUpdateBlobBuffers[r]^.OrgBufID >= 0 then
827
Freemem(FBlobBuffers[FUpdateBlobBuffers[r]^.OrgBufID]^.Buffer);
828
Dispose(FBlobBuffers[FUpdateBlobBuffers[r]^.OrgBufID]);
829
FBlobBuffers[FUpdateBlobBuffers[r]^.OrgBufID] :=FUpdateBlobBuffers[r];
833
setlength(FBlobBuffers,length(FBlobBuffers)+1);
834
FUpdateBlobBuffers[r]^.OrgBufID := high(FBlobBuffers);
835
FBlobBuffers[high(FBlobBuffers)] := FUpdateBlobBuffers[r];
839
SetLength(FUpdateBlobBuffers,0);
842
FCurrentRecBuf := StoreRecBuf;
848
procedure TBufDataset.InternalCancel;
853
if assigned(FUpdateBlobBuffers) then for i:=0 to length(FUpdateBlobBuffers)-1 do
854
if assigned(FUpdateBlobBuffers[i]) and (FUpdateBlobBuffers[i]^.FieldNo>0) then
856
Reallocmem(FUpdateBlobBuffers[i]^.Buffer,0);
857
Dispose(FUpdateBlobBuffers[i]);
858
FUpdateBlobBuffers[i] := nil;
862
procedure TBufDataset.InternalPost;
864
Var tmpRecBuffer : PBufRecLinkItem;
867
blobbuf : tbufblobfield;
871
inherited InternalPost;
872
if assigned(FUpdateBlobBuffers) then for i:=0 to length(FUpdateBlobBuffers)-1 do
873
if assigned(FUpdateBlobBuffers[i]) and (FUpdateBlobBuffers[i]^.FieldNo>0) then
875
blobbuf.BlobBuffer := FUpdateBlobBuffers[i];
876
CurrBuff := ActiveBuffer;
877
NullMask := pbyte(CurrBuff);
879
inc(CurrBuff,FFieldBufPositions[FUpdateBlobBuffers[i]^.FieldNo-1]);
880
Move(blobbuf, CurrBuff^, GetFieldSize(FieldDefs[FUpdateBlobBuffers[i]^.FieldNo-1]));
881
unSetFieldIsNull(NullMask,FUpdateBlobBuffers[i]^.FieldNo-1);
883
FUpdateBlobBuffers[i]^.FieldNo := -1;
886
if state = dsInsert then
888
if GetBookmarkFlag(ActiveBuffer) = bfEOF then
890
FCurrentRecBuf := FLastRecBuf
892
// The active buffer is the newly created TDataset record,
893
// from which the bookmark is set to the record where the new record should be
895
GetBookmarkData(ActiveBuffer,@FCurrentRecBuf);
897
// Create the new record buffer
898
tmpRecBuffer := FCurrentRecBuf^.prior;
900
FCurrentRecBuf^.prior := pointer(IntAllocRecordBuffer);
901
FCurrentRecBuf^.prior^.next := FCurrentRecBuf;
902
FCurrentRecBuf := FCurrentRecBuf^.prior;
903
If assigned(tmpRecBuffer) then // if not, it's the first record
905
FCurrentRecBuf^.prior := tmpRecBuffer;
906
tmpRecBuffer^.next := FCurrentRecBuf
909
FFirstRecBuf := FCurrentRecBuf;
911
// Link the newly created record buffer to the newly created TDataset record
912
with PBufBookmark(ActiveBuffer + FRecordSize)^ do
914
BookmarkData := FCurrentRecBuf;
915
BookmarkFlag := bfInserted;
921
GetBookmarkData(ActiveBuffer,@FCurrentRecBuf);
923
if not GetRecordUpdateBuffer then
925
FCurrentUpdateBuffer := length(FUpdateBuffer);
926
SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
928
FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := FCurrentRecBuf;
930
if state = dsEdit then
932
// Update the oldvalues-buffer
933
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := intAllocRecordBuffer;
934
move(FCurrentRecBuf^,FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize+sizeof(TBufRecLinkItem));
935
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukModify;
938
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukInsert;
941
CurrBuff := pchar(FCurrentRecBuf);
942
inc(Currbuff,sizeof(TBufRecLinkItem));
943
move(ActiveBuffer^,CurrBuff^,FRecordSize);
946
procedure TBufDataset.CalcRecordSize;
951
FNullmaskSize := 1+((FieldDefs.count-1) div 8);
952
FRecordSize := FNullmaskSize;
953
SetLength(FFieldBufPositions,FieldDefs.count);
954
for x := 0 to FieldDefs.count-1 do
956
FFieldBufPositions[x] := FRecordSize;
957
inc(FRecordSize, GetFieldSize(FieldDefs[x]));
961
function TBufDataset.GetRecordSize : Word;
964
result := FRecordSize + sizeof(TBufBookmark);
967
function TBufDataset.GetChangeCount: integer;
970
result := length(FUpdateBuffer);
974
procedure TBufDataset.InternalInitRecord(Buffer: PChar);
977
FillChar(Buffer^, FRecordSize, #0);
979
fillchar(Buffer^,FNullmaskSize,255);
982
procedure TBufDataset.SetRecNo(Value: Longint);
985
TmpRecBuffer : PBufRecLinkItem;
989
if value > RecordCount then
991
repeat until (getnextpacket < FPacketRecords) or (value <= RecordCount) or (FPacketRecords = -1);
992
if value > RecordCount then
994
DatabaseError(SNoSuchRecord,self);
998
TmpRecBuffer := FFirstRecBuf;
999
for recnr := 1 to value-1 do
1000
TmpRecBuffer := TmpRecBuffer^.next;
1001
GotoBookmark(@TmpRecBuffer);
1004
function TBufDataset.GetRecNo: Longint;
1006
Var SearchRecBuffer : PBufRecLinkItem;
1007
TmpRecBuffer : PBufRecLinkItem;
1012
abuf := GetCurrentBuffer;
1013
// If abuf isn't assigned, the recordset probably isn't opened.
1014
if assigned(abuf) and (FBRecordCount>0) and (state <> dsInsert) then
1016
GetBookmarkData(abuf,@SearchRecBuffer);
1017
TmpRecBuffer := FFirstRecBuf;
1019
while TmpRecBuffer <> SearchRecBuffer do
1022
TmpRecBuffer := TmpRecBuffer^.next;
1029
function TBufDataset.IsCursorOpen: Boolean;
1035
Function TBufDataset.GetRecordCount: Longint;
1038
Result := FBRecordCount;
1041
Function TBufDataSet.UpdateStatus: TUpdateStatus;
1044
Result:=usUnmodified;
1045
if GetRecordUpdateBuffer then
1046
case FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind of
1047
ukModify : Result := usModified;
1048
ukInsert : Result := usInserted;
1049
ukDelete : Result := usDeleted;
1053
function TbufDataset.GetNewBlobBuffer : PBlobBuffer;
1055
var ABlobBuffer : PBlobBuffer;
1058
setlength(FBlobBuffers,length(FBlobBuffers)+1);
1060
fillbyte(ABlobBuffer^,sizeof(ABlobBuffer^),0);
1061
ABlobBuffer^.OrgBufID := high(FUpdateBlobBuffers);
1062
FBlobBuffers[high(FBlobBuffers)] := ABlobBuffer;
1063
result := ABlobBuffer;
1066
function TbufDataset.GetNewWriteBlobBuffer : PBlobBuffer;
1068
var ABlobBuffer : PBlobBuffer;
1071
setlength(FUpdateBlobBuffers,length(FUpdateBlobBuffers)+1);
1073
fillbyte(ABlobBuffer^,sizeof(ABlobBuffer^),0);
1074
FUpdateBlobBuffers[high(FUpdateBlobBuffers)] := ABlobBuffer;
1075
result := ABlobBuffer;
1078
procedure TBufDataset.FreeBlobBuffer(var ABlobBuffer: PBlobBuffer);
1081
if not Assigned(ABlobBuffer) then Exit;
1082
FreeMem(ABlobBuffer^.Buffer, ABlobBuffer^.Size);
1083
Dispose(ABlobBuffer);
1087
function TBufBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
1091
soFromBeginning : FPosition:=Offset;
1092
soFromEnd : FPosition:=FBlobBuffer^.Size+Offset;
1093
soFromCurrent : FpoSition:=FPosition+Offset;
1099
function TBufBlobStream.Read(var Buffer; Count: Longint): Longint;
1104
if FPosition + count > FBlobBuffer^.Size then
1105
count := FBlobBuffer^.Size-FPosition;
1106
ptr := FBlobBuffer^.Buffer+FPosition;
1107
move(ptr^,buffer,count);
1108
inc(FPosition,count);
1112
function TBufBlobStream.Write(const Buffer; Count: Longint): Longint;
1117
ReAllocMem(FBlobBuffer^.Buffer,FPosition+Count);
1118
ptr := FBlobBuffer^.Buffer+FPosition;
1119
move(buffer,ptr^,count);
1120
inc(FBlobBuffer^.Size,count);
1121
inc(FPosition,count);
1125
constructor TBufBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
1127
var bufblob : TBufBlobField;
1130
FDataset := Field.DataSet as TBufDataset;
1131
if mode = bmread then
1133
if not field.getData(@bufblob) then
1134
DatabaseError(SFieldIsNull);
1135
if not assigned(bufblob.BlobBuffer) then with FDataSet do
1137
FBlobBuffer := GetNewBlobBuffer;
1138
bufblob.BlobBuffer := FBlobBuffer;
1139
LoadBlobIntoBuffer(FieldDefs[field.FieldNo-1],@bufblob);
1142
FBlobBuffer := bufblob.BlobBuffer;
1144
else if mode=bmWrite then with FDataSet as TBufDataset do
1146
FBlobBuffer := GetNewWriteBlobBuffer;
1147
FBlobBuffer^.FieldNo := Field.FieldNo;
1148
if (field.getData(@bufblob)) and assigned(bufblob.BlobBuffer) then
1149
FBlobBuffer^.OrgBufID := bufblob.BlobBuffer^.OrgBufID
1151
FBlobBuffer^.OrgBufID := -1;
1155
function TBufDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
1157
var bufblob : TBufBlobField;
1163
if not field.getData(@bufblob) then
1166
result := TBufBlobStream.Create(Field as tblobfield,bmread);
1168
else if mode=bmWrite then
1170
if not (state in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then
1172
DatabaseErrorFmt(SNotEditing,[Name],self);
1176
result := TBufBlobStream.Create(Field as tblobfield,bmWrite);
1178
if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
1179
DataEvent(deFieldChange, Ptrint(Field));
1183
procedure TBufDataset.DoFilterRecord(var Acceptable: Boolean);
1186
// check user filter
1187
if Assigned(OnFilterRecord) then
1188
OnFilterRecord(Self, Acceptable);
1191
if Acceptable and (Length(Filter) > 0) then
1192
Acceptable := Boolean((FParser.ExtractFromBuffer(GetCurrentBuffer))^);
1196
procedure TBufDataset.SetFilterText(const Value: String);
1198
if Value = Filter then
1204
// call dataset method
1207
// refilter dataset if filtered
1208
if IsCursorOpen and Filtered then Refresh;
1211
procedure TBufDataset.SetFiltered(Value: Boolean); {override;}
1213
if Value = Filtered then
1216
// pass on to ancestor
1219
// only refresh if active
1220
if IsCursorOpen then
1224
procedure TBufDataset.ParseFilter(const AFilter: string);
1227
if Length(AFilter) > 0 then
1229
if (FParser = nil) and IsCursorOpen then
1231
FParser := TBufDatasetParser.Create(Self);
1233
// have a parser now?
1234
if FParser <> nil then
1237
FParser.PartialMatch := not (foNoPartialCompare in FilterOptions);
1238
FParser.CaseInsensitive := foCaseInsensitive in FilterOptions;
1240
FParser.ParseExpression(AFilter);
1246
Function TBufDataset.Locate(const KeyFields: string; const KeyValues: Variant; options: TLocateOptions) : boolean;
1249
function CompareText0(substr, astr: pchar; len : integer; options: TLocateOptions): integer;
1252
i : integer; Chr1, Chr2: byte;
1257
while (result=0) and (i<len) and (chr1 <> 0) do
1259
Chr1 := byte(substr[i]);
1260
Chr2 := byte(astr[i]);
1262
if loCaseInsensitive in options then
1264
if Chr1 in [97..122] then
1266
if Chr2 in [97..122] then
1269
result := Chr1 - Chr2;
1271
if (result <> 0) and (chr1 = 0) and (loPartialKey in options) then result := 0;
1275
var keyfield : TField; // Field to search in
1276
ValueBuffer : pchar; // Pointer to value to search for, in TField' internal format
1279
FieldBufPos : PtrInt; // Amount to add to the record buffer to get the FieldBuffer
1280
CurrLinkItem: PBufRecLinkItem;
1284
CheckNull : Boolean;
1285
SaveState : TDataSetState;
1288
// For now it is only possible to search in one field at the same time
1291
if IsEmpty then exit;
1293
keyfield := FieldByName(keyfields);
1294
CheckNull := VarIsNull(KeyValues);
1296
if not CheckNull then
1299
SetTempState(dsFilter);
1300
keyfield.Value := KeyValues;
1301
RestoreState(SaveState);
1303
FieldBufPos := FFieldBufPositions[keyfield.FieldNo-1];
1304
VBLength := keyfield.DataSize;
1305
ValueBuffer := AllocMem(VBLength);
1306
currbuff := pointer(FLastRecBuf)+sizeof(TBufRecLinkItem)+FieldBufPos;
1307
move(currbuff^,ValueBuffer^,VBLength);
1310
CurrLinkItem := FFirstRecBuf;
1315
currbuff := pointer(CurrLinkItem)+sizeof(TBufRecLinkItem);
1316
if GetFieldIsnull(pbyte(CurrBuff),keyfield.Fieldno-1) then
1321
CurrLinkItem := CurrLinkItem^.next;
1322
if CurrLinkItem = FLastRecBuf then getnextpacket;
1323
until CurrLinkItem = FLastRecBuf;
1325
else if keyfield.DataType = ftString then
1328
currbuff := pointer(CurrLinkItem)+sizeof(TBufRecLinkItem);
1329
if not GetFieldIsnull(pbyte(CurrBuff),keyfield.Fieldno-1) then
1331
inc(CurrBuff,FieldBufPos);
1332
if CompareText0(ValueBuffer,CurrBuff,VBLength,options) = 0 then
1338
CurrLinkItem := CurrLinkItem^.next;
1339
if CurrLinkItem = FLastRecBuf then getnextpacket;
1340
until CurrLinkItem = FLastRecBuf;
1345
currbuff := pointer(CurrLinkItem)+sizeof(TBufRecLinkItem);
1346
if not GetFieldIsnull(pbyte(CurrBuff),keyfield.Fieldno-1) then
1348
inc(CurrBuff,FieldBufPos);
1349
if CompareByte(ValueBuffer^,CurrBuff^,VBLength) = 0 then
1356
CurrLinkItem := CurrLinkItem^.next;
1357
if CurrLinkItem = FLastRecBuf then getnextpacket;
1358
until CurrLinkItem = FLastRecBuf;
1364
bm.BookmarkData := CurrLinkItem;
1365
bm.BookmarkFlag := bfCurrent;
1369
ReAllocmem(ValueBuffer,0);