You are not logged in.
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
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
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!
Offline