Browse Source

* Rework default file mechanism in bufdataset to fix bug #34435

git-svn-id: trunk@43643 -
michael 5 years ago
parent
commit
76a4638a46

+ 67 - 28
packages/fcl-db/src/base/bufdataset.pas

@@ -353,7 +353,7 @@ type
 
 
   { TDataPacketReader }
   { TDataPacketReader }
 
 
-  TDataPacketFormat = (dfBinary,dfXML,dfXMLUTF8,dfAny);
+  TDataPacketFormat = (dfBinary,dfXML,dfXMLUTF8,dfAny,dfDefault);
 
 
   TDatapacketReaderClass = class of TDatapacketReader;
   TDatapacketReaderClass = class of TDatapacketReader;
   TDataPacketReader = class(TObject)
   TDataPacketReader = class(TObject)
@@ -564,6 +564,9 @@ type
     Property BufIndexes[Aindex : Integer] : TBufIndex Read GetBufIndex;
     Property BufIndexes[Aindex : Integer] : TBufIndex Read GetBufIndex;
   protected
   protected
     // abstract & virtual methods of TDataset
     // abstract & virtual methods of TDataset
+    class function DefaultReadFileFormat : TDataPacketFormat; virtual;
+    class function DefaultWriteFileFormat : TDataPacketFormat; virtual;
+    class function DefaultPacketClass : TDataPacketReaderClass ; virtual;
     procedure SetPacketRecords(aValue : integer); virtual;
     procedure SetPacketRecords(aValue : integer); virtual;
     procedure SetRecNo(Value: Longint); override;
     procedure SetRecNo(Value: Longint); override;
     function  GetRecNo: Longint; override;
     function  GetRecNo: Longint; override;
@@ -640,9 +643,9 @@ type
 
 
     procedure SetDatasetPacket(AReader : TDataPacketReader);
     procedure SetDatasetPacket(AReader : TDataPacketReader);
     procedure GetDatasetPacket(AWriter : TDataPacketReader);
     procedure GetDatasetPacket(AWriter : TDataPacketReader);
-    procedure LoadFromStream(AStream : TStream; Format: TDataPacketFormat = dfAny);
+    procedure LoadFromStream(AStream : TStream; Format: TDataPacketFormat = dfDefault);
     procedure SaveToStream(AStream : TStream; Format: TDataPacketFormat = dfBinary);
     procedure SaveToStream(AStream : TStream; Format: TDataPacketFormat = dfBinary);
-    procedure LoadFromFile(AFileName: string = ''; Format: TDataPacketFormat = dfAny);
+    procedure LoadFromFile(AFileName: string = ''; Format: TDataPacketFormat = dfDefault);
     procedure SaveToFile(AFileName: string = ''; Format: TDataPacketFormat = dfBinary);
     procedure SaveToFile(AFileName: string = ''; Format: TDataPacketFormat = dfBinary);
     procedure CreateDataset;
     procedure CreateDataset;
     Procedure Clear; // Will close and remove all field definitions.
     Procedure Clear; // Will close and remove all field definitions.
@@ -738,17 +741,18 @@ var
 
 
 begin
 begin
   Result := False;
   Result := False;
