mORMot and Open Source friends
Check-in [2d3aef367e]
Not logged in

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

Overview
Comment:data sent to MongoDB from JSON will now recognize strict 'YYYY-MM-DDThh:mm:ss' or 'YYYY-MM-DD' or 'Thh:mm:ss' patterns as betDateTime, as e.g. generated by TTextWriter.AddDateTime() or RecordSaveJSON()
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2d3aef367e454c44d2e203a3159c82bfe10d33a8
User & Date: User 2014-05-24 09:39:20
Context
2014-05-24
14:13
StrLen() function will now use faster SSE2 instructions on supported CPUs check-in: 840adacf7c user: User tags: trunk
09:39
data sent to MongoDB from JSON will now recognize strict 'YYYY-MM-DDThh:mm:ss' or 'YYYY-MM-DD' or 'Thh:mm:ss' patterns as betDateTime, as e.g. generated by TTextWriter.AddDateTime() or RecordSaveJSON() check-in: 2d3aef367e user: User tags: trunk
09:36
added Iso8601CheckAndDecode() function check-in: 447234b233 user: User tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SynMongoDB.pas.

442
443
444
445
446
447
448


449
450
451
452
453
454
455
....
2821
2822
2823
2824
2825
2826
2827










2828
2829
2830
2831
2832
2833
2834
....
3040
3041
3042
3043
3044
3045
3046


3047
3048
3049
3050
3051
3052
3053
....
3098
3099
3100
3101
3102
3103
3104

3105
3106
3107



3108
3109
3110
3111
3112
3113
3114
3115
3116
    procedure BSONWrite(const name: RawUTF8; const value: integer); overload;
    /// write a 64 bit integer value
    procedure BSONWrite(const name: RawUTF8; const value: Int64); overload;
    /// write a string (UTF-8) value
    procedure BSONWrite(const name: RawUTF8; const value: RawUTF8; isJavaScript: boolean=false); overload;
    /// write a string (UTF-8) value from a memory buffer
    procedure BSONWrite(const name: RawUTF8; value: PUTF8Char); overload;


    /// write a binary (BLOB) value
    procedure BSONWrite(const name: RawUTF8; Data: pointer; DataLen: integer); overload;
    /// write an ObjectID value
    procedure BSONWrite(const name: RawUTF8; const value: TBSONObjectID); overload;
    /// write a RegEx value
    procedure BSONWriteRegEx(const name: RawUTF8; const RegEx: RawByteString);
    /// write a data/time value
................................................................................
  BSONWrite(name,betString);
  L := StrLen(Value)+1;
  Write4(L);
  if L=1 then
    Write1(0) else
    Write(value,L);
end;











procedure TBSONWriter.BSONWriteDateTime(const name: RawUTF8; const value: TDateTime);
var UnixTime: Int64;
begin
  UnixTime := DateTimeToUnixMSTime(value);
  BSONWrite(name,betDateTime);
  Write8(UnixTime);
................................................................................
procedure TBSONWriter.BSONWriteFromJSON(const name: RawUTF8; var JSON: PUTF8Char;
  EndOfObject: PUTF8Char; DoNotTryExtendedMongoSyntax: boolean);
var tmp: variant; // we use a local variant for only BSONVariant values
    blob: RawByteString;
    wasString: boolean;
    err: integer;
    Value, Dot: PUTF8Char;


    VDouble: double;
    Kind: TBSONElementType;
