mORMot and Open Source friends
Check-in [8ac7d70abc]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:{2947} ensure JSONGetID() will return false if ID<=0 e.g. if {"ID":null,... is supplied as input
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8ac7d70abc09c40f1b9e7ee7e15adb95b8d984d5
User & Date: ab 2016-09-13 10:08:15
Context
2016-09-13
10:09
{2948} small refactoring with no functional change check-in: e5d8b046bd user: ab tags: trunk
10:08
{2947} ensure JSONGetID() will return false if ID<=0 e.g. if {"ID":null,... is supplied as input check-in: 8ac7d70abc user: ab tags: trunk
08:47
{2946} fixed [33568686fd] - certainly due to a cafeine low level check-in: 60826e6664 user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/mORMot.pas.

2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
.....
26739
26740
26741
26742
26743
26744
26745
26746
26747
26748
26749
26750
26751
26752
26753
26754
26755
26756
26757
26758
26759
26760
26761
26762
26763
26764
26765
26766
26767
26768
26769
26770
26771
26772
26773
26774
26775
26776
26777
26778
26779
26780
// "Name":Value pairs, as generated by TSQLRecord.GetJSONValues(W)
function JSONGetObject(var P: PUTF8Char; ExtractID: PID;
  var EndOfObject: AnsiChar; KeepIDField: boolean): RawUTF8;

/// retrieve the ID/RowID field of a JSON object
// - this function expects this "ID" property to be the FIRST in the
// "Name":Value pairs, as generated by TSQLRecord.GetJSONValues(W)
// - returns TRUE if ID/RowID has been found, and set ID with the value
function JSONGetID(P: PUTF8Char; out ID: TID): Boolean;

/// fill a TSQLRawBlob from TEXT-encoded blob data
// - blob data can be encoded as SQLite3 BLOB literals (X'53514C697465' e.g.) or
// or Base-64 encoded content ('\uFFF0base64encodedbinary') or plain TEXT
function BlobToTSQLRawBlob(P: PUTF8Char): TSQLRawBlob; overload;

................................................................................
end;

function StartWithQuotedID(P: PUTF8Char; out ID: TID): boolean;
begin
  if PCardinal(P)^ and $ffffdfdf=
      ord('I')+ord('D')shl 8+ord('"')shl 16+ord(':')shl 24 then begin
    SetID(P+4,ID);
    result := true;
    exit;
  end else
  if (PCardinalArray(P)^[0] and $dfdfdfdf=
       ord('R')+ord('O')shl 8+ord('W')shl 16+ord('I')shl 24) and
     (PCardinalArray(P)^[1] and $ffffdf=
       ord('D')+ord('"')shl 8+ord(':')shl 16) then begin
    SetID(P+7,ID);
    result := true;
    exit;
  end;
  ID := 0;
  result := false;
end;

function StartWithID(P: PUTF8Char; out ID: TID): boolean;
begin
  if PCardinal(P)^ and $ffdfdf=
      ord('I')+ord('D')shl 8+ord(':')shl 16 then begin
    SetID(P+3,ID);
    result := true;
    exit;
  end else
  if (PCardinalArray(P)^[0] and $dfdfdfdf=
       ord('R')+ord('O')shl 8+ord('W')shl 16+ord('I')shl 24) and
     (PCardinalArray(P)^[1] and $ffdf=ord('D')+ord(':')shl 8) then begin
    SetID(P+6,ID);
    result := true;
    exit;
  end;
  ID := 0;
  result := false;
end;

function JSONGetID(P: PUTF8Char; out ID: TID): Boolean;






|







 







|







|











|






|







2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
.....
26739
26740
26741
26742
26743
26744
26745
26746
26747
26748
26749
26750
26751
26752
26753
26754
26755
26756
26757
26758
26759
26760
26761
26762
26763
26764
26765
26766
26767
26768
26769
26770
26771
26772
26773
26774
26775
26776
26777
26778
26779
26780
// "Name":Value pairs, as generated by TSQLRecord.GetJSONValues(W)
function JSONGetObject(var P: PUTF8Char; ExtractID: PID;
  var EndOfObject: AnsiChar; KeepIDField: boolean): RawUTF8;

/// retrieve the ID/RowID field of a JSON object
// - this function expects this "ID" property to be the FIRST in the
// "Name":Value pairs, as generated by TSQLRecord.GetJSONValues(W)
// - returns TRUE if a ID/RowID>0 has been found, and set ID with the value
function JSONGetID(P: PUTF8Char; out ID: TID): Boolean;

/// fill a TSQLRawBlob from TEXT-encoded blob data
// - blob data can be encoded as SQLite3 BLOB literals (X'53514C697465' e.g.) or
// or Base-64 encoded content ('\uFFF0base64encodedbinary') or plain TEXT
function BlobToTSQLRawBlob(P: PUTF8Char): TSQLRawBlob; overload;

................................................................................
end;

function StartWithQuotedID(P: PUTF8Char; out ID: TID): boolean;
begin
  if PCardinal(P)^ and $ffffdfdf=
      ord('I')+ord('D')shl 8+ord('"')shl 16+ord(':')shl 24 then begin
    SetID(P+4,ID);
    result := ID>0;
    exit;
  end else
  if (PCardinalArray(P)^[0] and $dfdfdfdf=
       ord('R')+ord('O')shl 8+ord('W')shl 16+ord('I')shl 24) and
     (PCardinalArray(P)^[1] and $ffffdf=
       ord('D')+ord('"')shl 8+ord(':')shl 16) then begin
    SetID(P+7,ID);
    result := ID>0;
    exit;
  end;
  ID := 0;
  result := false;
end;

function StartWithID(P: PUTF8Char; out ID: TID): boolean;
begin
  if PCardinal(P)^ and $ffdfdf=
      ord('I')+ord('D')shl 8+ord(':')shl 16 then begin
    SetID(P+3,ID);
    result := ID>0;
    exit;
  end else
  if (PCardinalArray(P)^[0] and $dfdfdfdf=
       ord('R')+ord('O')shl 8+ord('W')shl 16+ord('I')shl 24) and
     (PCardinalArray(P)^[1] and $ffdf=ord('D')+ord(':')shl 8) then begin
    SetID(P+6,ID);
    result := ID>0;
    exit;
  end;
  ID := 0;
  result := false;
end;

function JSONGetID(P: PUTF8Char; out ID: TID): Boolean;

Changes to SQLite3/mORMotDB.pas.

1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
....
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
  const SentData: RawUTF8): TID;
begin
  if (TableModelIndex<0) or (fModel.Tables[TableModelIndex]<>fStoredClass) then
    result := 0 else // avoid GPF
  if fBatchMethod<>mNone then
    if fBatchMethod<>mPOST then
      result := 0 else begin
      JSONGetID(pointer(SentData),result);
      if result=0 then
        result := EngineLockedNextID else
        if result>fEngineLockedMaxID then
          fEngineLockedMaxID := result;
      InternalBatchAdd(SentData,result);
    end else begin
    result := ExecuteFromJSON(SentData,soInsert,0);
    // UpdatedID=0 -> insert with EngineLockedNextID
................................................................................
    F: integer;
    Query: ISQLDBStatement;
begin
  result := 0;
  StorageLock(false,'ExecuteFromJson'); // avoid race condition against max(ID)
  try
    case Occasion of
    soInsert: begin
      JSONGetID(pointer(SentData),InsertedID);
      if InsertedID=0 then // no specified "ID":... field value -> compute next
        InsertedID := EngineLockedNextID else
        if InsertedID>fEngineLockedMaxID then
          fEngineLockedMaxID := InsertedID;
    end;
    soUpdate:
      if UpdatedID<>0 then
        InsertedID := 0 else
        raise ESQLDBException.CreateUTF8('%.ExecuteFromJSON(%,soUpdate,UpdatedID=%)',
          [self,StoredClass,UpdatedID]);
    else raise ESQLDBException.CreateUTF8('%.ExecuteFromJSON(%,Occasion=%)?',
           [self,StoredClass,ToText(Occasion)^]);






|
<







 







|
|
|



<







1249
1250
1251
1252
1253
1254
1255
1256

1257
1258
1259
1260
1261
1262
1263
....
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846

1847
1848
1849
1850
1851
1852
1853
  const SentData: RawUTF8): TID;
