* New algorith to store update-buffers to file

* RowStateToByte and ByteToRowState
 * Start at the begin of a stream, after the automatic recognition of the stream has been used
 * Implemented TBufDataset.CompareBookmarks
 * Some update-buffer fixes
 * Fixed some warnings

git-svn-id: trunk@12155 -
This commit is contained in:
joost 2008-11-17 22:05:00 +00:00
parent 04d8ad3f17
commit d1beccce83

View File

@ -76,6 +76,10 @@ type
- If UpdateKind is ukDelete it contains a bookmark to the record just after the deleted record
}
BookmarkData : TBufBookmark;
{ DelBookMarkData:
- If UpdateKind is ukDelete it contains a bookmark to the deleted record, before it got deleted
}
DelBookmarkData : TBufBookmark;
{ OldValuesBuffer:
- If UpdateKind is ukModify it contains a record-buffer which contains the old data
- If UpdateKind is ukDelete it contains a record-buffer with the data of the deleted record
@ -105,7 +109,6 @@ type
TBufIndex = class(TObject)
private
FDataset : TBufDataset;
protected
function GetBookmarkSize: integer; virtual; abstract;
function GetCurrentBuffer: Pointer; virtual; abstract;
@ -306,22 +309,25 @@ type
TDatapacketReaderClass = class of TDatapacketReader;
TDataPacketReader = class(TObject)
FStream : TStream;
protected
class function RowStateToByte(const ARowState : TRowState) : byte;
class function ByteToRowState(const AByte : Byte) : TRowState;
public
constructor create(AStream : TStream); virtual;
procedure LoadFieldDefs(AFieldDefs : TFieldDefs); virtual; abstract;
procedure StoreFieldDefs(AFieldDefs : TFieldDefs); virtual; abstract;
procedure GetRecordUpdState(var AIsUpdate,AAddRecordBuffer,AIsFirstEntry : boolean); virtual; abstract;
procedure EndStoreRecord(const AChangeLog : TChangeLogEntryArr); virtual; abstract;
function GetRecordRowState(out AUpdOrder : Integer) : TRowState; virtual; abstract;
procedure EndStoreRecord; virtual; abstract;
function GetCurrentRecord : boolean; virtual; abstract;
procedure GotoNextRecord; virtual; abstract;
function GetCurrentElement : pointer; virtual; abstract;
procedure GotoElement(const AnElement : pointer); virtual; abstract;
procedure RestoreRecord(ADataset : TBufDataset); virtual; abstract;
procedure StoreRecord(ADataset : TBufDataset; RowState : TRowState); virtual; abstract;
procedure InitLoadRecords(var AChangeLog : TChangeLogEntryArr); virtual; abstract;
property Stream: TStream read FStream;
procedure StoreRecord(ADataset : TBufDataset; ARowState : TRowState; AUpdOrder : integer = 0); virtual; abstract;
procedure InitLoadRecords; virtual; abstract;
class function RecognizeStream(AStream : TStream) : boolean; virtual; abstract;
property Stream: TStream read FStream;
end;
{ TFpcBinaryDatapacketReader }
@ -330,16 +336,15 @@ type
public
procedure LoadFieldDefs(AFieldDefs : TFieldDefs); override;
procedure StoreFieldDefs(AFieldDefs : TFieldDefs); override;
procedure GetRecordUpdState(var AIsUpdate, AAddRecordBuffer,
AIsFirstEntry: boolean); override;
procedure EndStoreRecord(const AChangeLog : TChangeLogEntryArr); override;
function GetRecordRowState(out AUpdOrder : Integer) : TRowState; override;
procedure EndStoreRecord; override;
function GetCurrentRecord : boolean; override;
procedure GotoNextRecord; override;
procedure GotoElement(const AnElement : pointer); override;
procedure InitLoadRecords(var AChangeLog : TChangeLogEntryArr); override;
procedure InitLoadRecords; override;
function GetCurrentElement: pointer; override;
procedure RestoreRecord(ADataset : TBufDataset); override;
procedure StoreRecord(ADataset : TBufDataset; RowState : TRowState); override;
procedure StoreRecord(ADataset : TBufDataset; ARowState : TRowState; AUpdOrder : integer = 0); override;
class function RecognizeStream(AStream : TStream) : boolean; override;
end;
@ -385,7 +390,7 @@ type
function GetIndexName: String;
function LoadBuffer(Buffer : PChar): TGetResult;
function GetFieldSize(FieldDef : TFieldDef) : longint;
function GetRecordUpdateBuffer(const ABookmark : TBufBookmark) : boolean;
function GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludeDeleted : boolean = false; AFindNext : boolean = false) : boolean;
function GetActiveRecordUpdateBuffer : boolean;
procedure ProcessFieldCompareStruct(AField: TField; var ACompareRec : TDBCompareRec);
procedure SetIndexFieldNames(const AValue: String);
@ -395,7 +400,7 @@ type
function IntAllocRecordBuffer: PChar;
procedure DoFilterRecord(var Acceptable: Boolean);
procedure ParseFilter(const AFilter: string);
procedure IntLoadFielddefsFromFile(const FileName: string);
procedure IntLoadFielddefsFromFile;
procedure IntLoadRecordsFromFile;
protected
procedure UpdateIndexDefs; override;
@ -466,6 +471,7 @@ type
procedure LoadFromFile(AFileName: string = ''; Format: TDataPacketFormat = dfAny);
procedure SaveToFile(AFileName: string = ''; Format: TDataPacketFormat = dfBinary);
procedure CreateDataset;
function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint; override;
property ChangeCount : Integer read GetChangeCount;
property MaxIndexesCount : Integer read FMaxIndexesCount write SetMaxIndexesCount;
@ -913,7 +919,7 @@ begin
FFileStream := TFileStream.Create(FileName,fmOpenRead);
FDatasetReader := TFpcBinaryDatapacketReader.Create(FFileStream);
end;
if assigned(FDatasetReader) then IntLoadFielddefsFromFile(FFileName);
if assigned(FDatasetReader) then IntLoadFielddefsFromFile;
CalcRecordSize;
FBRecordcount := 0;
@ -1516,21 +1522,30 @@ begin
{$ENDIF}
end;
function TBufDataset.GetRecordUpdateBuffer(const ABookmark: TBufBookmark): boolean;
function TBufDataset.GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludeDeleted : boolean = false; AFindNext : boolean = false): boolean;
var x : integer;
var x : integer;
StartBuf : integer;
begin
if AFindNext then
begin
inc(FCurrentUpdateBuffer);
StartBuf:=FCurrentUpdateBuffer;
end
else
StartBuf := 0;
if (FCurrentUpdateBuffer >= length(FUpdateBuffer)) or not FCurrentIndex.CompareBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData,@ABookmark) then
for x := 0 to high(FUpdateBuffer) do
for x := StartBuf to high(FUpdateBuffer) do
if FCurrentIndex.CompareBookmarks(@FUpdateBuffer[x].BookmarkData,@ABookmark) and
(FUpdateBuffer[x].UpdateKind<>ukDelete) then // The Bookmarkdata of a deleted record does not contain the deleted record, but the record thereafter
((FUpdateBuffer[x].UpdateKind<>ukDelete) or IncludeDeleted) then // The Bookmarkdata of a deleted record does not contain the deleted record, but the record thereafter
begin
FCurrentUpdateBuffer := x;
break;
end;
Result := (FCurrentUpdateBuffer < length(FUpdateBuffer)) and
(FCurrentIndex.CompareBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData,@ABookmark));
(FCurrentIndex.CompareBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData,@ABookmark)) and
((FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind<>ukDelete) or IncludeDeleted) ;
end;
function TBufDataset.LoadBuffer(Buffer : PChar): TGetResult;
@ -1711,7 +1726,8 @@ begin
// may arise. The 'delete' is placed in the update-buffer before the actual delete
// took place. This can lead into troubles, because other updates can depend on
// the record still being available.
if not GetActiveRecordUpdateBuffer or (FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind = ukModify) then
if not GetActiveRecordUpdateBuffer or
(FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind = ukModify) then
begin
FCurrentUpdateBuffer := length(FUpdateBuffer);
SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
@ -1719,15 +1735,15 @@ begin
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := IntAllocRecordBuffer;
move(RemRec^, FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
FreeRecordBuffer(RemRecBuf);
FCurrentIndex.StoreCurrentRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
end
else //with FIndexes[0] do
begin
FreeRecordBuffer(RemRecBuf);
FCurrentIndex.StoreCurrentRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind <> ukModify then
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := nil; //this 'disables' the updatebuffer
end;
FCurrentIndex.StoreCurrentRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
FUpdateBuffer[FCurrentUpdateBuffer].DelBookmarkData := RemRecBookmrk;
dec(FBRecordCount);
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukDelete;
@ -2321,27 +2337,22 @@ begin
end;
procedure TBufDataset.GetDatasetPacket(AWriter: TDataPacketReader);
var i : integer;
ScrollResult : TGetResult;
var ScrollResult : TGetResult;
StoreDSState : TDataSetState;
ABookMark : PBufBookmark;
ATBookmark : TBufBookmark;
ChangeLog : array of TChangeLogEntry;
var RowState : TRowState;
RecUpdBuf: integer;
EntryNr : integer;
ChangeLogStr : String;
RowState : TRowState;
EntryNr : integer;
StoreUpdBuf : integer;
RunNr : integer;
begin
FDatasetReader := AWriter;
try
// CheckActive;
//CheckActive;
ABookMark:=@ATBookmark;
FDatasetReader.StoreFieldDefs(FieldDefs);
SetLength(ChangeLog,length(FUpdateBuffer));
EntryNr:=1;
StoreDSState:=State;
@ -2350,71 +2361,43 @@ begin
while ScrollResult=grOK do
begin
FCurrentIndex.StoreCurrentRecIntoBookmark(ABookmark);
if GetRecordUpdateBuffer(ABookmark^) and (FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind <> ukDelete) then
if GetRecordUpdateBuffer(ABookmark^,True) then
begin
if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind = ukInsert then
begin
RowState:=[rsvInserted];
FFilterBuffer:=FCurrentIndex.CurrentBuffer;
with ChangeLog[FCurrentUpdateBuffer] do
begin
OrigEntry:=0;
NewEntry:=EntryNr;
UpdateKind:=ukInsert;
end;
end
else // This is always ukModified
begin
RowState:=[rsvOriginal];
FFilterBuffer:=FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer;
ChangeLog[FCurrentUpdateBuffer].OrigEntry:=EntryNr;
end;
case FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind of
ukModify : begin
FFilterBuffer:=FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer;
FDatasetReader.StoreRecord(Self,[rsvOriginal],FCurrentUpdateBuffer);
RowState:=[rsvUpdated];
end;
ukDelete : begin
repeat
StoreUpdBuf := FCurrentUpdateBuffer;
RunNr := 0;
repeat
inc(RunNr);
Assert(FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind=ukDelete);
FFilterBuffer:=FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer;
FDatasetReader.StoreRecord(Self,[rsvDeleted],FCurrentUpdateBuffer);
RowState:=[];
until not GetRecordUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer].DelBookmarkData,True,RunNr>1);
FCurrentUpdateBuffer:=StoreUpdBuf;
until not GetRecordUpdateBuffer(ABookmark^,True,True)
end;
end; { case }
end
else
begin
FFilterBuffer:=FCurrentIndex.CurrentBuffer;
RowState:=[];
end;
FFilterBuffer:=FCurrentIndex.CurrentBuffer;
FDatasetReader.StoreRecord(Self,RowState,FCurrentUpdateBuffer);
FDatasetReader.StoreRecord(Self,RowState);
inc(EntryNr);
ScrollResult:=FCurrentIndex.ScrollForward;
end;
for RecUpdBuf:=0 to length(FUpdateBuffer)-1 do with FUpdateBuffer[RecUpdBuf] do
begin
if UpdateKind = ukDelete then
begin
RowState:=[rsvDeleted];
FFilterBuffer:=FUpdateBuffer[RecUpdBuf].OldValuesBuffer;
FDatasetReader.StoreRecord(Self, RowState);
with ChangeLog[RecUpdBuf] do
begin
NewEntry:=EntryNr;
UpdateKind:=ukDelete;
end;
inc(EntryNr);
end
else if UpdateKind = ukModify then
begin
RowState:=[rsvUpdated];
FCurrentIndex.GotoBookmark(@BookmarkData);
FFilterBuffer:=FCurrentIndex.CurrentBuffer;
FDatasetReader.StoreRecord(Self, RowState);
with ChangeLog[RecUpdBuf] do
begin
NewEntry:=EntryNr;
UpdateKind:=ukModify;
end;
inc(EntryNr);
end;
end;
RestoreState(StoreDSState);
FDatasetReader.EndStoreRecord(ChangeLog);
SetLength(ChangeLog,0);
FDatasetReader.EndStoreRecord;
finally
FDatasetReader := nil;
end;
@ -2427,7 +2410,10 @@ begin
if GetRegisterDatapacketReader(AStream,format,APacketReaderReg) then
APacketReader := APacketReaderReg.ReaderClass.create(AStream)
else if TFpcBinaryDatapacketReader.RecognizeStream(AStream) then
begin
AStream.Seek(0,soFromBeginning);
APacketReader := TFpcBinaryDatapacketReader.create(AStream)
end
else
DatabaseError(SStreamNotRecognised);
try
@ -2472,7 +2458,16 @@ begin
CreateFields;
end;
procedure TBufDataset.IntLoadFielddefsFromFile(const FileName: string);
function TBufDataset.CompareBookmarks(Bookmark1, Bookmark2: TBookmark
): Longint;
begin
if FCurrentIndex.CompareBookmarks(Bookmark1,Bookmark2) then
Result := 0
else
Result := -1;
end;
procedure TBufDataset.IntLoadFielddefsFromFile;
begin
FDatasetReader.LoadFielddefs(FieldDefs);
@ -2481,44 +2476,71 @@ end;
procedure TBufDataset.IntLoadRecordsFromFile;
var StoreState : TDataSetState;
ChangeLog : TChangeLogEntryArr;
ChangeLogStr : string;
ChangeLogInfo : TChangeLogInfoArr;
EntryNr : integer;
i : integer;
IsUpdate,
AddRecordBuffer,
IsFirstEntry : boolean;
var StoreState : TDataSetState;
EntryNr : integer;
AddRecordBuffer : boolean;
ARowState : TRowState;
AUpdOrder : integer;
begin
FDatasetReader.InitLoadRecords(ChangeLog);
FDatasetReader.InitLoadRecords;
EntryNr:=1;
StoreState:=SetTempState(dsFilter);
SetLength(ChangeLogInfo,length(ChangeLog));
while FDatasetReader.GetCurrentRecord do
begin
FDatasetReader.GetRecordUpdState(IsUpdate,AddRecordBuffer,IsFirstEntry);
if IsUpdate then
ARowState := FDatasetReader.GetRecordRowState(AUpdOrder);
if rsvOriginal in ARowState then
begin
if IsFirstEntry then
begin
for i := 0 to length(ChangeLog) -1 do
if ChangeLog[i].OrigEntry=EntryNr then break;
ChangeLogInfo[i].FirstChangeNode:=FDatasetReader.GetCurrentElement;
end
else
begin
for i := 0 to length(ChangeLog) -1 do
if ChangeLog[i].NewEntry=EntryNr then break;
ChangeLogInfo[i].SecondChangeNode:=FDatasetReader.GetCurrentElement;
end;
if length(FUpdateBuffer) < (AUpdOrder+1) then
SetLength(FUpdateBuffer,AUpdOrder+1);
FIndexes[0].StoreSpareRecIntoBookmark(@ChangeLogInfo[i].Bookmark);
end;
FCurrentUpdateBuffer:=AUpdOrder;
FFilterBuffer:=IntAllocRecordBuffer;
fillchar(FFilterBuffer^,FNullmaskSize,0);
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := FFilterBuffer;
FDatasetReader.RestoreRecord(self);
FDatasetReader.GotoNextRecord;
if not FDatasetReader.GetCurrentRecord then
DatabaseError(SStreamNotRecognised);
ARowState := FDatasetReader.GetRecordRowState(AUpdOrder);
if rsvUpdated in ARowState then
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukModify
else
DatabaseError(SStreamNotRecognised);
FFilterBuffer:=FIndexes[0].SpareBuffer;
FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
fillchar(FFilterBuffer^,FNullmaskSize,0);
FDatasetReader.RestoreRecord(self);
FIndexes[0].AddRecord(IntAllocRecordBuffer);
inc(FBRecordCount);
AddRecordBuffer:=False;
end
else if rsvDeleted in ARowState then
begin
if length(FUpdateBuffer) < (AUpdOrder+1) then
SetLength(FUpdateBuffer,AUpdOrder+1);
FCurrentUpdateBuffer:=AUpdOrder;
FFilterBuffer:=IntAllocRecordBuffer;
fillchar(FFilterBuffer^,FNullmaskSize,0);
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := FFilterBuffer;
FDatasetReader.RestoreRecord(self);
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukDelete;
FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
AddRecordBuffer:=False;
end
else
AddRecordBuffer:=True;
if AddRecordBuffer then
begin
@ -2534,35 +2556,6 @@ begin
inc(EntryNr);
end;
// Iterate through the ChangeLog list and add modifications to he update buffer
for i := 0 to length(ChangeLog)-1 do
begin
FCurrentUpdateBuffer:=Length(FUpdateBuffer);
setlength(FUpdateBuffer,FCurrentUpdateBuffer+1);
case ChangeLog[i].UpdateKind of
ukDelete : begin
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:=ukDelete;
FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData:=ChangeLogInfo[i].Bookmark;
FDatasetReader.GotoElement(ChangeLogInfo[i].FirstChangeNode);
FDatasetReader.RestoreRecord(self);
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer:=IntAllocRecordBuffer;
move(findexes[0].SpareBuffer^,FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
end;
ukModify : begin
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:=ukModify;
FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData:=ChangeLogInfo[i].Bookmark;
FDatasetReader.GotoElement(ChangeLogInfo[i].SecondChangeNode);
FDatasetReader.RestoreRecord(self);
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer:=IntAllocRecordBuffer;
move(findexes[0].SpareBuffer^,FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
end;
ukInsert : begin
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:=ukInsert;
FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData:=ChangeLogInfo[i].Bookmark;
FDatasetReader.GotoElement(ChangeLogInfo[i].FirstChangeNode);
end;
end; {case}
end;
RestoreState(StoreState);
FIndexes[0].SetToFirstRecord;
FAllPacketsFetched:=True;
@ -2820,6 +2813,7 @@ end;
constructor TArrayBufIndex.Create(const ADataset: TBufDataset);
begin
Inherited create(ADataset);
FInitialBuffers:=10000;
FGrowBuffer:=1000;
end;
@ -2993,6 +2987,27 @@ end;
{ TDataPacketReader }
class function TDataPacketReader.RowStateToByte(const ARowState: TRowState
): byte;
var RowStateInt : Byte;
begin
RowStateInt:=0;
if rsvOriginal in ARowState then RowStateInt := RowStateInt+1;
if rsvDeleted in ARowState then RowStateInt := RowStateInt+2;
if rsvInserted in ARowState then RowStateInt := RowStateInt+4;
if rsvUpdated in ARowState then RowStateInt := RowStateInt+8;
Result := RowStateInt;
end;
class function TDataPacketReader.ByteToRowState(const AByte: Byte): TRowState;
begin
result := [];
if (AByte and 1)=1 then Result := Result+[rsvOriginal];
if (AByte and 2)=2 then Result := Result+[rsvDeleted];
if (AByte and 4)=4 then Result := Result+[rsvInserted];
if (AByte and 8)=8 then Result := Result+[rsvUpdated];
end;
constructor TDataPacketReader.create(AStream: TStream);
begin
FStream := AStream;
@ -3044,17 +3059,20 @@ begin
end;
end;
procedure TFpcBinaryDatapacketReader.GetRecordUpdState(var AIsUpdate,
AAddRecordBuffer, AIsFirstEntry: boolean);
function TFpcBinaryDatapacketReader.GetRecordRowState(out AUpdOrder : Integer) : TRowState;
var Buf : byte;
begin
AIsUpdate:=False;
AAddRecordBuffer:=True;
Stream.Read(Buf,1);
Result := ByteToRowState(Buf);
if Result<>[] then
Stream.ReadBuffer(AUpdOrder,sizeof(integer))
else
AUpdOrder := 0;
end;
procedure TFpcBinaryDatapacketReader.EndStoreRecord(
const AChangeLog: TChangeLogEntryArr);
procedure TFpcBinaryDatapacketReader.EndStoreRecord;
begin
// inherited EndStoreRecord(AChangeLog);
// Do nothing
end;
function TFpcBinaryDatapacketReader.GetCurrentRecord: boolean;
@ -3070,18 +3088,17 @@ end;
procedure TFpcBinaryDatapacketReader.GotoElement(const AnElement: pointer);
begin
// inherited GotoElement(AnElement);
// Do nothing
end;
procedure TFpcBinaryDatapacketReader.InitLoadRecords(
var AChangeLog: TChangeLogEntryArr);
procedure TFpcBinaryDatapacketReader.InitLoadRecords;
begin
SetLength(AChangeLog,0);
// SetLength(AChangeLog,0);
end;
function TFpcBinaryDatapacketReader.GetCurrentElement: pointer;
begin
// Result:=inherited GetCurrentElement;
// Do nothing
end;
procedure TFpcBinaryDatapacketReader.RestoreRecord(ADataset: TBufDataset);
@ -3090,10 +3107,13 @@ begin
end;
procedure TFpcBinaryDatapacketReader.StoreRecord(ADataset: TBufDataset;
RowState: TRowState);
ARowState: TRowState; AUpdOrder : integer);
begin
// Ugly because private members of ADataset are used...
Stream.WriteByte($fe);
Stream.WriteByte(RowStateToByte(ARowState));
if ARowState<>[] then
Stream.WriteBuffer(AUpdOrder,sizeof(integer));
Stream.WriteBuffer(ADataset.GetCurrentBuffer^,ADataset.FRecordSize);
end;