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

git-svn-id: trunk@43643 -
This commit is contained in:
michael 2019-12-05 14:01:43 +00:00
parent e28b39e395
commit 76a4638a46
3 changed files with 170 additions and 28 deletions

View File

@ -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 for i := 0 to length(RegisteredDatapacketReaders)-1 do
begin if ((AFormat=dfAny) or (AFormat=RegisteredDatapacketReaders[i].Format)) then
if (AStream=nil) or (RegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then
begin begin
ADataReaderClass := RegisteredDatapacketReaders[i]; if (AStream=nil) or (RegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then
Result := True; begin
if (AStream <> nil) then AStream.Seek(0,soFromBeginning); ADataReaderClass := RegisteredDatapacketReaders[i];
break; 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 if (FFileName<>'') then
SaveToFile(FFileName); 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; var
APacketReaderReg: TDatapacketReaderRegistration; APacketReader: TDataPacketReader;
APacketReaderReg: TDatapacketReaderRegistration;
Fmt : TDataPacketFormat;
begin begin
if GetRegisterDatapacketReader(AStream, format, APacketReaderReg) then 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; procedure TCustomBufDataset.SaveToFile(AFileName: string; Format: TDataPacketFormat);
Format: TDataPacketFormat);
var AFileStream : TFileStream; 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;

View File

@ -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);

View File

@ -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);