-  for i := 0 to length(RegisteredDatapacketReaders)-1 do if ((AFormat=dfAny) or (AFormat=RegisteredDatapacketReaders[i].Format)) then
-    begin
-    if (AStream=nil) or (RegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then
+  for i := 0 to length(RegisteredDatapacketReaders)-1 do
+    if ((AFormat=dfAny) or (AFormat=RegisteredDatapacketReaders[i].Format)) then
       begin
       begin
-      ADataReaderClass := RegisteredDatapacketReaders[i];
-      Result := True;
-      if (AStream <> nil) then AStream.Seek(0,soFromBeginning);
-      break;
+      if (AStream=nil) or (RegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then
+        begin
+        ADataReaderClass := RegisteredDatapacketReaders[i];
+        Result := True;
+        if (AStream <> nil) then AStream.Seek(0,soFromBeginning);
+        break;
+        end;
+      AStream.Seek(0,soFromBeginning);
       end;
       end;
-    AStream.Seek(0,soFromBeginning);
-    end;
 end;
 end;
 
 
 function DBCompareText(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
 function DBCompareText(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
@@ -1431,8 +1435,8 @@ end;
 procedure TCustomBufDataset.DoBeforeClose;
 procedure TCustomBufDataset.DoBeforeClose;
 begin
 begin
   inherited DoBeforeClose;
   inherited DoBeforeClose;
-  if FFileName<>'' then
-    SaveToFile(FFileName);
+  if (FFileName<>'') then
+    SaveToFile(FFileName,dfDefault);
 end;
 end;
 
 
 procedure TCustomBufDataset.InternalClose;
 procedure TCustomBufDataset.InternalClose;
@@ -2258,6 +2262,22 @@ begin
       FMaxIndexesCount:=FIndexes.Count+2; // Custom+Default order
       FMaxIndexesCount:=FIndexes.Count+2; // Custom+Default order
 end;
 end;
 
 
+class function TCustomBufDataset.DefaultReadFileFormat: TDataPacketFormat;
+begin
+  Result:=dfAny;
+end;
+
+class function TCustomBufDataset.DefaultWriteFileFormat: TDataPacketFormat;
+begin
+  Result:=dfBinary;
+end;
+
+class function TCustomBufDataset.DefaultPacketClass: TDataPacketReaderClass;
+begin
+  Result:=TFpcBinaryDatapacketReader;
+end;
+
+
 procedure TCustomBufDataset.SetIndexFieldNames(const AValue: String);
 procedure TCustomBufDataset.SetIndexFieldNames(const AValue: String);
 
 
 begin
 begin
@@ -3056,11 +3076,17 @@ end;
 
 
 function TCustomBufDataset.GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader;
 function TCustomBufDataset.GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader;
 
 
-var APacketReader: TDataPacketReader;
-    APacketReaderReg: TDatapacketReaderRegistration;
-
-begin
-  if GetRegisterDatapacketReader(AStream, format, APacketReaderReg) then
+var
+  APacketReader: TDataPacketReader;
+  APacketReaderReg: TDatapacketReaderRegistration;
+  Fmt : TDataPacketFormat;
+begin
+  fmt:=Format;
+  if (Fmt=dfDefault) then
+    fmt:=DefaultReadFileFormat;
+  if fmt=dfDefault then
+    APacketReader := DefaultPacketClass.Create(Self, AStream)
+  else if GetRegisterDatapacketReader(AStream, fmt, APacketReaderReg) then
     APacketReader := APacketReaderReg.ReaderClass.Create(Self, AStream)
     APacketReader := APacketReaderReg.ReaderClass.Create(Self, AStream)
   else if TFpcBinaryDatapacketReader.RecognizeStream(AStream) then
   else if TFpcBinaryDatapacketReader.RecognizeStream(AStream) then
     begin
     begin
@@ -3433,11 +3459,17 @@ end;
 procedure TCustomBufDataset.SaveToStream(AStream: TStream; Format: TDataPacketFormat);
 procedure TCustomBufDataset.SaveToStream(AStream: TStream; Format: TDataPacketFormat);
 var APacketReaderReg : TDatapacketReaderRegistration;
 var APacketReaderReg : TDatapacketReaderRegistration;
     APacketWriter : TDataPacketReader;
     APacketWriter : TDataPacketReader;
+    Fmt : TDataPacketFormat;
 begin
 begin
   CheckBiDirectional;
   CheckBiDirectional;
-  if GetRegisterDatapacketReader(Nil,format,APacketReaderReg) then
+  fmt:=Format;
+  if Fmt=dfDefault then
+    fmt:=DefaultWriteFileFormat;
+  if fmt=dfDefault then
+    APacketWriter := DefaultPacketClass.Create(Self, AStream)
+  else if GetRegisterDatapacketReader(Nil,fmt,APacketReaderReg) then
     APacketWriter := APacketReaderReg.ReaderClass.Create(Self, AStream)
     APacketWriter := APacketReaderReg.ReaderClass.Create(Self, AStream)
-  else if Format = dfBinary then
+  else if fmt = dfBinary then
     APacketWriter := TFpcBinaryDatapacketReader.Create(Self, AStream)
     APacketWriter := TFpcBinaryDatapacketReader.Create(Self, AStream)
   else
   else
     DatabaseError(SNoReaderClassRegistered,Self);
     DatabaseError(SNoReaderClassRegistered,Self);
@@ -3449,9 +3481,13 @@ begin
 end;
 end;
 
 
 procedure TCustomBufDataset.LoadFromFile(AFileName: string; Format: TDataPacketFormat);
 procedure TCustomBufDataset.LoadFromFile(AFileName: string; Format: TDataPacketFormat);
-var AFileStream : TFileStream;
+
+var
+  AFileStream : TFileStream;
+
 begin
 begin
-  if AFileName='' then AFileName := FFileName;
+  if AFileName='' then
+     AFileName := FFileName;
   AFileStream := TFileStream.Create(AFileName,fmOpenRead);
   AFileStream := TFileStream.Create(AFileName,fmOpenRead);
   try
   try
     LoadFromStream(AFileStream, Format);
     LoadFromStream(AFileStream, Format);
@@ -3460,11 +3496,14 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TCustomBufDataset.SaveToFile(AFileName: string;
-  Format: TDataPacketFormat);
-var AFileStream : TFileStream;
+procedure TCustomBufDataset.SaveToFile(AFileName: string; Format: TDataPacketFormat);
+
+var
+  AFileStream : TFileStream;
+
 begin
 begin
-  if AFileName='' then AFileName := FFileName;
+  if AFileName='' then
+    AFileName := FFileName;
   AFileStream := TFileStream.Create(AFileName,fmCreate);
   AFileStream := TFileStream.Create(AFileName,fmCreate);
   try
   try
     SaveToStream(AFileStream, Format);
     SaveToStream(AFileStream, Format);
@@ -3536,7 +3575,7 @@ begin
   if not assigned(FDatasetReader) then
   if not assigned(FDatasetReader) then
     begin
     begin
     FFileStream := TFileStream.Create(FileName, fmOpenRead);
     FFileStream := TFileStream.Create(FileName, fmOpenRead);
-    FDatasetReader := GetPacketReader(dfAny, FFileStream);
+    FDatasetReader := GetPacketReader(dfDefault, FFileStream);
     end;
     end;
 
 
   FieldDefs.Clear;
   FieldDefs.Clear;

+ 19 - 1
packages/fcl-db/src/base/csvdataset.pp

@@ -95,6 +95,9 @@ Type
     FCSVOptions: TCSVOptions;
     FCSVOptions: TCSVOptions;
     procedure SetCSVOptions(AValue: TCSVOptions);
     procedure SetCSVOptions(AValue: TCSVOptions);
   Protected
   Protected
+    class function DefaultReadFileFormat : TDataPacketFormat; override;
+    class function DefaultWriteFileFormat : TDataPacketFormat; override;
+    class function DefaultPacketClass : TDataPacketReaderClass ; override;
     function GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader; override;
     function GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader; override;
     procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField); override;
     procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField); override;
     procedure InternalInitFieldDefs; override;
     procedure InternalInitFieldDefs; override;
@@ -305,10 +308,25 @@ begin
   FCSVOptions.Assign(AValue);
   FCSVOptions.Assign(AValue);
 end;
 end;
 
 
+class function TCustomCSVDataset.DefaultReadFileFormat: TDataPacketFormat;
+begin
+  Result:=dfDefault;
+end;
+
+class function TCustomCSVDataset.DefaultWriteFileFormat: TDataPacketFormat;
+begin
+  Result:=dfDefault;
+end;
+
+class function TCustomCSVDataset.DefaultPacketClass: TDataPacketReaderClass;
+begin
+  Result:=TCSVDataPacketReader;
+end;
+
 function TCustomCSVDataset.GetPacketReader(const Format: TDataPacketFormat;
 function TCustomCSVDataset.GetPacketReader(const Format: TDataPacketFormat;
   const AStream: TStream): TDataPacketReader;
   const AStream: TStream): TDataPacketReader;
 begin
 begin
-  If (Format=dfAny) then
+  If (Format in [dfAny,dfDefault]) then
     Result:=TCSVDataPacketReader.Create(Self,AStream,FCSVOptions)
     Result:=TCSVDataPacketReader.Create(Self,AStream,FCSVOptions)
   else
   else
     Result:=Inherited GetPacketReader(Format,AStream);
     Result:=Inherited GetPacketReader(Format,AStream);

+ 85 - 0
packages/fcl-db/tests/tccsvdataset.pp

@@ -15,6 +15,7 @@ type
   private
   private
     FCSVDataset: TCSVDataset;
     FCSVDataset: TCSVDataset;
     // Load CSVDataset from CSV stream containing lines
     // Load CSVDataset from CSV stream containing lines
+    procedure DoOpenClose;
     Procedure LoadFromLines(Const Lines: Array of string);
     Procedure LoadFromLines(Const Lines: Array of string);
     // Save CSVDataset to CSV stream, transform to lines
     // Save CSVDataset to CSV stream, transform to lines
     Procedure SaveToLines(Const Lines: TStrings);
     Procedure SaveToLines(Const Lines: TStrings);
@@ -47,6 +48,7 @@ type
     Procedure TestLoadPriorFieldDefsNoFieldNamesWrongCount;
     Procedure TestLoadPriorFieldDefsNoFieldNamesWrongCount;
     Procedure TestLoadPriorFieldDefsFieldNamesWrongCount;
     Procedure TestLoadPriorFieldDefsFieldNamesWrongCount;
     Procedure TestLoadPriorFieldDefsFieldNamesWrongNames;
     Procedure TestLoadPriorFieldDefsFieldNamesWrongNames;
+    Procedure TestOpenCloseCycle;
   end;
   end;
 
 
 implementation
 implementation
@@ -421,6 +423,89 @@ begin
     Fail(OK);
     Fail(OK);
 end;
 end;
 
 
+const
+  FILENAME = 'test.dat';
+
+procedure TTestCSVDataset.DoOpenClose;
+
+begin
+  CSVDataset.FileName := FILENAME;
+  With CSVDataset do
+     begin
+     CSVOptions.FirstLineAsFieldNames := True;
+     CSVOptions.DefaultFieldLength := 255;
+     CSVOptions.Delimiter := ',';
+     CSVOptions.QuoteChar := '"';
+     CSVOptions.IgnoreOuterWhitespace := False;
+     CSVOptions.QuoteOuterWhitespace := True;
+     end;
+  // When the program runs for the first time, the data file does not yet exist.
+  // We must create the FieldDefs and create the dataset.
+  if FileExists(CSVDataset.FileName) then
+    CSVDataset.Open
+  else
+    with CSVDataset do
+      begin
+      FieldDefs.Add('FirstName', ftString, 20);
+      FieldDefs.Add('LastName', ftstring, 20);
+      FieldDefs.Add('City', ftString, 20);
+      FieldDefs.Add('Address', ftString, 30);
+      FieldDefs.Add('Birthdate', ftDate);
+      CreateDataset;
+
+      // Open the dataset...
+      Open;
+
+      // ... and add some dummy data:
+      // Names from https://donatellanobatti.blogspot.de/
+      Append;
+      FieldByName('FirstName').AsString := 'Walter';
+      FieldByName('LastName').AsString := 'Mellon';
+      FieldByName('City').AsString := 'Oklahoma City';
+      FieldByName('Address').AsString :=  '1261, Main Street';
+      FieldbyName('Birthdate').AsDateTime := EncodeDate(1980, 1, 1);
+      Post;
+
+      Append;
+      FieldByName('FirstName').AsString := 'Mario';
+      FieldByName('LastName').AsString := 'Speedwagon';
+      FieldByName('City').AsString := 'Hollywood';
+      FieldByName('Address').AsString :=  '1500, Hollywood Blvd';
+      FieldbyName('Birthdate').AsDateTime := EncodeDate(1982, 12, 17);
+      Post;
+
+      Append;
+      FieldByName('FirstName').AsString := 'Anna';
+      FieldByName('LastName').AsString := 'Mull';
+      FieldByName('City').AsString := 'Los Angeles';
+      FieldByName('Address').AsString :=  '2202, Capitol Square';
+      FieldbyName('Birthdate').AsDateTime := EncodeDate(1982, 12, 17);
+      Post;
+      end;
+  // This will write the file;
+  CSVDataset.Close;
+end;
+
+procedure TTestCSVDataset.TestOpenCloseCycle;
+begin
+  if FileExists(FileName) then
+    AssertTrue('Delete before',DeleteFile(FileName));
+  try
+    // This will create the file
+    DoOpenClose;
+    // Recreate to be sure
+    FreeAndNil(FCSVDataset);
+    FCSVDataset:=TCSVDataset.Create(Nil);
+    FCSVDataset.Name:='DS';
+    DoOpenClose;
+  except
+    On E : Exception do
+      Fail('Failed using exception %s : %s',[E.ClassName,E.Message]);
+  end;
+  if FileExists(FileName) then
+    AssertTrue('Delete after',DeleteFile(FileName));
+end;
+
 procedure TTestCSVDataset.SetUp;
 procedure TTestCSVDataset.SetUp;
 begin
 begin
   FCSVDataset:=TCSVDataset.Create(Nil);
   FCSVDataset:=TCSVDataset.Create(Nil);