Browse Source

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 -

lacak 12 years ago
parent
commit
625a2c18f3
1 changed files with 114 additions and 43 deletions
  1. 114 43
      packages/fcl-db/src/base/bufdataset.pas

+ 114 - 43
packages/fcl-db/src/base/bufdataset.pas

@@ -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
-    DatabaseError(SStreamNotRecognised);
+  // 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);
+    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,18 +3548,7 @@ begin
   Stream.WriteBuffer(i,sizeof(i));
   Stream.WriteBuffer(i,sizeof(i));
 end;
 end;
 
 
-function TFpcBinaryDatapacketReader.GetRecordRowState(out AUpdOrder : Integer) : TRowState;
-var Buf : byte;
-begin
-  Stream.Read(Buf,1);
-  Result := ByteToRowState(Buf);
-  if Result<>[] then
-    Stream.ReadBuffer(AUpdOrder,sizeof(integer))
-  else
-    AUpdOrder := 0;
-end;
-
-procedure TFpcBinaryDatapacketReader.FinalizeStoreRecords;
+procedure TFpcBinaryDatapacketReader.InitLoadRecords;
 begin
 begin
   //  Do nothing
   //  Do nothing
 end;
 end;
@@ -3540,44 +3559,96 @@ begin
   Result := (Stream.Read(Buf,1)=1) and (Buf=$fe);
   Result := (Stream.Read(Buf,1)=1) and (Buf=$fe);
 end;
 end;
 
 
-procedure TFpcBinaryDatapacketReader.GotoNextRecord;
+function TFpcBinaryDatapacketReader.GetRecordRowState(out AUpdOrder : Integer) : TRowState;
+var Buf : byte;
 begin
 begin
-  //  Do Nothing
+  Stream.Read(Buf,1);
+  Result := ByteToRowState(Buf);
+  if Result<>[] then
+    Stream.ReadBuffer(AUpdOrder,sizeof(integer))
+  else
+    AUpdOrder := 0;
 end;
 end;
 
 
-procedure TFpcBinaryDatapacketReader.InitLoadRecords;
+procedure TFpcBinaryDatapacketReader.GotoNextRecord;
 begin
 begin
-  //  SetLength(AChangeLog,0);
+  //  Do Nothing
 end;
 end;
 
 
 procedure TFpcBinaryDatapacketReader.RestoreRecord(ADataset: TCustomBufDataset);
 procedure TFpcBinaryDatapacketReader.RestoreRecord(ADataset: TCustomBufDataset);
-begin
-  Stream.ReadBuffer(ADataset.GetCurrentBuffer^,ADataset.FRecordSize);
+var
+  AField: TField;
+  i: integer;
+  L: cardinal;
+  B: TBytes;
+begin
+  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
-  ): boolean;
-var s        : string;
-    len      : integer;
+procedure TFpcBinaryDatapacketReader.FinalizeStoreRecords;
 begin
 begin
-  Len := length(FpcBinaryIdent);
-  setlength(s,len);
-  if (AStream.Read (s[1],len) = len)
-  and (s=FpcBinaryIdent) then
-    Result := True
-  else
-    Result := False;
+  //  Do nothing
+end;
+
+class function TFpcBinaryDatapacketReader.RecognizeStream(AStream: TStream): boolean;
+var s : string;
+begin
+  SetLength(s, 13);
+  if (AStream.Read(s[1], 13) = 13) then
+    case s of
+      FpcBinaryIdent1,
+      FpcBinaryIdent2:
+        Result := True;
+      else
+        Result := False;
+    end;
 end;
 end;
 
 
 { TUniDirectionalBufIndex }
 { TUniDirectionalBufIndex }