begin
  if JSON^ in [#1..' '] then repeat inc(JSON) until not(JSON^ in [#1..' ']);
  if (not DoNotTryExtendedMongoSyntax) and
     BSONVariantType.TryJSONToVariant(JSON,tmp,EndOfObject) then
    // was betDateTime, betObjectID or betRegEx, from strict or extended JSON
................................................................................
        VDouble := GetExtended(Value,err);
        if err=0 then begin // floating-point number
          BSONWrite(name,VDouble);
          exit;
        end;
      end;
      // found no numerical value -> check text value

      if Base64MagicCheckAndDecode(Value,blob) then
        // recognized '\uFFF0base64encodedbinary' pattern
        BSONWrite(name,pointer(blob),length(blob)) else



        // will point to the in-place escaped JSON text
        BSONWrite(name,Value);
    end;
  end;
  if TotalWritten>BSON_MAXDOCUMENTSIZE then
    raise EBSONException.CreateFmt('BSON document size = %d > maximum %d',
      [TotalWritten,BSON_MAXDOCUMENTSIZE]);
end;







>
>







 







>
>
>
>
>
>
>
>
>
>







 







>
>







 







>
|


>
>
>

|







442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
....
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
....
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
....
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
    procedure BSONWrite(const name: RawUTF8; const value: integer); overload;
    /// write a 64 bit integer value
    procedure BSONWrite(const name: RawUTF8; const value: Int64); overload;
    /// write a string (UTF-8) value
    procedure BSONWrite(const name: RawUTF8; const value: RawUTF8; isJavaScript: boolean=false); overload;
    /// write a string (UTF-8) value from a memory buffer
    procedure BSONWrite(const name: RawUTF8; value: PUTF8Char); overload;
    /// write a string (UTF-8) value from a memory buffer
    procedure BSONWriteString(const name: RawUTF8; value: PUTF8Char; valueLen: integer); 
    /// write a binary (BLOB) value
    procedure BSONWrite(const name: RawUTF8; Data: pointer; DataLen: integer); overload;
    /// write an ObjectID value
    procedure BSONWrite(const name: RawUTF8; const value: TBSONObjectID); overload;
    /// write a RegEx value
    procedure BSONWriteRegEx(const name: RawUTF8; const RegEx: RawByteString);
    /// write a data/time value
................................................................................
  BSONWrite(name,betString);
  L := StrLen(Value)+1;
  Write4(L);
  if L=1 then
    Write1(0) else
    Write(value,L);
end;

procedure TBSONWriter.BSONWriteString(const name: RawUTF8; value: PUTF8Char; valueLen: integer);
begin
  BSONWrite(name,betString);
  inc(valueLen);
  Write4(valueLen);
  if valueLen=1 then
    Write1(0) else
    Write(value,valueLen);
end;

procedure TBSONWriter.BSONWriteDateTime(const name: RawUTF8; const value: TDateTime);
var UnixTime: Int64;
begin
  UnixTime := DateTimeToUnixMSTime(value);
  BSONWrite(name,betDateTime);
  Write8(UnixTime);
................................................................................
procedure TBSONWriter.BSONWriteFromJSON(const name: RawUTF8; var JSON: PUTF8Char;
  EndOfObject: PUTF8Char; DoNotTryExtendedMongoSyntax: boolean);
var tmp: variant; // we use a local variant for only BSONVariant values
    blob: RawByteString;
    wasString: boolean;
    err: integer;
    Value, Dot: PUTF8Char;
    ValueLen: integer;
    ValueDateTime: TDateTime;
    VDouble: double;
    Kind: TBSONElementType;
begin
  if JSON^ in [#1..' '] then repeat inc(JSON) until not(JSON^ in [#1..' ']);
  if (not DoNotTryExtendedMongoSyntax) and
     BSONVariantType.TryJSONToVariant(JSON,tmp,EndOfObject) then
    // was betDateTime, betObjectID or betRegEx, from strict or extended JSON
................................................................................
        VDouble := GetExtended(Value,err);
        if err=0 then begin // floating-point number
          BSONWrite(name,VDouble);
          exit;
        end;
      end;
      // found no numerical value -> check text value
      ValueLen := StrLen(Value);
      if Base64MagicCheckAndDecode(Value,ValueLen,blob) then
        // recognized '\uFFF0base64encodedbinary' pattern
        BSONWrite(name,pointer(blob),length(blob)) else
      if Iso8601CheckAndDecode(Value,ValueLen,ValueDateTime) then
        // recognized TTextWriter.AddDateTime() pattern
        BSONWriteDateTime(name,ValueDateTime) else
        // will point to the in-place escaped JSON text
        BSONWriteString(name,Value,ValueLen);
    end;
  end;
  if TotalWritten>BSON_MAXDOCUMENTSIZE then
    raise EBSONException.CreateFmt('BSON document size = %d > maximum %d',
      [TotalWritten,BSON_MAXDOCUMENTSIZE]);
end;

Changes to SynSelfTests.pas.

2594
2595
2596
2597
2598
2599
2600




2601
2602
2603
2604
2605
2606
2607
....
2634
2635
2636
2637
2638
2639
2640













2641
2642
2643
2644
2645
2646
2647
....
5060
5061
5062
5063
5064
5065
5066
5067



5068
5069
5070
5071
5072
5073
5074
procedure TTestLowLevelCommon.Iso8601DateAndTime;
procedure Test(D: TDateTime; Expanded: boolean);
var s,t: RawUTF8;
    E: TDateTime;
    I,J: TTimeLogBits;
begin
  s := DateTimeToIso8601(D,Expanded);




  E := Iso8601ToDateTime(s);
  Check(Abs(D-E)<(1000/MSecsPerDay)); // we allow 999 ms error
  Check(Iso8601ToTimeLog(s)<>0);
  I.From(s);
  t := I.Text(Expanded);
  if t<>s then // we allow error on time = 00:00:00 -> I.Text = just date
    Check(I.Value and (1 shl (6+6+5)-1)=0) else
................................................................................
  CheckSame(IntervalTextToDateTime('-20 06:03:20'),-20.252314,1e-6);
  Check(DateTimeToIso8601Text(IntervalTextToDateTime('+0 06:03:20'))='T06:03:20');
  tmp := DateTimeToIso8601Text(IntervalTextToDateTime('+1 06:03:20'));
  Check(tmp='1899-12-31T06:03:20');
  tmp := DateTimeToIso8601Text(IntervalTextToDateTime('-2 06:03:20'));
  Check(tmp='1899-12-28T06:03:20');
  CheckSame(TimeLogToDateTime(135131870949),41578.477512,1e-5);













end;

procedure TTestLowLevelCommon._IdemPropName;
const abcde: PUTF8Char = 'ABcdE';
      abcdf: PUTF8Char = 'ABcdF';
var WinAnsi: WinAnsiString;
begin
................................................................................
  Check(BSONToJSON(BSONFieldSelector('a,b,c'))='{"a":1,"b":1,"c":1}');
  Check(VariantSaveMongoJSON(BSONVariantFieldSelector(['a','b','c']),modMongoShell)='{a:1,b:1,c:1}');
  Check(VariantSaveMongoJSON(BSONVariantFieldSelector('a,b,c'),modMongoShell)='{a:1,b:1,c:1}');
  o := _Obj(['id',ObjectID(BSONID),'name','John','date',variant(d2)]);
  u := VariantSaveMongoJSON(o,modNoMongo);
  u2 := FormatUTF8('{"id":"%","name":"John","date":"%"}',[BSONID,st]);
  Check(u=u2);
  Check(VariantSaveJson(BSONVariant(u))=u);



  u := VariantSaveMongoJSON(o,modMongoShell);
  Check(u=FormatUTF8('{id:ObjectId("%"),name:"John",date:ISODate("%")}',[BSONID,st]));
  u3 := VariantSaveJson(BSONVariant(u));
  u := VariantSaveJSON(o);
  Check(u=FormatUTF8('{"id":{"$oid":"%"},"name":"John","date":"%"}',[BSONID,st]));
  u := VariantSaveMongoJSON(o,modMongoStrict);
  Check(u=FormatUTF8('{"id":{"$oid":"%"},"name":"John","date":{"$date":"%"}}',[BSONID,st]));






>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>
>
>







2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
....
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
....
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
procedure TTestLowLevelCommon.Iso8601DateAndTime;
procedure Test(D: TDateTime; Expanded: boolean);
var s,t: RawUTF8;
    E: TDateTime;
    I,J: TTimeLogBits;
begin
  s := DateTimeToIso8601(D,Expanded);
  if Expanded then begin
    Check(Iso8601CheckAndDecode(Pointer(s),length(s),E));
    Check(Abs(D-E)<(1000/MSecsPerDay)); // we allow 999 ms error
  end;
  E := Iso8601ToDateTime(s);
  Check(Abs(D-E)<(1000/MSecsPerDay)); // we allow 999 ms error
  Check(Iso8601ToTimeLog(s)<>0);
  I.From(s);
  t := I.Text(Expanded);
  if t<>s then // we allow error on time = 00:00:00 -> I.Text = just date
    Check(I.Value and (1 shl (6+6+5)-1)=0) else
................................................................................
  CheckSame(IntervalTextToDateTime('-20 06:03:20'),-20.252314,1e-6);
  Check(DateTimeToIso8601Text(IntervalTextToDateTime('+0 06:03:20'))='T06:03:20');
  tmp := DateTimeToIso8601Text(IntervalTextToDateTime('+1 06:03:20'));
  Check(tmp='1899-12-31T06:03:20');
  tmp := DateTimeToIso8601Text(IntervalTextToDateTime('-2 06:03:20'));
  Check(tmp='1899-12-28T06:03:20');
  CheckSame(TimeLogToDateTime(135131870949),41578.477512,1e-5);
  tmp := '1982-10-30T06:03:20';
  Check(Iso8601CheckAndDecode(Pointer(tmp),length(tmp),D));
  Check(DateTimeToIso8601(D,true)=tmp);
  tmp := '1982-10-30';
  Check(Iso8601CheckAndDecode(Pointer(tmp),length(tmp),D));
  Check(DateToIso8601(D,true)=tmp);
  tmp := 'T06:03:20';
  Check(Iso8601CheckAndDecode(Pointer(tmp),length(tmp),D));
  Check(TimeToIso8601(D,true)=tmp);
  tmp := '1982-10-30 06:03:20';
  Check(not Iso8601CheckAndDecode(Pointer(tmp),length(tmp),D));
  tmp := 'T06:03:2a';
  Check(not Iso8601CheckAndDecode(Pointer(tmp),length(tmp),D));
end;

procedure TTestLowLevelCommon._IdemPropName;
const abcde: PUTF8Char = 'ABcdE';
      abcdf: PUTF8Char = 'ABcdF';
var WinAnsi: WinAnsiString;
begin
................................................................................
  Check(BSONToJSON(BSONFieldSelector('a,b,c'))='{"a":1,"b":1,"c":1}');
  Check(VariantSaveMongoJSON(BSONVariantFieldSelector(['a','b','c']),modMongoShell)='{a:1,b:1,c:1}');
  Check(VariantSaveMongoJSON(BSONVariantFieldSelector('a,b,c'),modMongoShell)='{a:1,b:1,c:1}');
  o := _Obj(['id',ObjectID(BSONID),'name','John','date',variant(d2)]);
  u := VariantSaveMongoJSON(o,modNoMongo);
  u2 := FormatUTF8('{"id":"%","name":"John","date":"%"}',[BSONID,st]);
  Check(u=u2);
  u3 := VariantSaveJson(BSONVariant(u));
  Check(u3=FormatUTF8('{"id":"%","name":"John","date":{"$date":"%"}}',[BSONID,st]));
  u3 := VariantSaveMongoJSON(BSONVariant(u),modNoMongo);
  Check(u3=u);
  u := VariantSaveMongoJSON(o,modMongoShell);
  Check(u=FormatUTF8('{id:ObjectId("%"),name:"John",date:ISODate("%")}',[BSONID,st]));
  u3 := VariantSaveJson(BSONVariant(u));
  u := VariantSaveJSON(o);
  Check(u=FormatUTF8('{"id":{"$oid":"%"},"name":"John","date":"%"}',[BSONID,st]));
  u := VariantSaveMongoJSON(o,modMongoStrict);
  Check(u=FormatUTF8('{"id":{"$oid":"%"},"name":"John","date":{"$date":"%"}}',[BSONID,st]));