#1 2014-04-14 20:18:23

martin.suer
Member
Registered: 2013-12-15
Posts: 76

Bug in TSQLRecord.FillRow when TSQLRecord contains a TDocVariant

Hi ab,

I am using XE5 and the nightly build.

Code to reproduce:

program VariantBug;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  SynCommons,
  mORMot,
  mORMotSqlite3,
  SynSQLite3Static;

type
  TSQLRecordData = class(TSQLRecord)
  private
    fName: RawUTF8;
    fData: variant;
  public
  published
    property Name: RawUTF8 read fName write fName stored AS_UNIQUE;
    property Data: variant read fData write fData;
  end;

var
  Model : TSQLModel;
  DB : TSQLRest;
  aRec: TSQLRecordData;
  aID: integer;
begin
  DeleteFile('bug.db3');
  Model := TSQLModel.Create([TSQLRecordData]);
  DB := TSQLRestServerDB.Create(Model, 'bug.db3');
  TSQLRestServerDB(DB).CreateMissingTables(0);

  aRec := TSQLRecordData.Create;
  aRec.Name := 'Joe';
  aRec.Data := _ObjFast(['name','Joe','age',30]);
  aID := DB.Add(aRec,True);
  aRec.Free;

  aRec := TSQLRecordData.Create;
  aRec.Name := 'Jon';
  aRec.Data := _ObjFast(['name','Jon','age',31]);
  aID := DB.Add(aRec,True);
  aRec.Free;

  aRec := TSQLRecordData.Create;
  aRec.Name := 'Joan';
  aRec.Data := _ObjFast(['name','Joan','age',29]);
  aID := DB.Add(aRec,True);
  aRec.Free;

  aRec := TSQLRecordData.CreateAndFillPrepare(DB,'');
  aRec.FillOne;
  WriteLn(aRec.Data.Name, ' (',aRec.Data.age,')');
  aRec.FillOne;
  WriteLn(aRec.Data.Name, ' (',aRec.Data.age,')');

  aRec.FillRow(1);
  WriteLn(aRec.Data.Name, ' (',aRec.Data.age,')'); // <== Exception
  // because aRec.Data is unassigned here

  aRec.Free;
  ReadLn;
end.

without the Data property in the TSQLRecord, the FillRow Method is working correctly.

Is this a limitation or a bug?

Martin

Offline

#2 2014-04-15 10:57:40

martin.suer
Member
Registered: 2013-12-15
Posts: 76

Re: Bug in TSQLRecord.FillRow when TSQLRecord contains a TDocVariant

Ok, the reason is that the Fill method changes the content of the internal TSQLTable (while the Variant content is parsed). So any record can only be filled once.

A change in mormot.pas will fix the immediate problem:

procedure TSQLPropInfoRTTIVariant.SetValue(Instance: TObject; Value: PUTF8Char;
  wasString: boolean);
var V: Variant;
    ValueCopy: RawUTF8;
begin
  ValueCopy:=Value;
  GetVariantFromJSON(@ValueCopy[1],wasString,V,@DocVariantOptions);
  SetVariantProp(Instance,pointer(fPropInfo),V);
end;

but I am not sure if this a) is the right place, b) will introduce side effects.
And of course it will decrease performance (not significantly though), since the string will be copied here.

What about other serialized types (e.g. records)? Does the same problem exist there too?

Thoughts?

Last edited by martin.suer (2014-04-15 11:00:17)

Offline

#3 2014-04-19 07:14:47

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,216
Website

Re: Bug in TSQLRecord.FillRow when TSQLRecord contains a TDocVariant

This was indeed an issue.

I've ensured that TSQLPropInfoRTTIObject.SetValue() and TSQLPropInfoRTTIVariant.SetValue() will use a temporary copy of the JSON content before parsing it: it will allow to re-parse a TSQLRecord object e.g. via FillRow(aIndex) more than once.
See http://synopse.info/fossil/info/899e5aa900

I made a code review, and only the TSQLPropInfoRTTIObject class sounded to be affected too...

Thanks for the feedback!
Sorry for a late answer!
smile

Offline

Board footer

Powered by FluxBB