fcl-db: bufdataset: fixes bug when saving of BLOB data using SaveToFile or SaveToStream in dfBinary format.(instead of BLOB data only pointer to "BLOB buffer record" is saved)

Format of saved data is changed as follows:
- Identification (start of file) changes from "BinBufDataset" (OLD version named 1.0) to "BinBufDataSet" (NEW version named 2.0)
- Just after Identification ("BinBufDataSet") is written one byte indicating version (for now it is 20 as 2.0)
- FieldDefs are saved in same format as in OLD format (nothing chages)
- Record header is saved in same format as in OLD format (nothing chages)
- Record data are saved field by field, where each field begins with 4 bytes indicating length of data, followed by data (here is used TField.AsBytes to get actual data)

Backward compatibility is keept in reading OLD format
When saving NEW format is always used

git-svn-id: trunk@25333 -
This commit is contained in:
lacak 2013-08-23 08:38:29 +00:00
parent a298a9ecf8
commit 625a2c18f3

View File

@ -350,6 +350,7 @@ type
protected protected
class function RowStateToByte(const ARowState : TRowState) : byte; class function RowStateToByte(const ARowState : TRowState) : byte;
class function ByteToRowState(const AByte : Byte) : TRowState; class function ByteToRowState(const AByte : Byte) : TRowState;
class procedure RestoreBlobField(ADataset: TCustomBufDataset; AField: TField; ASource: pointer; ASize: integer);
public public
constructor create(AStream : TStream); virtual; constructor create(AStream : TStream); virtual;
// Load a dataset from stream: // Load a dataset from stream:
@ -357,10 +358,10 @@ type
procedure LoadFieldDefs(AFieldDefs : TFieldDefs; var AnAutoIncValue : integer); virtual; abstract; procedure LoadFieldDefs(AFieldDefs : TFieldDefs; var AnAutoIncValue : integer); virtual; abstract;
// Is called before the records are loaded // Is called before the records are loaded
procedure InitLoadRecords; virtual; abstract; procedure InitLoadRecords; virtual; abstract;
// Return the RowState of the current record, and the order of the update
function GetRecordRowState(out AUpdOrder : Integer) : TRowState; virtual; abstract;
// Returns if there is at least one more record available in the stream // Returns if there is at least one more record available in the stream
function GetCurrentRecord : boolean; virtual; abstract; function GetCurrentRecord : boolean; virtual; abstract;
// Return the RowState of the current record, and the order of the update
function GetRecordRowState(out AUpdOrder : Integer) : TRowState; virtual; abstract;
// Store a record from stream in the current record buffer // Store a record from stream in the current record buffer
procedure RestoreRecord(ADataset : TCustomBufDataset); virtual; abstract; procedure RestoreRecord(ADataset : TCustomBufDataset); virtual; abstract;
// Move the stream to the next record // Move the stream to the next record
@ -381,19 +382,26 @@ type
{ TFpcBinaryDatapacketReader } { TFpcBinaryDatapacketReader }
TFpcBinaryDatapacketReader = class(TDataPacketReader) TFpcBinaryDatapacketReader = class(TDataPacketReader)
private
const
FpcBinaryIdent1 = 'BinBufDataset'; // Old version 1; support for transient period;
FpcBinaryIdent2 = 'BinBufDataSet';
var
FVersion: byte;
public public
procedure LoadFieldDefs(AFieldDefs : TFieldDefs; var AnAutoIncValue : integer); override; procedure LoadFieldDefs(AFieldDefs : TFieldDefs; var AnAutoIncValue : integer); override;
procedure StoreFieldDefs(AFieldDefs : TFieldDefs; AnAutoIncValue : integer); override; procedure StoreFieldDefs(AFieldDefs : TFieldDefs; AnAutoIncValue : integer); override;
function GetRecordRowState(out AUpdOrder : Integer) : TRowState; override;
procedure FinalizeStoreRecords; override;
function GetCurrentRecord : boolean; override;
procedure GotoNextRecord; override;
procedure InitLoadRecords; override; procedure InitLoadRecords; override;
function GetCurrentRecord : boolean; override;
function GetRecordRowState(out AUpdOrder : Integer) : TRowState; override;
procedure RestoreRecord(ADataset : TCustomBufDataset); override; procedure RestoreRecord(ADataset : TCustomBufDataset); override;
procedure GotoNextRecord; override;
procedure StoreRecord(ADataset : TCustomBufDataset; ARowState : TRowState; AUpdOrder : integer = 0); override; procedure StoreRecord(ADataset : TCustomBufDataset; ARowState : TRowState; AUpdOrder : integer = 0); override;
procedure FinalizeStoreRecords; override;
class function RecognizeStream(AStream : TStream) : boolean; override; class function RecognizeStream(AStream : TStream) : boolean; override;
end; end;
TCustomBufDataset = class(TDBDataSet) TCustomBufDataset = class(TDBDataSet)
private private
FFileName: string; FFileName: string;
@ -3462,24 +3470,45 @@ begin
if (AByte and 8)=8 then Result := Result+[rsvUpdated]; if (AByte and 8)=8 then Result := Result+[rsvUpdated];
end; end;
class procedure TDataPacketReader.RestoreBlobField(ADataset: TCustomBufDataset; AField: TField; ASource: pointer; ASize: integer);
var
ABufBlobField: TBufBlobField;
begin
ABufBlobField.BlobBuffer:=ADataset.GetNewBlobBuffer;
ABufBlobField.BlobBuffer^.Size:=ASize;
ReAllocMem(ABufBlobField.BlobBuffer^.Buffer, ASize);
move(ASource^, ABufBlobField.BlobBuffer^.Buffer^, ASize);
AField.SetData(@ABufBlobField);
end;
constructor TDataPacketReader.create(AStream: TStream); constructor TDataPacketReader.create(AStream: TStream);
begin begin
FStream := AStream; FStream := AStream;
end; end;
{ TFpcBinaryDatapacketReader }
const FpcBinaryIdent = 'BinBufDataset'; { TFpcBinaryDatapacketReader }
procedure TFpcBinaryDatapacketReader.LoadFieldDefs(AFieldDefs: TFieldDefs; var AnAutoIncValue: integer); procedure TFpcBinaryDatapacketReader.LoadFieldDefs(AFieldDefs: TFieldDefs; var AnAutoIncValue: integer);
var FldCount : word; var FldCount : word;
i : integer; i : integer;
s : string;
begin begin
if not RecognizeStream(Stream) then // Identify version
SetLength(s, 13);
if (Stream.Read(s[1], 13) = 13) then
case s of
FpcBinaryIdent1:
FVersion := 10;
FpcBinaryIdent2:
FVersion := Stream.ReadByte;
else
DatabaseError(SStreamNotRecognised); DatabaseError(SStreamNotRecognised);
end;
// Read FieldDefs
FldCount:=Stream.ReadWord; FldCount:=Stream.ReadWord;
AFieldDefs.Clear; AFieldDefs.Clear;
for i := 0 to FldCount -1 do with TFieldDef.create(AFieldDefs) do for i := 0 to FldCount -1 do with TFieldDef.create(AFieldDefs) do
@ -3499,14 +3528,15 @@ end;
procedure TFpcBinaryDatapacketReader.StoreFieldDefs(AFieldDefs: TFieldDefs; AnAutoIncValue: integer); procedure TFpcBinaryDatapacketReader.StoreFieldDefs(AFieldDefs: TFieldDefs; AnAutoIncValue: integer);
var i : integer; var i : integer;
begin begin
Stream.Write(FpcBinaryIdent[1],length(FpcBinaryIdent)); Stream.Write(FpcBinaryIdent2[1], length(FpcBinaryIdent2));
Stream.WriteByte(20); // version 2.0
Stream.WriteWord(AFieldDefs.Count); Stream.WriteWord(AFieldDefs.Count);
for i := 0 to AFieldDefs.Count -1 do with AFieldDefs[i] do for i := 0 to AFieldDefs.Count -1 do with AFieldDefs[i] do
begin begin
Stream.WriteAnsiString(Name); Stream.WriteAnsiString(Name);
Stream.WriteAnsiString(DisplayName); Stream.WriteAnsiString(DisplayName);
Stream.WriteWord(size); Stream.WriteWord(Size);
Stream.WriteWord(ord(DataType)); Stream.WriteWord(ord(DataType));
if faReadonly in Attributes then if faReadonly in Attributes then
@ -3518,6 +3548,17 @@ begin
Stream.WriteBuffer(i,sizeof(i)); Stream.WriteBuffer(i,sizeof(i));
end; end;
procedure TFpcBinaryDatapacketReader.InitLoadRecords;
begin
// Do nothing
end;
function TFpcBinaryDatapacketReader.GetCurrentRecord: boolean;
var Buf : byte;
begin
Result := (Stream.Read(Buf,1)=1) and (Buf=$fe);
end;
function TFpcBinaryDatapacketReader.GetRecordRowState(out AUpdOrder : Integer) : TRowState; function TFpcBinaryDatapacketReader.GetRecordRowState(out AUpdOrder : Integer) : TRowState;
var Buf : byte; var Buf : byte;
begin begin
@ -3529,56 +3570,86 @@ begin
AUpdOrder := 0; AUpdOrder := 0;
end; end;
procedure TFpcBinaryDatapacketReader.FinalizeStoreRecords;
begin
// Do nothing
end;
function TFpcBinaryDatapacketReader.GetCurrentRecord: boolean;
var Buf : byte;
begin
Result := (Stream.Read(Buf,1)=1) and (Buf=$fe);
end;
procedure TFpcBinaryDatapacketReader.GotoNextRecord; procedure TFpcBinaryDatapacketReader.GotoNextRecord;
begin begin
// Do Nothing // Do Nothing
end; end;
procedure TFpcBinaryDatapacketReader.InitLoadRecords;
begin
// SetLength(AChangeLog,0);
end;
procedure TFpcBinaryDatapacketReader.RestoreRecord(ADataset: TCustomBufDataset); procedure TFpcBinaryDatapacketReader.RestoreRecord(ADataset: TCustomBufDataset);
var
AField: TField;
i: integer;
L: cardinal;
B: TBytes;
begin begin
Stream.ReadBuffer(ADataset.GetCurrentBuffer^,ADataset.FRecordSize); case FVersion of
10:
Stream.ReadBuffer(ADataset.GetCurrentBuffer^, ADataset.FRecordSize); // Ugly because private members of ADataset are used...
20:
with ADataset do
for i:=0 to FieldDefs.Count-1 do
begin
AField := Fields.FieldByNumber(FieldDefs[i].FieldNo);
if AField=nil then continue;
L := Stream.ReadDWord;
SetLength(B, L);
if L > 0 then
Stream.ReadBuffer(B[0], L);
if FieldDefs[i].DataType in [ftBlob, ftMemo, ftWideMemo] then
RestoreBlobField(ADataset, AField, @B[0], L)
else
AField.SetData(@B[0], False); // set it to the FilterBuffer
end;
end;
end; end;
procedure TFpcBinaryDatapacketReader.StoreRecord(ADataset: TCustomBufDataset; procedure TFpcBinaryDatapacketReader.StoreRecord(ADataset: TCustomBufDataset;
ARowState: TRowState; AUpdOrder : integer); ARowState: TRowState; AUpdOrder : integer);
var
AField: TField;
i: integer;
L: cardinal;
B: TBytes;
begin begin
// Ugly because private members of ADataset are used... // Record header
Stream.WriteByte($fe); Stream.WriteByte($fe);
Stream.WriteByte(RowStateToByte(ARowState)); Stream.WriteByte(RowStateToByte(ARowState));
if ARowState<>[] then if ARowState<>[] then
Stream.WriteBuffer(AUpdOrder,sizeof(integer)); Stream.WriteBuffer(AUpdOrder,sizeof(integer));
Stream.WriteBuffer(ADataset.GetCurrentBuffer^,ADataset.FRecordSize);
// Record data
// Old 1.0 version: Stream.WriteBuffer(ADataset.GetCurrentBuffer^, ADataset.FRecordSize);
with ADataset do
for i:=0 to FieldDefs.Count-1 do
begin
AField := Fields.FieldByNumber(FieldDefs[i].FieldNo);
if AField=nil then continue;
B := AField.AsBytes;
L := length(B);
Stream.WriteDWord(L);
if L > 0 then
Stream.WriteBuffer(B[0], L);
end;
end; end;
class function TFpcBinaryDatapacketReader.RecognizeStream(AStream: TStream procedure TFpcBinaryDatapacketReader.FinalizeStoreRecords;
): boolean;
var s : string;
len : integer;
begin begin
Len := length(FpcBinaryIdent); // Do nothing
setlength(s,len); end;
if (AStream.Read (s[1],len) = len)
and (s=FpcBinaryIdent) then class function TFpcBinaryDatapacketReader.RecognizeStream(AStream: TStream): boolean;
Result := True var s : string;
begin
SetLength(s, 13);
if (AStream.Read(s[1], 13) = 13) then
case s of
FpcBinaryIdent1,
FpcBinaryIdent2:
Result := True;
else else
Result := False; Result := False;
end; end;
end;
{ TUniDirectionalBufIndex } { TUniDirectionalBufIndex }