begin
  if (TableModelIndex<0) or (fModel.Tables[TableModelIndex]<>fStoredClass) then
    result := 0 else // avoid GPF
  if fBatchMethod<>mNone then
    if fBatchMethod<>mPOST then
      result := 0 else begin
      if not JSONGetID(pointer(SentData),result) then

        result := EngineLockedNextID else
        if result>fEngineLockedMaxID then
          fEngineLockedMaxID := result;
      InternalBatchAdd(SentData,result);
    end else begin
    result := ExecuteFromJSON(SentData,soInsert,0);
    // UpdatedID=0 -> insert with EngineLockedNextID
................................................................................
    F: integer;
    Query: ISQLDBStatement;
begin
  result := 0;
  StorageLock(false,'ExecuteFromJson'); // avoid race condition against max(ID)
  try
    case Occasion of
    soInsert:
      if not JSONGetID(pointer(SentData),InsertedID) then
        // no specified "ID":... field value -> compute next
        InsertedID := EngineLockedNextID else
        if InsertedID>fEngineLockedMaxID then
          fEngineLockedMaxID := InsertedID;

    soUpdate:
      if UpdatedID<>0 then
        InsertedID := 0 else
        raise ESQLDBException.CreateUTF8('%.ExecuteFromJSON(%,soUpdate,UpdatedID=%)',
          [self,StoredClass,UpdatedID]);
    else raise ESQLDBException.CreateUTF8('%.ExecuteFromJSON(%,Occasion=%)?',
           [self,StoredClass,ToText(Occasion)^]);

Changes to SynSelfTests.pas.

9492
9493
9494
9495
9496
9497
9498
9499




9500
9501
9502
9503
9504
9505
9506
begin
  if Pos('TSQLite3Library',Owner.CustomVersions)=0 then
    Owner.CustomVersions := Owner.CustomVersions+#13#10+
      string(sqlite3.ClassName)+' '+string(sqlite3.Version);
  Check(JSONGetID('{"id":123}',id) and (id=123));
  Check(JSONGetID('{"rowid":1234}',id) and (id=1234));
  Check(JSONGetID(' { "id": 123}',id) and (id=123));
  Check(JSONGetID(' { "rowid": 1234}',id) and (id=1234));




  Check(not JSONGetID('{"ide":123}',id));
  Check(not JSONGetID('{"rowide":1234}',id));
  Check(not JSONGetID('{"as":123}',id));
  Check(not JSONGetID('{"s":1234}',id));
  Check(not JSONGetID('"ide":123}',id));
  Check(not JSONGetID('{ rowide":1234}',id));
  if ClassType=TTestMemoryBased then






|
>
>
>
>







9492
9493
9494
9495
9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
9506
9507
9508
9509
9510
begin
  if Pos('TSQLite3Library',Owner.CustomVersions)=0 then
    Owner.CustomVersions := Owner.CustomVersions+#13#10+
      string(sqlite3.ClassName)+' '+string(sqlite3.Version);
  Check(JSONGetID('{"id":123}',id) and (id=123));
  Check(JSONGetID('{"rowid":1234}',id) and (id=1234));
  Check(JSONGetID(' { "id": 123}',id) and (id=123));
  Check(JSONGetID(' { "ROWID": 1234}',id) and (id=1234));
  Check(not JSONGetID('{"id":0}',id));
  Check(not JSONGetID('{"id":-10}',id));
  Check(not JSONGetID('{"id":null}',id));
  Check(not JSONGetID('{"ROWID":null}',id));
  Check(not JSONGetID('{"ide":123}',id));
  Check(not JSONGetID('{"rowide":1234}',id));
  Check(not JSONGetID('{"as":123}',id));
  Check(not JSONGetID('{"s":1234}',id));
  Check(not JSONGetID('"ide":123}',id));
  Check(not JSONGetID('{ rowide":1234}',id));
  if ClassType=TTestMemoryBased then

Changes to SynopseCommit.inc.

1
'1.18.2946'
|
1
'1.18.2947'