From be5bada4d039adbd7608cb087dbdad948374548c Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 24 Apr 2023 23:14:34 +0000 Subject: [PATCH 01/37] Allow trailing commas in objects and arrays of JSON. FossilOrigin-Name: 4031b231c223db598b45dbd192b027a99a9b82a981d43c75f723a3fb06720b82 --- manifest | 18 ++++++++++-------- manifest.uuid | 2 +- src/json.c | 4 ++-- test/json101.test | 12 ++++++++++++ 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 0509728ae1..d92e1a5b2b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\scompile-time\sdetection\sof\sarchitecture\sbyte-order\sin\sthe\sRTREE\nextension\sso\sthat\sit\sis\saligned\swith\sthe\slatest\senhancements\sin\sthe\score. -D 2023-04-24T19:23:42.196 +C Allow\strailing\scommas\sin\sobjects\sand\sarrays\sof\sJSON. +D 2023-04-24T23:14:34.394 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c edae65fe1f66ce8b1e7fa6eb036a3d8cf525dacd91d58f12068e80a81ac34f61 +F src/json.c ebb5827e6ab6e527c1cafdc857858cc38d822480594ba054a7b26d1493293b4b F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -1249,7 +1249,7 @@ F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ff F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9e1946 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa -F test/json101.test c7707ee623c57a24845ef260d4388a6fade1eb294e450caadd7f1d9ced19dea7 +F test/json101.test 7241ab038c75ba780b009c6f37e4b660e5e63d1d74c110336a4779a2db8653f7 F test/json102.test 327e77275f338c028faefa2da5164daf6b142a165e3015ff2a6e4251ddc6a0ac F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 @@ -2059,9 +2059,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e79c95fc130fc302719690eb6391d96070aff825b2b51ef6c4ad459d9a8918d7 -Q +491bd51da5e2069078d7295396d80d2ccdc3a5871714fef948076939174e6acd -R 0ddcb2c385e38b7edc9b293e66b74615 +P 122431d3a7267ec83768316ab146c0557fb6c0577a4a47ac6ed3d7aa6811ca9a +R 9daab91f5de799face026ab747c90d1d +T *branch * json5 +T *sym-json5 * +T -sym-trunk * U drh -Z 660ef923c98b6f83ec3ee30adf820b34 +Z e1af4267df4cb005f405e96353c52d96 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 07be9e66cc..ef3e218689 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -122431d3a7267ec83768316ab146c0557fb6c0577a4a47ac6ed3d7aa6811ca9a \ No newline at end of file +4031b231c223db598b45dbd192b027a99a9b82a981d43c75f723a3fb06720b82 \ No newline at end of file diff --git a/src/json.c b/src/json.c index aa8bf64c86..4c68608deb 100644 --- a/src/json.c +++ b/src/json.c @@ -804,7 +804,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ x = jsonParseValue(pParse, j); if( x<0 ){ pParse->iDepth--; - if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1; + if( x==(-2) ) break; return -1; } if( pParse->oom ) return -1; @@ -838,7 +838,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ){ - if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1; + if( x==(-3) ) break; return -1; } j = x; diff --git a/test/json101.test b/test/json101.test index 596a084968..58559aafe5 100644 --- a/test/json101.test +++ b/test/json101.test @@ -308,12 +308,24 @@ do_execsql_test json-5.8 { do_execsql_test json-6.1 { SELECT json_valid('{"a":55,"b":72,}'); +} {1} +do_execsql_test json-6.1b { + SELECT json_valid('{"a":55,"b":72 , }'); +} {1} +do_execsql_test json-6.1c { + SELECT json_valid('{"a":55,"b":72,,}'); } {0} do_execsql_test json-6.2 { SELECT json_valid('{"a":55,"b":72}'); } {1} do_execsql_test json-6.3 { SELECT json_valid('["a",55,"b",72,]'); +} {1} +do_execsql_test json-6.3b { + SELECT json_valid('["a",55,"b",72 , ]'); +} {1} +do_execsql_test json-6.3c { + SELECT json_valid('["a",55,"b",72,,]'); } {0} do_execsql_test json-6.4 { SELECT json_valid('["a",55,"b",72]'); From 058f3dbb270e9784f72cf9deb7f176fa194e56fe Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 25 Apr 2023 21:24:20 +0000 Subject: [PATCH 02/37] The json_valid() function only returns true for pure JSON. JSON5 (or at least that subset of JSON5 that has been so far implemented) is accepted by all routines, but json_valid() still returns false for JSON5 inputs. The new json_valid5(X) routine returns true or false if X is or is not valid JSON5. All of this is experimental and subject to change. FossilOrigin-Name: 5d33ab77800765c8b3a13ffcc02ba8a348d71b2b425924560418b517d723494d --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/json.c | 26 ++++++++++++++++++++++---- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index d92e1a5b2b..b5b8818efe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\strailing\scommas\sin\sobjects\sand\sarrays\sof\sJSON. -D 2023-04-24T23:14:34.394 +C The\sjson_valid()\sfunction\sonly\sreturns\strue\sfor\spure\sJSON.\s\sJSON5\s(or\nat\sleast\sthat\ssubset\sof\sJSON5\sthat\shas\sbeen\sso\sfar\simplemented)\sis\saccepted\nby\sall\sroutines,\sbut\sjson_valid()\sstill\sreturns\sfalse\sfor\sJSON5\sinputs.\nThe\snew\sjson_valid5(X)\sroutine\sreturns\strue\sor\sfalse\sif\sX\sis\sor\sis\snot\svalid\nJSON5.\s\sAll\sof\sthis\sis\sexperimental\sand\ssubject\sto\schange. +D 2023-04-25T21:24:20.117 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c ebb5827e6ab6e527c1cafdc857858cc38d822480594ba054a7b26d1493293b4b +F src/json.c 1d049d073c0197d55e0046877cb87ad7b462419748cf08663d3baf04db676efb F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2059,11 +2059,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 122431d3a7267ec83768316ab146c0557fb6c0577a4a47ac6ed3d7aa6811ca9a -R 9daab91f5de799face026ab747c90d1d -T *branch * json5 -T *sym-json5 * -T -sym-trunk * +P 4031b231c223db598b45dbd192b027a99a9b82a981d43c75f723a3fb06720b82 +R 4aa06ba3d655f30dc5fe96279fd3a8e6 U drh -Z e1af4267df4cb005f405e96353c52d96 +Z 9b40834215aad90a070495da8b812bef # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ef3e218689..b9efb7f40f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4031b231c223db598b45dbd192b027a99a9b82a981d43c75f723a3fb06720b82 \ No newline at end of file +5d33ab77800765c8b3a13ffcc02ba8a348d71b2b425924560418b517d723494d \ No newline at end of file diff --git a/src/json.c b/src/json.c index 4c68608deb..b201fdde5c 100644 --- a/src/json.c +++ b/src/json.c @@ -130,9 +130,10 @@ struct JsonParse { JsonNode *aNode; /* Array of nodes containing the parse */ const char *zJson; /* Original JSON string */ u32 *aUp; /* Index of parent of each node */ - u8 oom; /* Set to true if out of memory */ - u8 nErr; /* Number of errors seen */ u16 iDepth; /* Nesting depth */ + u8 nErr; /* Number of errors seen */ + u8 oom; /* Set to true if out of memory */ + u8 has5; /* True if input has JSON5 features */ int nJson; /* Length of the zJson string in bytes */ u32 iHold; /* Replace cache line with the lowest iHold value */ }; @@ -804,7 +805,10 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ x = jsonParseValue(pParse, j); if( x<0 ){ pParse->iDepth--; - if( x==(-2) ) break; + if( x==(-2) ){ + if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; + break; + } return -1; } if( pParse->oom ) return -1; @@ -838,7 +842,10 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ){ - if( x==(-3) ) break; + if( x==(-3) ){ + if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; + break; + } return -1; } j = x; @@ -1997,6 +2004,16 @@ static void jsonValidFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv +){ + JsonParse *p; /* The parse */ + UNUSED_PARAMETER(argc); + p = jsonParseCached(ctx, argv, 0); + sqlite3_result_int(ctx, p!=0 && p->has5==0); +} +static void jsonValid5Func( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv ){ JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); @@ -2725,6 +2742,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_type, 1, 0, jsonTypeFunc), JFUNCTION(json_type, 2, 0, jsonTypeFunc), JFUNCTION(json_valid, 1, 0, jsonValidFunc), + JFUNCTION(json_valid5, 1, 0, jsonValid5Func), #if SQLITE_DEBUG JFUNCTION(json_parse, 1, 0, jsonParseFunc), JFUNCTION(json_test1, 1, 0, jsonTest1Func), From 02efa7be9610963288319eb0f79a1a128d24bd5f Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 26 Apr 2023 13:25:30 +0000 Subject: [PATCH 03/37] Add scripts for JSON performance testing. FossilOrigin-Name: 3051d6a7c12cbf86634d8fab64f3f3bc9a1eb184260e56a82eb543cb634ba7ef --- manifest | 14 +- manifest.uuid | 2 +- test/json/README.md | 26 +++ test/json/json-generator.tcl | 401 ++++++++++++++++++++++++++++++++++ test/json/json-q1.txt | 4 + test/json/json-speed-check.sh | 79 +++++++ 6 files changed, 520 insertions(+), 6 deletions(-) create mode 100644 test/json/README.md create mode 100644 test/json/json-generator.tcl create mode 100644 test/json/json-q1.txt create mode 100755 test/json/json-speed-check.sh diff --git a/manifest b/manifest index b5b8818efe..cc417d08a7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sjson_valid()\sfunction\sonly\sreturns\strue\sfor\spure\sJSON.\s\sJSON5\s(or\nat\sleast\sthat\ssubset\sof\sJSON5\sthat\shas\sbeen\sso\sfar\simplemented)\sis\saccepted\nby\sall\sroutines,\sbut\sjson_valid()\sstill\sreturns\sfalse\sfor\sJSON5\sinputs.\nThe\snew\sjson_valid5(X)\sroutine\sreturns\strue\sor\sfalse\sif\sX\sis\sor\sis\snot\svalid\nJSON5.\s\sAll\sof\sthis\sis\sexperimental\sand\ssubject\sto\schange. -D 2023-04-25T21:24:20.117 +C Add\sscripts\sfor\sJSON\sperformance\stesting. +D 2023-04-26T13:25:30.498 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1249,6 +1249,10 @@ F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ff F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9e1946 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa +F test/json/README.md 9d117e0d6da9eee96de2fc8e32b603208b0e5b460ff99e5de3407bb713f0bb5c +F test/json/json-generator.tcl 229bd293f1865f787c160886cadd282631721925cca2947aaa54bbcd7f65cef7 +F test/json/json-q1.txt 335a7c8ab291d354f33b7decc9559e99a2823d4142291c4be7aa339a631f3c2d +F test/json/json-speed-check.sh b1544c21e6f04e9c3ecc3caa858e0607703a4ed29d4d8b631a736d28e298ed9d x F test/json101.test 7241ab038c75ba780b009c6f37e4b660e5e63d1d74c110336a4779a2db8653f7 F test/json102.test 327e77275f338c028faefa2da5164daf6b142a165e3015ff2a6e4251ddc6a0ac F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe @@ -2059,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4031b231c223db598b45dbd192b027a99a9b82a981d43c75f723a3fb06720b82 -R 4aa06ba3d655f30dc5fe96279fd3a8e6 +P 5d33ab77800765c8b3a13ffcc02ba8a348d71b2b425924560418b517d723494d +R 370adb66bc2173cabe35b75b43054560 U drh -Z 9b40834215aad90a070495da8b812bef +Z 49f1c31b7a1708965e204247b5ad4fd6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b9efb7f40f..96d71859fb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5d33ab77800765c8b3a13ffcc02ba8a348d71b2b425924560418b517d723494d \ No newline at end of file +3051d6a7c12cbf86634d8fab64f3f3bc9a1eb184260e56a82eb543cb634ba7ef \ No newline at end of file diff --git a/test/json/README.md b/test/json/README.md new file mode 100644 index 0000000000..94f5516c06 --- /dev/null +++ b/test/json/README.md @@ -0,0 +1,26 @@ +The files in this subdirectory are used to help measure the performance +of the SQLite JSON parser. + +# 1.0 Prerequisites + + 1. Valgrind + + 2. Fossil + +# 2.0 Setup + + 1. Run: "`tclsh json-generator.tcl | sqlite3 json100mb.db`" to create + the 100 megabyte test database. Do this so that the "json100mb.db" + file lands in the same directory as the json-generator.tcl script. + + 2. Build the baseline sqlite3.c file. ("`make sqlite3.c`") + + 3. Run "`sh json-speed-check-1.sh trunk`". This creates the baseline + profile in "jout-trunk.txt". + +# 3.0 Testing + + 1. Build the sqlite3.c to be tested. + + 2. Run "`sh json-speed-check-1.sh x1`". The profile output will appear + in jout-x1.txt. Substitute any label you want in place of "x1". diff --git a/test/json/json-generator.tcl b/test/json/json-generator.tcl new file mode 100644 index 0000000000..e99d6bccb0 --- /dev/null +++ b/test/json/json-generator.tcl @@ -0,0 +1,401 @@ +#!/usr/bin/tclsh +# +# Generate SQL that will populate an SQLite database with about 100 megabytes +# of pseudo-random JSON text. +# +# tclsh json-generator.tcl | sqlite3 json110mb.db +# +# srand() is used to initialize the random seed so that the same JSON +# is generated for every run. +# +expr srand(12345678) +set wordlist { +ability able abroad access account act + action active actor add address adept + adroit advance advice affect age ageless + agency agent agile agree air airfare + airline airport alert almond alpha always + amend amount amplify analyst anchor angel + angelic angle ankle annual answer antique + anybody anyhow appeal apple apricot apt + area argon arm army arrival arsenic + art artful article arugula aside ask + aspect assist assume atom atone attempt + author autumn average avocado award awl + azure back bacon bag bagel bake + baker balance ball balloon bamboo banana + band banjo bank barium base basil + basin basis basket bass bat bath + battery beach beak bean bear bearcub + beauty beef beet beige being bell + belly belt bench bend benefit best + beta better beyond bicycle bid big + bike bill bird biscuit bismuth bisque + bit black blank blest blind bliss + block bloom blue board boat body + bokchoy bone bonus book bookish boot + border boron boss bossy bottle bottom + bow bowl bowtie box brain brainy + branch brave bravely bread break breath + breezy brick bridge brie brief briefly + bright broad broil bromine bronze brother + brow brown brush buddy budget buffalo + bug bugle bull bunch burger burly + burrito bus busy butter button buy + buyer byte cab cabbage cabinet cable + cadet cadmium caesium cake calcium caliper + call caller calm calmly camera camp + can canary cancel candle candy cap + capable caper capital captain car carbon + card care career careful carp carpet + carrot carry case cash cassava casual + cat catch catfish catsear catsup cause + cave celery cell century chain chair + chalk chance change channel chapter chard + charge charity chart check cheddar cheery + cheese chicken chicory chiffon child chin + chip chives choice chowder chum church + circle city claim clam class classic + classy clay clean cleaner clear clearly + clerk click client climate clock clorine + closet clothes cloud clown club clue + cluster coach coast coat cobbler cobolt + cod code coffee colby cold collar + college comb combine comet comfort command + comment common company complex concept concern + concert conduit consist contact contest context + control convert cook cookie copilot copper + copy coral cordial corn corner corny + correct cost count counter country county + couple courage course court cover cow + cowbird crab crack craft crash crazy + cream credit creek cress crevice crew + crimson croaker crop cross crowd cube + cuckoo cuisine culture cup current curve + cut cyan cycle dagger daily dance + dare darter data date day daylily + deal dear dearly debate debit decade + decimal deep deft deftly degree delay + deluxe deposit depth design desk detail + device dew diamond diet dig dill + dinner dip direct dirt dish disk + display diver divide divine doctor dodger + donut door dot double dough draft + drag dragon drama draw drawer drawing + dream drill drink drive driver drop + drum dry dryer drywall duck due + dump dusk dust duty dye eagle + ear earring earth ease east easy + eat economy edge editor eel effect + effort egg eight elbow elegant element + elf elk email emerald employ end + endive endless energy engine enjoy enter + entry equal equip error escape essay + eternal evening event exam example excuse + exit expert extent extreme eye face + fact factor factual fail failure fair + fajita fall family fan fang farm + farmer fat fault feature feed feel + feeling fench fennel festive few fiber + field fig figure file fill film + filter final finance finding finger finish + fire fish fishing fit fitting five + fix flier flight floor floral florine + flour flow flower fly flying focus + fold folding food foot force forest + forever forgive form formal format fortune + forum frame free freedom freely fresh + friend frog front fruit fuchsia fuel + fun funny future gain galaxy gallium + game gamma gap garage garden garlic + gas gate gather gauge gear gem + gene general gentle gently gherkin ghost + gift give glad glass gleeful glossy + glove glue goal goat goby gold + goldeye golf good gouda goulash gourd + grab grace grade gram grand grape + grapes grass gravy gray great green + grits grocery ground group grouper grout + growth guard guave guess guest guide + guitar gumbo guppy habit hacksaw haddock + hafnium hagfish hair half halibut hall + hammer hand handle handy hanger happy + hat havarti hay haybale head health + healthy hearing heart hearty heat heavy + heel height helium hello help helpful + herald herring hide high highly highway + hill hip hipster hire history hit + hoki hold hole holiday holly home + honest honey hook hope hopeful horizon + horn horse host hotel hour house + housing human humane humor hunt hurry + ice icecube icefish icy idea ideal + image impact impress inch income indigo + initial inkpen insect inside intense invite + iodine iridium iron island issue item + ivory jacket jargon javelin jello jelly + jewel job jocund join joint joke + jovial joy joyful joyous judge juice + jump junior jury just justice kale + keel keep kelp ketchup key keyhole + keyway khaki kick kid kidney kiloohm + kind kindly king kitchen kite kiwi + knee knife krill krypton kumquat lab + lace lack ladder lake lamp lamprey + land laser laugh law lawn lawyer + layer lead leader leading leaf leafy + league leather leave lecture leek leg + lemon length lentil lesson let letter + lettuce level library life lift light + lily lime limit line linen link + lip list listen lithium lively living + lizard load loan lobster local lock + log long longfin look lotus love + lovely loving low lucid luck luffa + lunch lung machine magenta magnet mail + main major make mall manager mango + manner many map march market maroon + martian master match math matter maximum + maybe meal meaning meat media medium + meet meeting melody melon member memory + mention menu mercury merry mess message + messy metal meter method micron middle + might mile milk mind mine minimum + minnow minor mint minute mirror miss + mission misty mix mixer mixture mobile + mode model moment monitor monk month + moon moray morning most motor mouse + mouth move mover movie much mud + mudfish muffin mullet munster muon muscle + music mustard nail name nation native + natural nature navy neat neatly nebula + neck needle neon nerve net network + neutron news nibble nice nickel night + niobium nobody noise noodle normal north + nose note nothing notice nova novel + number nurse nursery oar object offer + office officer oil okay okra old + olive one onion open opening opinion + option orange orbit orchid order oregano + other ounce outcome outside oven owner + oxygen oyster pace pack package page + pager paint pair pale pan pancake + papaya paper pardon parent park parking + parsley parsnip part partner party pass + passage past pasta path patient pattern + pause pay pea peace peach peacock + peahen peak peanut pear pearl pen + penalty pencil pension people pepper perch + perfect period permit person phase phone + photo phrase physics piano pick picture + pie piece pigeon pike pilot pin + pink pinkie pious pipe pitch pizza + place plan plane planet plant planter + plastic plate play player playful plenty + pliers plum pod poem poet poetry + point police policy pollock pony pool + pop popover poptart pork port portal + post pot potato pound powder power + present press price pride primary print + prior private prize problem process produce + product profile profit program project promise + prompt proof proper protein proton public + puff puffer pull pumpkin pup pupfish + pure purple purpose push put quality + quark quarter quiet quill quit quote + rabbit raccoon race radiant radio radish + radium radon rain rainbow raise ramp + ranch range rasp rate ratio ray + razor reach read reading real reality + reason recipe record recover red redeem + reed reef refuse region regret regular + relaxed release relief relish remote remove + rent repair repeat reply report request + reserve resist resolve resort rest result + return reveal review reward ribbon rice + rich ride ridge right ring rise + risk river rivet road roast rock + rocket role roll roof room rope + rose rough roughy round row royal + rub ruby rudder ruin rule run + runner rush rust sacred saddle safe + safety sail salad salami sale salmon + salt sample sand sander sandy sauce + save saving saw scale scampi scene + scheme school score screen script sea + search season seat second secret sector + seemly self sell senate senior sense + series serve set shake shape share + shark shell shift shine shiny ship + shock shoe shoot shop shovel show + side sign signal silk silly silver + simple sing singer single sink site + size skill skin sky slate sleep + sleepy slice slide slip smart smell + smelt smile smoke smooth snap snipe + snow snowy sock socket sodium soft + softly soil sole solid song sorrel + sort soul sound soup source south + space spare speech speed spell spend + sphere spice spider spirit spite split + spoon sport spot spray spread spring + squab square squash stable staff stage + stand staple star start state status + stay steak steel step stern stew + stick still stock stone stop store + storm story strain street stress strike + string stroke strong studio study stuff + style sugar suit sulfur summer sun + sunny sunset super superb surf survey + sweet swim swing switch symbol system + table tackle tail tale talk tan + tank tap tape target task taste + tau tea teach teal team tear + tell ten tender tennis tent term + test tetra text thanks theme theory + thing think thread throat thumb ticket + tidy tie tiger till time timely + tin tip title toast today toe + tomato tone tongue tool tooth top + topic total touch tough tour towel + tower town track trade train trash + travel tray treat tree trick trip + trout trowel truck trupet trust truth + try tube tuna tune turf turkey + turn turnip tutor tux tweet twist + two type union unique unit upbeat + upper use useful user usual valley + value van vase vast veil vein + velvet verse very vessel vest video + view violet visit visual vivid voice + volume vowel voyage waffle wait wake + walk wall warm warmth wasabi wash + watch water wave wax way wealth + wear web wedge week weekly weight + west whale what wheat wheel when + where while who whole why will + win wind window wing winner winter + wire wish witty wolf wonder wood + wool woolly word work worker world + worry worth worthy wrap wrench wrist + writer xenon yak yam yard yarrow + year yearly yellow yew yogurt young + youth zebra zephyr zinc zone zoo +} +set nwordlist [llength $wordlist] + +proc random_char {} { + return [string index \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + [expr {int(rand()*52)}]] +} +proc random_label {} { + set label [random_char] + while {rand()>0.8} { + append label [random_char] + } + if {rand()>0.9} {append label -} + append label [format %d [expr {int(rand()*100)}]] + return $label +} +proc random_numeric {} { + set n [expr {(rand()*2-1.0)*1e6}] + switch [expr {int(rand()*6)}] { + 0 {set format %.3f} + 1 {set format %.6E} + 2 {set format %.4e} + default {set format %g} + } + return [format $format $n] +} + + +proc random_json {limit indent} { + global nwordlist wordlist + set res {} + if {$indent==0 || ($limit>0 && rand()>0.5)} { + incr limit -1 + incr indent 2 + set n [expr {int(rand()*5)+1}] + if {$n==5} {incr n [expr {int(rand()*10)}]} + if {rand()>0.5} { + set res \173\n + for {set i 0} {$i<$n} {incr i} { + append res [string repeat { } $indent] + if {rand()>0.8} { + if {rand()>0.5} { + set sep ":\n [string repeat { } $indent]" + } else { + set sep " : " + } + } else { + set sep : + } + append res \"[random_label]\"$sep[random_json $limit $indent] + if {$i<$n-1} {append res ,} + append res \n + } + incr indent -2 + append res [string repeat { } $indent] + append res \175 + return $res + } else { + set res \[\n + for {set i 0} {$i<$n} {incr i} { + append res [string repeat { } $indent] + append res [random_json $limit $indent] + if {$i<$n-1} {append res ,} + append res \n + } + incr indent -2 + append res [string repeat { } $indent] + append res \] + return $res + } + } elseif {rand()>0.9} { + if {rand()>0.7} {return "true"} + if {rand()>0.5} {return "false"} + return "null" + } elseif {rand()>0.5} { + return [random_numeric] + } else { + set res \" + set n [expr {int(rand()*4)+1}] + if {$n>=4} {set n [expr {$n+int(rand()*6)}]} + for {set i 0} {$i<$n} {incr i} { + if {rand()<0.05} { + set w [random_numeric] + } else { + set k [expr {int(rand()*$nwordlist)}] + set w [lindex $wordlist $k] + } + if {rand()<0.07} { + set w \\\"$w\\\" + } + if {$i<$n-1} { + switch [expr {int(rand()*9)}] { + 0 {set sp {, }} + 1 {set sp "\\n "} + 2 {set sp "-"} + default {set sp { }} + } + append res $w$sp + } else { + append res $w + if {rand()<0.2} {append res .} + } + } + return $res\" + } +} + +puts "CREATE TABLE IF NOT EXISTS data1(x JSON);" +puts "BEGIN;" +set sz 0 +for {set i 0} {$sz<100000000} {incr i} { + set j [random_json 7 0] + incr sz [string length $j] + puts "INSERT INTO data1(x) VALUES('$j');" +} +puts "COMMIT;" +puts "SELECT sum(length(x)) FROM data1;" diff --git a/test/json/json-q1.txt b/test/json/json-q1.txt new file mode 100644 index 0000000000..0395f0c061 --- /dev/null +++ b/test/json/json-q1.txt @@ -0,0 +1,4 @@ +.mode qbox +.timer on +.param set $label 'q87' +SELECT rowid, x->>$label FROM data1 WHERE x->>$label IS NOT NULL; diff --git a/test/json/json-speed-check.sh b/test/json/json-speed-check.sh new file mode 100755 index 0000000000..01147c18be --- /dev/null +++ b/test/json/json-speed-check.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# +# This is a template for a script used for day-to-day size and +# performance monitoring of SQLite. Typical usage: +# +# sh speed-check.sh trunk # Baseline measurement of trunk +# sh speed-check.sh x1 # Measure some experimental change +# fossil xdiff --tk jout-trunk.txt jout-x1.txt # View chanages +# +# There are multiple output files, all with a base name given by +# the first argument: +# +# summary-$BASE.txt # Copy of standard output +# jout-$BASE.txt # cachegrind output +# explain-$BASE.txt # EXPLAIN listings (only with --explain) +# +if test "$1" = "" +then + echo "Usage: $0 OUTPUTFILE [OPTIONS]" + exit +fi +NAME=$1 +shift +#CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5" +CC_OPTS="-DSQLITE_ENABLE_MEMSYS5" +CC=gcc +LEAN_OPTS="-DSQLITE_THREADSAFE=0" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_MEMSTATUS=0" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_LIKE_DOESNT_MATCH_BLOBS" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_MAX_EXPR_DEPTH=0" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DECLTYPE" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DEPRECATED" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_PROGRESS_CALLBACK" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_SHARED_CACHE" +LEAN_OPTS="$LEAN_OPTS -DSQLITE_USE_ALLOCA" +BASELINE="trunk" +doExplain=0 +doCachegrind=1 +doVdbeProfile=0 +doWal=1 +doDiff=1 +while test "$1" != ""; do + case $1 in + --nodiff) + doDiff=0 + ;; + --lean) + CC_OPTS="$CC_OPTS $LEAN_OPTS" + ;; + --clang) + CC=clang + ;; + --gcc7) + CC=gcc-7 + ;; + -*) + CC_OPTS="$CC_OPTS $1" + ;; + *) + BASELINE=$1 + ;; + esac + shift +done +echo "NAME = $NAME" | tee summary-$NAME.txt +echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt +rm -f cachegrind.out.* jsonshell +$CC -g -Os -Wall -I. $CC_OPTS ./shell.c ./sqlite3.c -o jsonshell -ldl -lpthread +ls -l jsonshell | tee -a summary-$NAME.txt +home=/home/drh/sqlite/json-perf +valgrind --tool=cachegrind ./jsonshell $home/json100mb.db <$home/json-q1.txt \ + 2>&1 | tee -a summary-$NAME.txt +cg_anno.tcl cachegrind.out.* >jout-$NAME.txt +echo '*****************************************************' >>jout-$NAME.txt +sed 's/^[0-9=-]\{9\}/==00000==/' summary-$NAME.txt >>jout-$NAME.txt +if test "$NAME" != "$BASELINE"; then + fossil xdiff --tk -c 20 jout-$BASELINE.txt jout-$NAME.txt +fi From ddc8b9ee0e3bccd6e488503750b31de5c672250d Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 26 Apr 2023 13:52:20 +0000 Subject: [PATCH 04/37] Fix to the json-speed-check.sh script. FossilOrigin-Name: d839c9544d7f28c1abc779eb2d40f95c1a9386984656fbd29d19b1e7830171bc --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/json/json-speed-check.sh | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index cc417d08a7..4f17dbdff9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sscripts\sfor\sJSON\sperformance\stesting. -D 2023-04-26T13:25:30.498 +C Fix\sto\sthe\sjson-speed-check.sh\sscript. +D 2023-04-26T13:52:20.152 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1252,7 +1252,7 @@ F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json/README.md 9d117e0d6da9eee96de2fc8e32b603208b0e5b460ff99e5de3407bb713f0bb5c F test/json/json-generator.tcl 229bd293f1865f787c160886cadd282631721925cca2947aaa54bbcd7f65cef7 F test/json/json-q1.txt 335a7c8ab291d354f33b7decc9559e99a2823d4142291c4be7aa339a631f3c2d -F test/json/json-speed-check.sh b1544c21e6f04e9c3ecc3caa858e0607703a4ed29d4d8b631a736d28e298ed9d x +F test/json/json-speed-check.sh 362f9c5c4a69c6f2c6fa98f538fb2e07e084dc0bef74f8bbd92cc1666e8415e5 x F test/json101.test 7241ab038c75ba780b009c6f37e4b660e5e63d1d74c110336a4779a2db8653f7 F test/json102.test 327e77275f338c028faefa2da5164daf6b142a165e3015ff2a6e4251ddc6a0ac F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5d33ab77800765c8b3a13ffcc02ba8a348d71b2b425924560418b517d723494d -R 370adb66bc2173cabe35b75b43054560 +P 3051d6a7c12cbf86634d8fab64f3f3bc9a1eb184260e56a82eb543cb634ba7ef +R 76520c4f968a15591ad7c027fe9539e0 U drh -Z 49f1c31b7a1708965e204247b5ad4fd6 +Z 781a4e98041d9791d0ec4084972b315b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 96d71859fb..d8bd0e1a4d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3051d6a7c12cbf86634d8fab64f3f3bc9a1eb184260e56a82eb543cb634ba7ef \ No newline at end of file +d839c9544d7f28c1abc779eb2d40f95c1a9386984656fbd29d19b1e7830171bc \ No newline at end of file diff --git a/test/json/json-speed-check.sh b/test/json/json-speed-check.sh index 01147c18be..343f830bda 100755 --- a/test/json/json-speed-check.sh +++ b/test/json/json-speed-check.sh @@ -68,7 +68,8 @@ echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt rm -f cachegrind.out.* jsonshell $CC -g -Os -Wall -I. $CC_OPTS ./shell.c ./sqlite3.c -o jsonshell -ldl -lpthread ls -l jsonshell | tee -a summary-$NAME.txt -home=/home/drh/sqlite/json-perf +home=`echo $0 | sed -e 's,/[^/]*$,,'` +echo ./jsonshell $home/json100mb.db "<$home/json-q1.txt" valgrind --tool=cachegrind ./jsonshell $home/json100mb.db <$home/json-q1.txt \ 2>&1 | tee -a summary-$NAME.txt cg_anno.tcl cachegrind.out.* >jout-$NAME.txt From f62518f65d0896d77fece571bfe63038267a9b15 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 26 Apr 2023 15:19:19 +0000 Subject: [PATCH 05/37] Work toward implementing JSON5 whitespace. Untested and incomplete. FossilOrigin-Name: d262c059455ebe0650a45a6c1c04d1baf9609c635df352732dd192426e1bdc39 --- manifest | 14 +++--- manifest.uuid | 2 +- src/json.c | 126 +++++++++++++++++++++++++++++++++++++++++++++- test/json101.test | 37 ++++++++------ 4 files changed, 156 insertions(+), 23 deletions(-) diff --git a/manifest b/manifest index 4f17dbdff9..a607ef397a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sto\sthe\sjson-speed-check.sh\sscript. -D 2023-04-26T13:52:20.152 +C Work\stoward\simplementing\sJSON5\swhitespace.\s\sUntested\sand\sincomplete. +D 2023-04-26T15:19:19.822 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 1d049d073c0197d55e0046877cb87ad7b462419748cf08663d3baf04db676efb +F src/json.c d0f9a0c630bdea3721e28b5ba2fce119c0e732ad48f6674a8f1644f2ec45ffb1 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -1253,7 +1253,7 @@ F test/json/README.md 9d117e0d6da9eee96de2fc8e32b603208b0e5b460ff99e5de3407bb713 F test/json/json-generator.tcl 229bd293f1865f787c160886cadd282631721925cca2947aaa54bbcd7f65cef7 F test/json/json-q1.txt 335a7c8ab291d354f33b7decc9559e99a2823d4142291c4be7aa339a631f3c2d F test/json/json-speed-check.sh 362f9c5c4a69c6f2c6fa98f538fb2e07e084dc0bef74f8bbd92cc1666e8415e5 x -F test/json101.test 7241ab038c75ba780b009c6f37e4b660e5e63d1d74c110336a4779a2db8653f7 +F test/json101.test de9c93169b84ac96fd5836c638a2ae1f00e4afbd4003c6b596692d7f05e1cd69 F test/json102.test 327e77275f338c028faefa2da5164daf6b142a165e3015ff2a6e4251ddc6a0ac F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3051d6a7c12cbf86634d8fab64f3f3bc9a1eb184260e56a82eb543cb634ba7ef -R 76520c4f968a15591ad7c027fe9539e0 +P d839c9544d7f28c1abc779eb2d40f95c1a9386984656fbd29d19b1e7830171bc +R 9b88e23f3662645c8ffe23999211d809 U drh -Z 781a4e98041d9791d0ec4084972b315b +Z 75164b4fdafac0aaf63d4b3999912d9c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d8bd0e1a4d..0de6b4f785 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d839c9544d7f28c1abc779eb2d40f95c1a9386984656fbd29d19b1e7830171bc \ No newline at end of file +d262c059455ebe0650a45a6c1c04d1baf9609c635df352732dd192426e1bdc39 \ No newline at end of file diff --git a/src/json.c b/src/json.c index b201fdde5c..58f89b1aaa 100644 --- a/src/json.c +++ b/src/json.c @@ -758,6 +758,124 @@ static int jsonIs4Hex(const char *z){ return 1; } +/* +** Return the number of bytes of JSON5 whitespace at the beginning of +** the input string z[]. +** +** JSON5 whitespace consists of any of the following characters: +** +** Unicode UTF-8 Name +** U+0009 09 horizontal tab +** U+000a 0a line feed +** U+000b 0b vertical tab +** U+000c 0c form feed +** U+000d 0d carriage return +** U+0020 20 space +** U+00a0 c2 a0 non-breaking space +** U+1680 e1 9a 80 ogham space mark +** U+2000 e2 80 80 en quad +** U+2001 e2 80 81 em quad +** U+2002 e2 80 82 en space +** U+2003 e2 80 83 em space +** U+2004 e2 80 84 three-per-em space +** U+2005 e2 80 85 four-per-em space +** U+2006 e2 80 86 six-per-em space +** U+2007 e2 80 87 figure space +** U+2008 e2 80 88 punctuation space +** U+2009 e2 80 89 thin space +** U+200a e2 80 8a hair space +** U+2028 e2 80 a8 line separator +** U+2029 e2 80 a9 paragraph separator +** U+202f e2 80 af narrow no-break space (NNBSP) +** U+205f e2 81 9f medium mathematical space (MMSP) +** U+3000 e3 80 80 ideographical space +** U+FEFF ef bb bf byte order mark +** +** In addition, comments between '/', '*' and '*', '/' and +** from '/', '/' to end-of-line are also considered to be whitespace. +*/ +static int json5Whitespace(const char *zIn){ + int n = 0; + const u8 *z = (u8*)zIn; + while( 1 /*exit by "goto whitespace_done"*/ ){ + switch( z[n] ){ + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x20: { + n++; + break; + } + case '/': { + if( z[n+1]=='*' && z[n+2]!=0 ){ + int j; + for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){ + if( z[j]==0 ) goto whitespace_done; + } + n += j; + break; + }else if( z[n+1]=='/' ){ + int j; + for(j=n+2; z[j] && z[j]!='\n'; j++){} + n += j; + break; + } + goto whitespace_done; + } + case 0xc2: { + if( z[n+1]==0xa0 ){ + n += 2; + break; + } + goto whitespace_done; + } + case 0xe1: { + if( z[n+1]==0x9a && z[n+2]==0x80 ){ + n += 3; + break; + } + goto whitespace_done; + } + case 0xe2: { + if( z[n+1]==0x80 ){ + u8 c = z[n+2]; + if( c<0x80 ) goto whitespace_done; + if( c<=0x8a || c==0xa8 || c==0xa9 || c==0xaf ){ + n += 3; + break; + } + }else if( z[n+1]==0x81 && z[n+2]==0x9f ){ + n += 3; + break; + } + goto whitespace_done; + } + case 0xe3: { + if( z[n+1]==0x80 && z[n+2]==0x80 ){ + n += 3; + break; + } + goto whitespace_done; + } + case 0xef: { + if( z[n+1]==0xbb && z[n+2]==0xbf ){ + n += 3; + break; + } + goto whitespace_done; + } + default: { + goto whitespace_done; + } + } + } + whitespace_done: + return n; +} + + #ifdef SQLITE_ENABLE_JSON_NAN_INF /* ** Extra floating-point literals to allow in JSON. @@ -795,7 +913,9 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ JsonNode *pNode; const char *z = pParse->zJson; while( fast_isspace(z[i]) ){ i++; } - if( (c = z[i])=='{' ){ +json_parse_restart: + c = z[i]; + if( c=='{' ){ /* Parse object */ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; @@ -960,6 +1080,10 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ return -3; /* End of [...] */ }else if( c==0 ){ return 0; /* End of file */ + }else if( (j = json5Whitespace(&z[i]))>0 ){ + i += j; + pParse->has5 = 1; + goto json_parse_restart; }else{ #ifdef SQLITE_ENABLE_JSON_NAN_INF int k, nn; diff --git a/test/json101.test b/test/json101.test index 58559aafe5..3fc3923da3 100644 --- a/test/json101.test +++ b/test/json101.test @@ -308,26 +308,35 @@ do_execsql_test json-5.8 { do_execsql_test json-6.1 { SELECT json_valid('{"a":55,"b":72,}'); -} {1} -do_execsql_test json-6.1b { - SELECT json_valid('{"a":55,"b":72 , }'); -} {1} -do_execsql_test json-6.1c { - SELECT json_valid('{"a":55,"b":72,,}'); } {0} do_execsql_test json-6.2 { - SELECT json_valid('{"a":55,"b":72}'); + SELECT json_valid5('{"a":55,"b":72,}'); } {1} do_execsql_test json-6.3 { - SELECT json_valid('["a",55,"b",72,]'); + SELECT json_valid(json('{"a":55,"b":72,}')); } {1} -do_execsql_test json-6.3b { - SELECT json_valid('["a",55,"b",72 , ]'); -} {1} -do_execsql_test json-6.3c { - SELECT json_valid('["a",55,"b",72,,]'); -} {0} do_execsql_test json-6.4 { + SELECT json_valid('{"a":55,"b":72 , }'); +} {0} +do_execsql_test json-6.5 { + SELECT json_valid5('{"a":55,"b":72 , }'); +} {1} +do_execsql_test json-6.6 { + SELECT json_valid5('{"a":55,"b":72,,}'); +} {0} +do_execsql_test json-6.7 { + SELECT json_valid('{"a":55,"b":72}'); +} {1} +do_execsql_test json-6.8 { + SELECT json_valid5('["a",55,"b",72,]'); +} {1} +do_execsql_test json-6.9 { + SELECT json_valid5('["a",55,"b",72 , ]'); +} {1} +do_execsql_test json-6.10 { + SELECT json_valid5('["a",55,"b",72,,]'); +} {0} +do_execsql_test json-6.11 { SELECT json_valid('["a",55,"b",72]'); } {1} From 5255102e04d0872280dc6c776a4318e985c54abf Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 26 Apr 2023 15:58:08 +0000 Subject: [PATCH 06/37] Improvement to the way the JSON performance measure scripts work → keep the test database in the directory of the test, not in the source tree. FossilOrigin-Name: ac411dbdcbf0b9040fb5b7de173271f383a6aa303d57f22ebd200809a5b4a6d3 --- manifest | 14 +++++++------- manifest.uuid | 2 +- test/json/README.md | 3 ++- test/json/json-speed-check.sh | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index a607ef397a..901405d91b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Work\stoward\simplementing\sJSON5\swhitespace.\s\sUntested\sand\sincomplete. -D 2023-04-26T15:19:19.822 +C Improvement\sto\sthe\sway\sthe\sJSON\sperformance\smeasure\sscripts\swork\s→\skeep\sthe\ntest\sdatabase\sin\sthe\sdirectory\sof\sthe\stest,\snot\sin\sthe\ssource\stree. +D 2023-04-26T15:58:08.228 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1249,10 +1249,10 @@ F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ff F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9e1946 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa -F test/json/README.md 9d117e0d6da9eee96de2fc8e32b603208b0e5b460ff99e5de3407bb713f0bb5c +F test/json/README.md 506af1f54574b524106acb50d1a341ab5ddfa6d83fe25095007892b07e663e85 F test/json/json-generator.tcl 229bd293f1865f787c160886cadd282631721925cca2947aaa54bbcd7f65cef7 F test/json/json-q1.txt 335a7c8ab291d354f33b7decc9559e99a2823d4142291c4be7aa339a631f3c2d -F test/json/json-speed-check.sh 362f9c5c4a69c6f2c6fa98f538fb2e07e084dc0bef74f8bbd92cc1666e8415e5 x +F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3b1f1511d21af6737 x F test/json101.test de9c93169b84ac96fd5836c638a2ae1f00e4afbd4003c6b596692d7f05e1cd69 F test/json102.test 327e77275f338c028faefa2da5164daf6b142a165e3015ff2a6e4251ddc6a0ac F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d839c9544d7f28c1abc779eb2d40f95c1a9386984656fbd29d19b1e7830171bc -R 9b88e23f3662645c8ffe23999211d809 +P d262c059455ebe0650a45a6c1c04d1baf9609c635df352732dd192426e1bdc39 +R 3eb29750520f57f10b6b50d11e64a15f U drh -Z 75164b4fdafac0aaf63d4b3999912d9c +Z f254fb62ebf6c065b09ee32328c8e984 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0de6b4f785..288ecccb2f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d262c059455ebe0650a45a6c1c04d1baf9609c635df352732dd192426e1bdc39 \ No newline at end of file +ac411dbdcbf0b9040fb5b7de173271f383a6aa303d57f22ebd200809a5b4a6d3 \ No newline at end of file diff --git a/test/json/README.md b/test/json/README.md index 94f5516c06..6a16114925 100644 --- a/test/json/README.md +++ b/test/json/README.md @@ -11,7 +11,8 @@ of the SQLite JSON parser. 1. Run: "`tclsh json-generator.tcl | sqlite3 json100mb.db`" to create the 100 megabyte test database. Do this so that the "json100mb.db" - file lands in the same directory as the json-generator.tcl script. + file lands in the directory from which you will run tests, not in + the test/json subdirectory of the source tree. 2. Build the baseline sqlite3.c file. ("`make sqlite3.c`") diff --git a/test/json/json-speed-check.sh b/test/json/json-speed-check.sh index 343f830bda..f7e7f1be58 100755 --- a/test/json/json-speed-check.sh +++ b/test/json/json-speed-check.sh @@ -69,8 +69,8 @@ rm -f cachegrind.out.* jsonshell $CC -g -Os -Wall -I. $CC_OPTS ./shell.c ./sqlite3.c -o jsonshell -ldl -lpthread ls -l jsonshell | tee -a summary-$NAME.txt home=`echo $0 | sed -e 's,/[^/]*$,,'` -echo ./jsonshell $home/json100mb.db "<$home/json-q1.txt" -valgrind --tool=cachegrind ./jsonshell $home/json100mb.db <$home/json-q1.txt \ +echo ./jsonshell json100mb.db "<$home/json-q1.txt" +valgrind --tool=cachegrind ./jsonshell json100mb.db <$home/json-q1.txt \ 2>&1 | tee -a summary-$NAME.txt cg_anno.tcl cachegrind.out.* >jout-$NAME.txt echo '*****************************************************' >>jout-$NAME.txt From f14b2e35fa2bcacdf14a3be31e7370670ebd1e34 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 26 Apr 2023 17:30:28 +0000 Subject: [PATCH 07/37] Partial implementation of JSON5 numeric literal extensions. Use a switch() statement in the parser for better performance. FossilOrigin-Name: 78404dc37024cad5fe7eacf78ea85b56f08b129a1b9a046c3e1b11275068a485 --- manifest | 14 +++--- manifest.uuid | 2 +- src/json.c | 120 +++++++++++++++++++++++++++++++++------------- test/json102.test | 33 ++++++++----- 4 files changed, 117 insertions(+), 52 deletions(-) diff --git a/manifest b/manifest index 901405d91b..aefd7ea121 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvement\sto\sthe\sway\sthe\sJSON\sperformance\smeasure\sscripts\swork\s→\skeep\sthe\ntest\sdatabase\sin\sthe\sdirectory\sof\sthe\stest,\snot\sin\sthe\ssource\stree. -D 2023-04-26T15:58:08.228 +C Partial\simplementation\sof\sJSON5\snumeric\sliteral\sextensions.\s\sUse\sa\sswitch()\nstatement\sin\sthe\sparser\sfor\sbetter\sperformance. +D 2023-04-26T17:30:28.870 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c d0f9a0c630bdea3721e28b5ba2fce119c0e732ad48f6674a8f1644f2ec45ffb1 +F src/json.c a346dbe63628f8cb9c6bf6bbccb8fc81a49730db2311ec9b483d7fc3582da223 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -1254,7 +1254,7 @@ F test/json/json-generator.tcl 229bd293f1865f787c160886cadd282631721925cca2947aa F test/json/json-q1.txt 335a7c8ab291d354f33b7decc9559e99a2823d4142291c4be7aa339a631f3c2d F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3b1f1511d21af6737 x F test/json101.test de9c93169b84ac96fd5836c638a2ae1f00e4afbd4003c6b596692d7f05e1cd69 -F test/json102.test 327e77275f338c028faefa2da5164daf6b142a165e3015ff2a6e4251ddc6a0ac +F test/json102.test 1f61f469d763ff26430dbee76bc75e0aa73084ca84f10e58744fdb899e56de3e F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d262c059455ebe0650a45a6c1c04d1baf9609c635df352732dd192426e1bdc39 -R 3eb29750520f57f10b6b50d11e64a15f +P ac411dbdcbf0b9040fb5b7de173271f383a6aa303d57f22ebd200809a5b4a6d3 +R 0e63126100fc0a5fcfa419eb4dbeae91 U drh -Z f254fb62ebf6c065b09ee32328c8e984 +Z 7c56c83a7327e89fcacb5797373de3a9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 288ecccb2f..76ab21e90f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac411dbdcbf0b9040fb5b7de173271f383a6aa303d57f22ebd200809a5b4a6d3 \ No newline at end of file +78404dc37024cad5fe7eacf78ea85b56f08b129a1b9a046c3e1b11275068a485 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 58f89b1aaa..63feff9488 100644 --- a/src/json.c +++ b/src/json.c @@ -912,10 +912,9 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ int x; JsonNode *pNode; const char *z = pParse->zJson; - while( fast_isspace(z[i]) ){ i++; } json_parse_restart: - c = z[i]; - if( c=='{' ){ + switch( (u8)z[i] ){ + case '{': { /* Parse object */ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; @@ -951,7 +950,8 @@ json_parse_restart: } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; return j+1; - }else if( c=='[' ){ + } + case '[': { /* Parse array */ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); if( iThis<0 ) return -1; @@ -977,7 +977,8 @@ json_parse_restart: } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; return j+1; - }else if( c=='"' ){ + } + case '"': { /* Parse string */ u8 jnFlags = 0; j = i+1; @@ -1004,32 +1005,61 @@ json_parse_restart: jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]); if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; return j+1; - }else if( c=='n' - && strncmp(z+i,"null",4)==0 - && !sqlite3Isalnum(z[i+4]) ){ - jsonParseAddNode(pParse, JSON_NULL, 0, 0); - return i+4; - }else if( c=='t' - && strncmp(z+i,"true",4)==0 - && !sqlite3Isalnum(z[i+4]) ){ - jsonParseAddNode(pParse, JSON_TRUE, 0, 0); - return i+4; - }else if( c=='f' - && strncmp(z+i,"false",5)==0 - && !sqlite3Isalnum(z[i+5]) ){ - jsonParseAddNode(pParse, JSON_FALSE, 0, 0); - return i+5; - }else if( c=='-' || (c>='0' && c<='9') ){ + } + case 'n': { + if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ + jsonParseAddNode(pParse, JSON_NULL, 0, 0); + return i+4; + } + return -1; + } + case 't': { + if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ + jsonParseAddNode(pParse, JSON_TRUE, 0, 0); + return i+4; + } + return -1; + } + case 'f': { + if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ + jsonParseAddNode(pParse, JSON_FALSE, 0, 0); + return i+5; + } + return -1; + } + case '+': + pParse->has5 = 1; + /* fall through */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { /* Parse number */ u8 seenDP = 0; u8 seenE = 0; assert( '-' < '0' ); + assert( '+' < '0' ); + assert( '.' < '0' ); + c = z[i]; + if( c<='0' ){ - j = c=='-' ? i+1 : i; - if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1; + if( c=='0' ){ + if( sqlite3Isdigit(z[i+1]) ){ + pParse->has5 = 1; + } + }else{ + if( !sqlite3Isdigit(z[i+1]) ) return -1; + if( z[i+1]=='0' && sqlite3Isdigit(z[i+2]) ) pParse->has5 = 1; + } } - j = i+1; - for(;; j++){ + for(j=i+1;; j++){ c = z[j]; if( c>='0' && c<='9' ) continue; if( c=='.' ){ @@ -1074,19 +1104,44 @@ json_parse_restart: jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, j - i, &z[i]); return j; - }else if( c=='}' ){ + } + case '}': { return -2; /* End of {...} */ - }else if( c==']' ){ + } + case ']': { return -3; /* End of [...] */ - }else if( c==0 ){ + } + case 0: { return 0; /* End of file */ - }else if( (j = json5Whitespace(&z[i]))>0 ){ - i += j; - pParse->has5 = 1; + } + case 0x09: + case 0x0a: + case 0x0d: + case 0x20: { + do{ + i++; + }while( fast_isspace(z[i]) ); goto json_parse_restart; - }else{ + } + case 0x0b: + case 0x0c: + case '/': + case 0xc2: + case 0xe1: + case 0xe2: + case 0xe3: { + j = json5Whitespace(&z[i]); + if( j>0 ){ + i += j; + pParse->has5 = 1; + goto json_parse_restart; + } + return -1; + } + default: { #ifdef SQLITE_ENABLE_JSON_NAN_INF int k, nn; + c = z[i]; for(k=0; k Date: Wed, 26 Apr 2023 20:26:14 +0000 Subject: [PATCH 08/37] Implement some of the JSON5 enhancements to string and numeric literals. This is an incremental check-in of work in progress. FossilOrigin-Name: 9508efa9d61c0ff0eb73100dd52889dadc5fa2a2091b944a9b8a74e8a5c50e82 --- manifest | 12 ++-- manifest.uuid | 2 +- src/json.c | 160 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 147 insertions(+), 27 deletions(-) diff --git a/manifest b/manifest index aefd7ea121..4c3f3616dd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Partial\simplementation\sof\sJSON5\snumeric\sliteral\sextensions.\s\sUse\sa\sswitch()\nstatement\sin\sthe\sparser\sfor\sbetter\sperformance. -D 2023-04-26T17:30:28.870 +C Implement\ssome\sof\sthe\sJSON5\senhancements\sto\sstring\sand\snumeric\sliterals.\nThis\sis\san\sincremental\scheck-in\sof\swork\sin\sprogress. +D 2023-04-26T20:26:14.388 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c a346dbe63628f8cb9c6bf6bbccb8fc81a49730db2311ec9b483d7fc3582da223 +F src/json.c f82b86599b220a2c60731eba883bbea92ee4d6e61aaa56740179e8aa5e6ec596 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ac411dbdcbf0b9040fb5b7de173271f383a6aa303d57f22ebd200809a5b4a6d3 -R 0e63126100fc0a5fcfa419eb4dbeae91 +P 78404dc37024cad5fe7eacf78ea85b56f08b129a1b9a046c3e1b11275068a485 +R e7aefca3f1ce2ae26f999bdfb414a2b2 U drh -Z 7c56c83a7327e89fcacb5797373de3a9 +Z b316d61c8d04f67716fa9ffd06cd770b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 76ab21e90f..2e71d5072e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -78404dc37024cad5fe7eacf78ea85b56f08b129a1b9a046c3e1b11275068a485 \ No newline at end of file +9508efa9d61c0ff0eb73100dd52889dadc5fa2a2091b944a9b8a74e8a5c50e82 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 63feff9488..3e2d6646d8 100644 --- a/src/json.c +++ b/src/json.c @@ -104,6 +104,7 @@ static const char * const jsonType[] = { #define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ #define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ #define JNODE_LABEL 0x40 /* Is a label of an object */ +#define JNODE_JSON5 0x80 /* Node contains JSON5 enhancements */ /* A single node of parsed JSON @@ -295,6 +296,72 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ assert( p->nUsednAlloc ); } +/* +** The zIn[0..N] string is a JSON5 string literal. Append to p a translation +** of the string literal that standard JSON and that omits all JSON5 +** features. +*/ +static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ + jsonAppendChar(p, '"'); + jsonAppendRaw(p, &zIn[1], N-2); /* TODO: translate JSON5 escapes */ + jsonAppendChar(p, '"'); +} + +/* +** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation +** of the string literal that standard JSON and that omits all JSON5 +** features. +*/ +static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ + if( zIn[0]=='+' ){ + zIn++; + N--; + }else if( zIn[0]=='-' ){ + jsonAppendChar(p, '-'); + zIn++; + N--; + } + while( zIn[0]=='0' && N>1 ){ + zIn++; + N--; + } + jsonAppendRaw(p, zIn, N); +} + +/* +** The zIn[0..N] string is a JSON5 real literal. Append to p a translation +** of the string literal that standard JSON and that omits all JSON5 +** features. +*/ +static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ + int i; + if( zIn[0]=='+' ){ + zIn++; + N--; + }else if( zIn[0]=='-' ){ + jsonAppendChar(p, '-'); + zIn++; + N--; + } + if( zIn[0]=='.' ){ + jsonAppendChar(p, '0'); + } + for(i=0; i0 ){ + jsonAppendRaw(p, zIn, N); + } +} + + + /* ** Append a function parameter value to the JSON string under ** construction. @@ -425,17 +492,32 @@ static void jsonRenderNode( break; } case JSON_STRING: { + assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ - assert( pNode->eU==1 ); jsonAppendString(pOut, pNode->u.zJContent, pNode->n); - break; + }else if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); + }else{ + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); } - /* no break */ deliberate_fall_through + break; + } + case JSON_REAL: { + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); + }else{ + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + } + break; } - case JSON_REAL: case JSON_INT: { assert( pNode->eU==1 ); - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); + }else{ + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + } break; } case JSON_ARRAY: { @@ -615,9 +697,10 @@ static void jsonReturn( const char *z; char *zOut; u32 j; + u32 nOut = n; assert( pNode->eU==1 ); z = pNode->u.zJContent; - zOut = sqlite3_malloc( n+1 ); + zOut = sqlite3_malloc( nOut+1 ); if( zOut==0 ){ sqlite3_result_error_nomem(pCtx); break; @@ -669,6 +752,15 @@ static void jsonReturn( c = '\r'; }else if( c=='t' ){ c = '\t'; + }else if( c=='v' ){ + c = '\v'; + }else if( c=='\'' ){ + c = '\''; + }else if( c=='0' ){ + c = 0; + }else if( c=='x' ){ + c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]); + i += 2; } zOut[j++] = c; } @@ -749,13 +841,18 @@ static int jsonParseAddNode( return pParse->nNode++; } +/* +** Return true if z[] begins with 2 (or more) hexadecimal digits +*/ +static int jsonIs2Hex(const char *z){ + return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]); +} + /* ** Return true if z[] begins with 4 (or more) hexadecimal digits */ static int jsonIs4Hex(const char *z){ - int i; - for(i=0; i<4; i++) if( !sqlite3Isxdigit(z[i]) ) return 0; - return 1; + return jsonIs2Hex(z) && jsonIs2Hex(&z[2]); } /* @@ -978,9 +1075,17 @@ json_parse_restart: pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; return j+1; } - case '"': { + case '\'': { + u8 jnFlags; + char cDelim; + pParse->has5 = 1; + jnFlags = JNODE_JSON5; + goto parse_string; + case '"': /* Parse string */ - u8 jnFlags = 0; + jnFlags = 0; + parse_string: + cDelim = z[i]; j = i+1; for(;;){ c = z[j]; @@ -992,12 +1097,16 @@ json_parse_restart: c = z[++j]; if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' || c=='n' || c=='r' || c=='t' - || (c=='u' && jsonIs4Hex(z+j+1)) ){ - jnFlags = JNODE_ESCAPE; + || (c=='u' && jsonIs4Hex(&z[j+1])) ){ + jnFlags |= JNODE_ESCAPE; + }else if( c=='\'' || c=='0' || c=='v' + || (c=='x' && jsonIs2Hex(&z[j+1])) ){ + jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); + pParse->has5 = 1; }else{ return -1; } - }else if( c=='"' ){ + }else if( c==cDelim ){ break; } j++; @@ -1027,9 +1136,11 @@ json_parse_restart: } return -1; } - case '+': + case '+': { + u8 seenDP, seenE, jnFlags; pParse->has5 = 1; - /* fall through */ + jnFlags = JNODE_JSON5; + goto parse_number; case '-': case '0': case '1': @@ -1040,10 +1151,12 @@ json_parse_restart: case '6': case '7': case '8': - case '9': { + case '9': /* Parse number */ - u8 seenDP = 0; - u8 seenE = 0; + jnFlags = 0; + parse_number: + seenDP = 0; + seenE = 0; assert( '-' < '0' ); assert( '+' < '0' ); assert( '.' < '0' ); @@ -1053,10 +1166,14 @@ json_parse_restart: if( c=='0' ){ if( sqlite3Isdigit(z[i+1]) ){ pParse->has5 = 1; + jnFlags = JNODE_JSON5; } }else{ if( !sqlite3Isdigit(z[i+1]) ) return -1; - if( z[i+1]=='0' && sqlite3Isdigit(z[i+2]) ) pParse->has5 = 1; + if( z[i+1]=='0' && sqlite3Isdigit(z[i+2]) ){ + pParse->has5 = 1; + jnFlags = JNODE_JSON5; + } } } for(j=i+1;; j++){ @@ -1103,6 +1220,9 @@ json_parse_restart: if( z[j-1]<'0' ) return -1; jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, j - i, &z[i]); + if( jnFlags && !pParse->oom ){ + pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; + } return j; } case '}': { From f59c01e24cb3146f5e5ec6ccb73731a8a365b6c5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 12:24:00 +0000 Subject: [PATCH 09/37] Translate JSON5-only string literal escape sequences into the JSON equivalents. FossilOrigin-Name: 14e82f36eed31af1237898728bf353b968523c62b1f8d1d90dbbabd92d0c2834 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 4c3f3616dd..e797d70238 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Implement\ssome\sof\sthe\sJSON5\senhancements\sto\sstring\sand\snumeric\sliterals.\nThis\sis\san\sincremental\scheck-in\sof\swork\sin\sprogress. -D 2023-04-26T20:26:14.388 +C Translate\sJSON5-only\sstring\sliteral\sescape\ssequences\sinto\sthe\sJSON\sequivalents. +D 2023-04-27T12:24:00.195 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c f82b86599b220a2c60731eba883bbea92ee4d6e61aaa56740179e8aa5e6ec596 +F src/json.c e872bb2ba63296c21dd62f83557d504bfb94f299a6591ec735cd4a9aed4e321b F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 78404dc37024cad5fe7eacf78ea85b56f08b129a1b9a046c3e1b11275068a485 -R e7aefca3f1ce2ae26f999bdfb414a2b2 +P 9508efa9d61c0ff0eb73100dd52889dadc5fa2a2091b944a9b8a74e8a5c50e82 +R 672753aa30f1f89635383bf8b5e62624 U drh -Z b316d61c8d04f67716fa9ffd06cd770b +Z aa11f958e9e420359b5624437ca5455e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2e71d5072e..4badfeac1a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9508efa9d61c0ff0eb73100dd52889dadc5fa2a2091b944a9b8a74e8a5c50e82 \ No newline at end of file +14e82f36eed31af1237898728bf353b968523c62b1f8d1d90dbbabd92d0c2834 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3e2d6646d8..0cbcac3c4e 100644 --- a/src/json.c +++ b/src/json.c @@ -302,8 +302,42 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ ** features. */ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ + int i; jsonAppendChar(p, '"'); - jsonAppendRaw(p, &zIn[1], N-2); /* TODO: translate JSON5 escapes */ + zIn++; + N -= 2; + while( N>0 ){ + for(i=0; i0 ){ + jsonAppendRaw(p, zIn, i); + zIn += i; + N -= i; + if( N==0 ) break; + } + assert( zIn[0]=='\\' ); + switch( zIn[1] ){ + case '\'': + jsonAppendChar(p, '\''); + break; + case 'v': + jsonAppendRaw(p, "\\u0009", 6); + break; + case 'x': + jsonAppendRaw(p, "\\u00", 4); + jsonAppendRaw(p, &zIn[2], 2); + zIn += 2; + N -= 2; + break; + case '0': + jsonAppendRaw(p, "\\u0000", 6); + break; + default: + jsonAppendRaw(p, zIn, 2); + break; + } + zIn += 2; + N -= 2; + } jsonAppendChar(p, '"'); } From a549e7a527902556f8bd55e506674f8a3a983952 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 13:44:26 +0000 Subject: [PATCH 10/37] Permit JSON5 whitespace in all contexts of objects and arrays. FossilOrigin-Name: 93f3ab26b57c0469862a56e97d4b3c796b27f9f582046fcff1f2aa8d8910c550 --- manifest | 12 +++---- manifest.uuid | 2 +- src/json.c | 88 +++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/manifest b/manifest index e797d70238..a76a4753ef 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Translate\sJSON5-only\sstring\sliteral\sescape\ssequences\sinto\sthe\sJSON\sequivalents. -D 2023-04-27T12:24:00.195 +C Permit\sJSON5\swhitespace\sin\sall\scontexts\sof\sobjects\sand\sarrays. +D 2023-04-27T13:44:26.660 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c e872bb2ba63296c21dd62f83557d504bfb94f299a6591ec735cd4a9aed4e321b +F src/json.c 28ca7f26c211e0fd705d06d9036de59304f9a3dd5e69e3745a688aafd8c618ae F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9508efa9d61c0ff0eb73100dd52889dadc5fa2a2091b944a9b8a74e8a5c50e82 -R 672753aa30f1f89635383bf8b5e62624 +P 14e82f36eed31af1237898728bf353b968523c62b1f8d1d90dbbabd92d0c2834 +R 37b7a9f174ccda66654bba8268903617 U drh -Z aa11f958e9e420359b5624437ca5455e +Z 18c492aceae37854acd7e6823a29d2b5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4badfeac1a..2f3e3c7fd1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -14e82f36eed31af1237898728bf353b968523c62b1f8d1d90dbbabd92d0c2834 \ No newline at end of file +93f3ab26b57c0469862a56e97d4b3c796b27f9f582046fcff1f2aa8d8910c550 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0cbcac3c4e..dbc1c6b833 100644 --- a/src/json.c +++ b/src/json.c @@ -136,6 +136,7 @@ struct JsonParse { u8 oom; /* Set to true if out of memory */ u8 has5; /* True if input has JSON5 features */ int nJson; /* Length of the zJson string in bytes */ + u32 iErr; /* Error location in zJson[] */ u32 iHold; /* Replace cache line with the lowest iHold value */ }; @@ -945,7 +946,7 @@ static int json5Whitespace(const char *zIn){ for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){ if( z[j]==0 ) goto whitespace_done; } - n += j; + n += j+1; break; }else if( z[n+1]=='/' ){ int j; @@ -1032,9 +1033,14 @@ static const struct NanInfName { ** Parse a single JSON value which begins at pParse->zJson[i]. Return the ** index of the first character past the end of the value parsed. ** -** Return negative for a syntax error. Special cases: return -2 if the -** first non-whitespace character is '}' and return -3 if the first -** non-whitespace character is ']'. +** Special return values: +** +** 0 End if input +** -1 Syntax error +** -2 '}' seen +** -3 ']' seen +** -4 ',' seen +** -5 ':' seen */ static int jsonParseValue(JsonParse *pParse, u32 i){ char c; @@ -1050,12 +1056,12 @@ json_parse_restart: iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; for(j=i+1;;j++){ - while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); - if( x<0 ){ + if( x<=0 ){ pParse->iDepth--; if( x==(-2) ){ + j = pParse->iErr; if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; break; } @@ -1066,18 +1072,33 @@ json_parse_restart: if( pNode->eType!=JSON_STRING ) return -1; pNode->jnFlags |= JNODE_LABEL; j = x; - while( fast_isspace(z[j]) ){ j++; } - if( z[j]!=':' ) return -1; - j++; + if( z[j]==':' ){ + j++; + }else{ + x = jsonParseValue(pParse, j); + if( x!=(-5) ) return -1; + j = pParse->iErr+1; + } x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ) return -1; j = x; - while( fast_isspace(z[j]) ){ j++; } - c = z[j]; - if( c==',' ) continue; - if( c!='}' ) return -1; - break; + if( z[j]==',' ){ + continue; + }else if( z[j]=='}' ){ + break; + }else{ + x = jsonParseValue(pParse, j); + if( x==(-4) ){ + j = pParse->iErr; + continue; + } + if( x==(-2) ){ + j = pParse->iErr; + break; + } + } + return -1; } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; return j+1; @@ -1088,23 +1109,34 @@ json_parse_restart: if( iThis<0 ) return -1; memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ){ if( x==(-3) ){ + j = pParse->iErr; if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; break; } return -1; } j = x; - while( fast_isspace(z[j]) ){ j++; } - c = z[j]; - if( c==',' ) continue; - if( c!=']' ) return -1; - break; + if( z[j]==',' ){ + continue; + }else if( z[j]==']' ){ + break; + }else{ + x = jsonParseValue(pParse, j); + if( x==(-4) ){ + j = pParse->iErr; + continue; + } + if( x==(-3) ){ + j = pParse->iErr; + break; + } + } + return -1; } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; return j+1; @@ -1260,11 +1292,21 @@ json_parse_restart: return j; } case '}': { + pParse->iErr = i; return -2; /* End of {...} */ } case ']': { + pParse->iErr = i; return -3; /* End of [...] */ } + case ',': { + pParse->iErr = i; + return -4; /* List separator */ + } + case ':': { + pParse->iErr = i; + return -5; /* Object label/value separator */ + } case 0: { return 0; /* End of file */ } @@ -1334,7 +1376,11 @@ static int jsonParse( if( i>0 ){ assert( pParse->iDepth==0 ); while( fast_isspace(zJson[i]) ) i++; - if( zJson[i] ) i = -1; + if( zJson[i] ){ + i += json5Whitespace(&zJson[i]); + if( zJson[i] ) return -1; + pParse->has5 = 1; + } } if( i<=0 ){ if( pCtx!=0 ){ From 0d8579120a2d46b75d1564e7d928690fb33ef572 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 14:38:51 +0000 Subject: [PATCH 11/37] Performance optimization in the JSON parser. FossilOrigin-Name: 5a88ba743f55d45b1c0ce0090bb3b396bcf7fcf7b3bcb989aaf30b8bb772599e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index a76a4753ef..25d0e8f823 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Permit\sJSON5\swhitespace\sin\sall\scontexts\sof\sobjects\sand\sarrays. -D 2023-04-27T13:44:26.660 +C Performance\soptimization\sin\sthe\sJSON\sparser. +D 2023-04-27T14:38:51.628 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 28ca7f26c211e0fd705d06d9036de59304f9a3dd5e69e3745a688aafd8c618ae +F src/json.c d65da6a334ff2f18cea8fa618bf64e085cf3517bdb9a5e570cb1e8531040d976 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 14e82f36eed31af1237898728bf353b968523c62b1f8d1d90dbbabd92d0c2834 -R 37b7a9f174ccda66654bba8268903617 +P 93f3ab26b57c0469862a56e97d4b3c796b27f9f582046fcff1f2aa8d8910c550 +R 66ad085cb2272ce979fd2644e3aac860 U drh -Z 18c492aceae37854acd7e6823a29d2b5 +Z 7c28704af9aa014ef0ec768389ba7618 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2f3e3c7fd1..6d73e72cdd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -93f3ab26b57c0469862a56e97d4b3c796b27f9f582046fcff1f2aa8d8910c550 \ No newline at end of file +5a88ba743f55d45b1c0ce0090bb3b396bcf7fcf7b3bcb989aaf30b8bb772599e \ No newline at end of file diff --git a/src/json.c b/src/json.c index dbc1c6b833..3828ca6bc3 100644 --- a/src/json.c +++ b/src/json.c @@ -1075,19 +1075,35 @@ json_parse_restart: if( z[j]==':' ){ j++; }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==':' ){ + j++; + goto parse_object_value; + } + } x = jsonParseValue(pParse, j); if( x!=(-5) ) return -1; j = pParse->iErr+1; } + parse_object_value: x = jsonParseValue(pParse, j); pParse->iDepth--; - if( x<0 ) return -1; + if( x<=0 ) return -1; j = x; if( z[j]==',' ){ continue; }else if( z[j]=='}' ){ break; }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==',' ){ + continue; + }else if( z[j]=='}' ){ + break; + } + } x = jsonParseValue(pParse, j); if( x==(-4) ){ j = pParse->iErr; @@ -1112,7 +1128,7 @@ json_parse_restart: if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); pParse->iDepth--; - if( x<0 ){ + if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; @@ -1126,6 +1142,14 @@ json_parse_restart: }else if( z[j]==']' ){ break; }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==',' ){ + continue; + }else if( z[j]==']' ){ + break; + } + } x = jsonParseValue(pParse, j); if( x==(-4) ){ j = pParse->iErr; @@ -1378,7 +1402,10 @@ static int jsonParse( while( fast_isspace(zJson[i]) ) i++; if( zJson[i] ){ i += json5Whitespace(&zJson[i]); - if( zJson[i] ) return -1; + if( zJson[i] ){ + jsonParseReset(pParse); + return 1; + } pParse->has5 = 1; } } From ef3a162be6508eae2d09315f6e82d30543367e6d Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 15:11:26 +0000 Subject: [PATCH 12/37] Allow the labels on JSON objects to be unquoted identifier names. FossilOrigin-Name: fb428db3f64f148ab9a3478fdcc8b3733e58102b3c8895a482e2551d974d5661 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 16 ++++++++++++++-- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 25d0e8f823..ff21e8d8d7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Performance\soptimization\sin\sthe\sJSON\sparser. -D 2023-04-27T14:38:51.628 +C Allow\sthe\slabels\son\sJSON\sobjects\sto\sbe\sunquoted\sidentifier\snames. +D 2023-04-27T15:11:26.153 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c d65da6a334ff2f18cea8fa618bf64e085cf3517bdb9a5e570cb1e8531040d976 +F src/json.c 22e67e7ae7d1bd46df5616452a7b14587799df6dfff0c47bd09c3926c89ead99 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 93f3ab26b57c0469862a56e97d4b3c796b27f9f582046fcff1f2aa8d8910c550 -R 66ad085cb2272ce979fd2644e3aac860 +P 5a88ba743f55d45b1c0ce0090bb3b396bcf7fcf7b3bcb989aaf30b8bb772599e +R 7efb5a3476c2275fadcb3d73f88a7a54 U drh -Z 7c28704af9aa014ef0ec768389ba7618 +Z 22784998a1ee89abb38fb8b4e89c3f5d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6d73e72cdd..ec0911a184 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5a88ba743f55d45b1c0ce0090bb3b396bcf7fcf7b3bcb989aaf30b8bb772599e \ No newline at end of file +fb428db3f64f148ab9a3478fdcc8b3733e58102b3c8895a482e2551d974d5661 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3828ca6bc3..178267bdd1 100644 --- a/src/json.c +++ b/src/json.c @@ -1059,13 +1059,25 @@ json_parse_restart: if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); if( x<=0 ){ - pParse->iDepth--; if( x==(-2) ){ j = pParse->iErr; if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; + pParse->iDepth--; break; } - return -1; + j += json5Whitespace(&z[j]); + if( sqlite3Isalpha(z[j]) || z[j]=='_' || z[j]=='$' ){ + int k; + for(k=j+1; sqlite3Isalnum(z[k]) || z[k]=='_' || z[k]=='$'; k++){} + jsonParseAddNode(pParse, JSON_STRING, k-j, &z[j]); + if( !pParse->oom ){ + pParse->aNode[pParse->nNode-1].jnFlags = JNODE_RAW; + } + pParse->has5 = 1; + x = k; + }else{ + return -1; + } } if( pParse->oom ) return -1; pNode = &pParse->aNode[pParse->nNode-1]; From 13f8a80d944a26a35d4cb9dbbfda50eff963502d Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 15:48:58 +0000 Subject: [PATCH 13/37] JSON string literals may span multiple lines by escaping new line characters. FossilOrigin-Name: 66da4bd4a30c390fa1a7960ce2edaef82e63971ecf33ffb6b906db9f278041c5 --- manifest | 12 ++++---- manifest.uuid | 2 +- src/json.c | 81 ++++++++++++++++++++++++++++++++++----------------- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/manifest b/manifest index ff21e8d8d7..fff4f23d06 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sthe\slabels\son\sJSON\sobjects\sto\sbe\sunquoted\sidentifier\snames. -D 2023-04-27T15:11:26.153 +C JSON\sstring\sliterals\smay\sspan\smultiple\slines\sby\sescaping\snew\sline\scharacters. +D 2023-04-27T15:48:58.281 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 22e67e7ae7d1bd46df5616452a7b14587799df6dfff0c47bd09c3926c89ead99 +F src/json.c d0a088f85237afcf78aa9607261a4912ce1a42db3fca907305f78da8be0c51d9 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5a88ba743f55d45b1c0ce0090bb3b396bcf7fcf7b3bcb989aaf30b8bb772599e -R 7efb5a3476c2275fadcb3d73f88a7a54 +P fb428db3f64f148ab9a3478fdcc8b3733e58102b3c8895a482e2551d974d5661 +R 187d7b6dd1b32f74babea313dab398fc U drh -Z 22784998a1ee89abb38fb8b4e89c3f5d +Z 182d7f6abcc455723bd9a825ca2f2b58 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ec0911a184..6a4b1b10bd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fb428db3f64f148ab9a3478fdcc8b3733e58102b3c8895a482e2551d974d5661 \ No newline at end of file +66da4bd4a30c390fa1a7960ce2edaef82e63971ecf33ffb6b906db9f278041c5 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 178267bdd1..e446830db9 100644 --- a/src/json.c +++ b/src/json.c @@ -316,7 +316,7 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ if( N==0 ) break; } assert( zIn[0]=='\\' ); - switch( zIn[1] ){ + switch( (u8)zIn[1] ){ case '\'': jsonAppendChar(p, '\''); break; @@ -332,6 +332,21 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ case '0': jsonAppendRaw(p, "\\u0000", 6); break; + case '\r': + if( N>=3 && zIn[2]=='\n' ){ + zIn++; + N--; + } + break; + case '\n': + break; + case 0xe2: + assert( N>=4 ); + assert( 0x80==(u8)zIn[2] ); + assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); + zIn += 2; + N -= 2; + break; default: jsonAppendRaw(p, zIn, 2); break; @@ -742,9 +757,7 @@ static void jsonReturn( } for(i=1, j=0; ihas5 = 1; From dbcea23976955b2aa36ca9eb92b0fe5315ef7665 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 16:24:12 +0000 Subject: [PATCH 14/37] Fix the handling of escape solidus in the JSON routines. FossilOrigin-Name: 676877aca235e620ee12d10235dd6ad009d4968455ec170daeb1998b94a7e0a2 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 6 ++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index fff4f23d06..40fa719367 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JSON\sstring\sliterals\smay\sspan\smultiple\slines\sby\sescaping\snew\sline\scharacters. -D 2023-04-27T15:48:58.281 +C Fix\sthe\shandling\sof\sescape\ssolidus\sin\sthe\sJSON\sroutines. +D 2023-04-27T16:24:12.129 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c d0a088f85237afcf78aa9607261a4912ce1a42db3fca907305f78da8be0c51d9 +F src/json.c cdf05600af31e37ad50924f39d7fa6227a7c9b94ea3d3690312cc1b7e15b36ef F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P fb428db3f64f148ab9a3478fdcc8b3733e58102b3c8895a482e2551d974d5661 -R 187d7b6dd1b32f74babea313dab398fc +P 66da4bd4a30c390fa1a7960ce2edaef82e63971ecf33ffb6b906db9f278041c5 +R 9748a62c2b5ef29db66fe80859611ec7 U drh -Z 182d7f6abcc455723bd9a825ca2f2b58 +Z 8f82433213e0588347a902f584c3080a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6a4b1b10bd..9374e8e5aa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -66da4bd4a30c390fa1a7960ce2edaef82e63971ecf33ffb6b906db9f278041c5 \ No newline at end of file +676877aca235e620ee12d10235dd6ad009d4968455ec170daeb1998b94a7e0a2 \ No newline at end of file diff --git a/src/json.c b/src/json.c index e446830db9..e508209fc9 100644 --- a/src/json.c +++ b/src/json.c @@ -802,10 +802,8 @@ static void jsonReturn( c = '\t'; }else if( c=='v' ){ c = '\v'; - }else if( c=='\'' ){ - c = '\''; - }else if( c=='"' ){ - c = '"'; + }else if( c=='\'' || c=='"' || c=='/' ){ + /* pass through unchanged */ }else if( c=='0' ){ c = 0; }else if( c=='x' ){ From 8ded1642009400732782d4030d8d4359064531d0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 16:57:14 +0000 Subject: [PATCH 15/37] Fix handling of reverse solidus in string literals. Allow decimal points in floating point literals to occurs and the beginning or end of the mantissa. FossilOrigin-Name: d92a6ab2871095ac66c60cfa15dbafa7b762f83d287d452f61792eb30cf5b26b --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 31 ++++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 40fa719367..8880fe850e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\shandling\sof\sescape\ssolidus\sin\sthe\sJSON\sroutines. -D 2023-04-27T16:24:12.129 +C Fix\shandling\sof\sreverse\ssolidus\sin\sstring\sliterals.\s\sAllow\sdecimal\spoints\nin\sfloating\spoint\sliterals\sto\soccurs\sand\sthe\sbeginning\sor\send\sof\sthe\smantissa. +D 2023-04-27T16:57:14.640 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c cdf05600af31e37ad50924f39d7fa6227a7c9b94ea3d3690312cc1b7e15b36ef +F src/json.c d1a73af4f4185cfc355fa4b7be0cdc5cddd80b06da3d8ad3f966b8758e9027a1 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 66da4bd4a30c390fa1a7960ce2edaef82e63971ecf33ffb6b906db9f278041c5 -R 9748a62c2b5ef29db66fe80859611ec7 +P 676877aca235e620ee12d10235dd6ad009d4968455ec170daeb1998b94a7e0a2 +R 55fe1e7f7cae4da350b14dce9970e986 U drh -Z 8f82433213e0588347a902f584c3080a +Z eab22ac4584a027288ad76a2849798e8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9374e8e5aa..80675cd170 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -676877aca235e620ee12d10235dd6ad009d4968455ec170daeb1998b94a7e0a2 \ No newline at end of file +d92a6ab2871095ac66c60cfa15dbafa7b762f83d287d452f61792eb30cf5b26b \ No newline at end of file diff --git a/src/json.c b/src/json.c index e508209fc9..4ef53cd8ae 100644 --- a/src/json.c +++ b/src/json.c @@ -398,6 +398,7 @@ static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ } for(i=0; ihas5 = 1; jnFlags = JNODE_JSON5; goto parse_number; + case '.': + if( sqlite3Isdigit(z[i+1]) ){ + pParse->has5 = 1; + jnFlags = JNODE_JSON5; + seenE = 0; + seenDP = 1; + goto parse_number_2; + } + return -1; case '-': case '0': case '1': @@ -1303,6 +1313,7 @@ json_parse_restart: } } } + parse_number_2: for(j=i+1;; j++){ c = z[j]; if( c>='0' && c<='9' ) continue; @@ -1313,7 +1324,14 @@ json_parse_restart: continue; } if( c=='e' || c=='E' ){ - if( z[j-1]<'0' ) return -1; + if( z[j-1]<'0' ){ + if( z[j-1]=='.' && j-2>=i && sqlite3Isdigit(z[j-2]) ){ + pParse->has5 = 1; + jnFlags |= JNODE_JSON5; + }else{ + return -1; + } + } if( seenE ) return -1; seenDP = seenE = 1; c = z[j+1]; @@ -1344,7 +1362,14 @@ json_parse_restart: #endif break; } - if( z[j-1]<'0' ) return -1; + if( z[j-1]<'0' ){ + if( z[j-1]=='.' && j-2>=i && sqlite3Isdigit(z[j-2]) ){ + pParse->has5 = 1; + jnFlags |= JNODE_JSON5; + }else{ + return -1; + } + } jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, j - i, &z[i]); if( jnFlags && !pParse->oom ){ From 47dd0e735de7a9bf133fc5855d1e8c021beca351 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 17:32:29 +0000 Subject: [PATCH 16/37] All floating point literals "NaN" and "Infinity". Additional variants of these literals are available if compiled with SQLITE_EXTENDED_NAN_INF. FossilOrigin-Name: c13346afbecb92275e741252897d00478dab4be2d158889bc735e80efd9444f5 --- manifest | 12 ++++----- manifest.uuid | 2 +- src/json.c | 70 +++++++++++++++++++++++++++++++++++---------------- 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/manifest b/manifest index 8880fe850e..e9ac4de4a3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\shandling\sof\sreverse\ssolidus\sin\sstring\sliterals.\s\sAllow\sdecimal\spoints\nin\sfloating\spoint\sliterals\sto\soccurs\sand\sthe\sbeginning\sor\send\sof\sthe\smantissa. -D 2023-04-27T16:57:14.640 +C All\sfloating\spoint\sliterals\s"NaN"\sand\s"Infinity".\s\sAdditional\svariants\nof\sthese\sliterals\sare\savailable\sif\scompiled\swith\sSQLITE_EXTENDED_NAN_INF. +D 2023-04-27T17:32:29.533 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c d1a73af4f4185cfc355fa4b7be0cdc5cddd80b06da3d8ad3f966b8758e9027a1 +F src/json.c 530e1f4f4899a3c368bfaac63028e4c15ebd279a697780374d0cb7817103350a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 676877aca235e620ee12d10235dd6ad009d4968455ec170daeb1998b94a7e0a2 -R 55fe1e7f7cae4da350b14dce9970e986 +P d92a6ab2871095ac66c60cfa15dbafa7b762f83d287d452f61792eb30cf5b26b +R a7e436b837797408dc673da04c49a8e7 U drh -Z eab22ac4584a027288ad76a2849798e8 +Z 6274ce9cc076310c4a10615de56897ee # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 80675cd170..799d6b004d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d92a6ab2871095ac66c60cfa15dbafa7b762f83d287d452f61792eb30cf5b26b \ No newline at end of file +c13346afbecb92275e741252897d00478dab4be2d158889bc735e80efd9444f5 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 4ef53cd8ae..9029ba9f13 100644 --- a/src/json.c +++ b/src/json.c @@ -1031,7 +1031,7 @@ static int json5Whitespace(const char *zIn){ } -#ifdef SQLITE_ENABLE_JSON_NAN_INF +#ifdef SQLITE_EXTENDED_NAN_INF /* ** Extra floating-point literals to allow in JSON. */ @@ -1050,7 +1050,7 @@ static const struct NanInfName { { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, }; -#endif /* SQLITE_ENABLE_JSON_NAN_INF */ +#endif /* SQLITE_EXTENDED_NAN_INF */ /* ** Parse a single JSON value which begins at pParse->zJson[i]. Return the @@ -1306,7 +1306,35 @@ json_parse_restart: jnFlags = JNODE_JSON5; } }else{ - if( !sqlite3Isdigit(z[i+1]) ) return -1; + if( !sqlite3Isdigit(z[i+1]) ){ + if( z[i+1]=='I' + && (c=='-' || c=='+') + && strncmp(&z[i+1], "Infinity",8)==0 + ){ + if( z[i]=='-' ){ + jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); + }else{ + jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); + } + return i+9; + } +#ifdef SQLITE_EXTENDED_NAN_INF + /* Non-standard JSON and JSON5: Allow "Inf" as an alternative + ** spelling for "Infinity" and allow it to be in any case. */ + if( (z[i+1]=='I' || z[i+1]=='i') + && (c=='-' || c=='+') + && sqlite3StrNICmp(&z[i+1], "inf",3)==0 + ){ + if( z[i]=='-' ){ + jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); + }else{ + jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); + } + return i+4; + } +#endif + return -1; + } if( z[i+1]=='0' && sqlite3Isdigit(z[i+2]) ){ pParse->has5 = 1; jnFlags = JNODE_JSON5; @@ -1342,24 +1370,6 @@ json_parse_restart: if( c<'0' || c>'9' ) return -1; continue; } -#ifdef SQLITE_ENABLE_JSON_NAN_INF - /* Non-standard JSON: Allow "-Inf" (in any case) - ** to be understood as floating point literals. */ - if( (c=='i' || c=='I') - && j==i+1 - && z[i]=='-' - && sqlite3StrNICmp(&z[j], "inf",3)==0 - ){ - if( !sqlite3Isalnum(z[j+3]) ){ - jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); - return i+4; - }else if( (sqlite3StrNICmp(&z[j],"infinity",8)==0 && - !sqlite3Isalnum(z[j+8])) ){ - jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); - return i+9; - } - } -#endif break; } if( z[j-1]<'0' ){ @@ -1377,6 +1387,22 @@ json_parse_restart: } return j; } + case 'N': { + if( strncmp(&z[i],"NaN",3)==0 ){ + jsonParseAddNode(pParse, JSON_NULL, 4, "null"); + pParse->has5 = 1; + return i+3; + } + return -1; + } + case 'I': { + if( strncmp(&z[i],"Infinity",8)==0 ){ + jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); + pParse->has5 = 1; + return i+8; + } + return -1; + } case '}': { pParse->iErr = i; return -2; /* End of {...} */ @@ -1421,7 +1447,7 @@ json_parse_restart: return -1; } default: { -#ifdef SQLITE_ENABLE_JSON_NAN_INF +#ifdef SQLITE_EXTENDED_NAN_INF int k, nn; c = z[i]; for(k=0; k Date: Thu, 27 Apr 2023 18:28:10 +0000 Subject: [PATCH 17/37] Add support for hexadecimal integer literals in JSON. FossilOrigin-Name: 85e00c9e68d0695592e8f72555ee133c096bfca5a860a8e21d1e0ef756705aaf --- manifest | 14 ++++++------ manifest.uuid | 2 +- src/json.c | 62 ++++++++++++++++++++++++++++++--------------------- src/util.c | 4 +++- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/manifest b/manifest index e9ac4de4a3..1118208d40 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C All\sfloating\spoint\sliterals\s"NaN"\sand\s"Infinity".\s\sAdditional\svariants\nof\sthese\sliterals\sare\savailable\sif\scompiled\swith\sSQLITE_EXTENDED_NAN_INF. -D 2023-04-27T17:32:29.533 +C Add\ssupport\sfor\shexadecimal\sinteger\sliterals\sin\sJSON. +D 2023-04-27T18:28:10.882 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 530e1f4f4899a3c368bfaac63028e4c15ebd279a697780374d0cb7817103350a +F src/json.c e2830f713ac361dbdabcd9017acfb8e8e4a783ff5ca0f3925626edd8c7b11909 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -700,7 +700,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 3f4fb5ad7c9b48d7911974d6579192bb3a6c27f46140b6cbb9139cc8a77b8691 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c b1d8d87c4c8c77e70f48c43f91444fd66d91532693573b70b837afd572010176 +F src/util.c d4bcb560471cd94e6e17d448311f8d5bf81a7e5276295a53501058ef1b95dd1a F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd F src/vdbe.c a6c52ba65e8ceb574fe0eda62af84e6c50c176ffc5f310c613425f7ab2b1484b F src/vdbe.h 637ae853b7d42ae3951034cc63ab7c8af837861f79504cdb5399552fcd89a884 @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d92a6ab2871095ac66c60cfa15dbafa7b762f83d287d452f61792eb30cf5b26b -R a7e436b837797408dc673da04c49a8e7 +P c13346afbecb92275e741252897d00478dab4be2d158889bc735e80efd9444f5 +R 6e312c065e46414b9040d6fd248d4cee U drh -Z 6274ce9cc076310c4a10615de56897ee +Z e7a3b5e2b439e9e4434717ea6eca66ea # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 799d6b004d..30c4114d5d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c13346afbecb92275e741252897d00478dab4be2d158889bc735e80efd9444f5 \ No newline at end of file +85e00c9e68d0695592e8f72555ee133c096bfca5a860a8e21d1e0ef756705aaf \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9029ba9f13..10f6b9839d 100644 --- a/src/json.c +++ b/src/json.c @@ -372,6 +372,18 @@ static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ N--; } while( zIn[0]=='0' && N>1 ){ + if( zIn[1]=='x' || zIn[1]=='X' ){ + sqlite3_int64 i = 0; + int rc = sqlite3DecOrHexToI64(zIn, &i); + if( rc<=1 ){ + jsonPrintf(100,p,"%lld",i); + }else if( rc==2 ){ + jsonAppendRaw(p, "9.0e999", 7); + }else{ + jsonAppendRaw(p, "9223372036854775808", 18); + } + return; + } zIn++; N--; } @@ -684,44 +696,32 @@ static void jsonReturn( } case JSON_INT: { sqlite3_int64 i = 0; + int rc; + int bNeg = 0; const char *z; + + assert( pNode->eU==1 ); z = pNode->u.zJContent; - if( z[0]=='-' ){ z++; } - while( z[0]>='0' && z[0]<='9' ){ - unsigned v = *(z++) - '0'; - if( i>=LARGEST_INT64/10 ){ - if( i>LARGEST_INT64/10 ) goto int_as_real; - if( z[0]>='0' && z[0]<='9' ) goto int_as_real; - if( v==9 ) goto int_as_real; - if( v==8 ){ - if( pNode->u.zJContent[0]=='-' ){ - sqlite3_result_int64(pCtx, SMALLEST_INT64); - goto int_done; - }else{ - goto int_as_real; - } - } - } - i = i*10 + v; + if( z[0]=='-' ){ z++; bNeg = 1; } + else if( z[0]=='+' ){ z++; } + rc = sqlite3DecOrHexToI64(z, &i); + if( rc<=1 ){ + sqlite3_result_int64(pCtx, bNeg ? -i : i); + }else if( rc==3 && bNeg ){ + sqlite3_result_int64(pCtx, SMALLEST_INT64); + }else{ + goto to_double; } - if( pNode->u.zJContent[0]=='-' ){ i = -i; } - sqlite3_result_int64(pCtx, i); - int_done: break; - int_as_real: ; /* no break */ deliberate_fall_through } case JSON_REAL: { double r; -#ifdef SQLITE_AMALGAMATION const char *z; assert( pNode->eU==1 ); + to_double: z = pNode->u.zJContent; sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); -#else - assert( pNode->eU==1 ); - r = strtod(pNode->u.zJContent, 0); -#endif sqlite3_result_double(pCtx, r); break; } @@ -1370,6 +1370,16 @@ json_parse_restart: if( c<'0' || c>'9' ) return -1; continue; } + if( (c=='x' || c=='X') + && (j==i+1 || (j==i+2 && (z[i]=='-' || z[i]=='+'))) + && z[j-1]=='0' + && sqlite3Isxdigit(z[j+1]) + ){ + assert( seenDP==0 ); + pParse->has5 = 1; + jnFlags |= JNODE_JSON5; + for(j=j+2; sqlite3Isxdigit(z[j]); j++){} + } break; } if( z[j-1]<'0' ){ diff --git a/src/util.c b/src/util.c index 72e8a18b2f..3d4e014382 100644 --- a/src/util.c +++ b/src/util.c @@ -843,7 +843,9 @@ int sqlite3DecOrHexToI64(const char *z, i64 *pOut){ u = u*16 + sqlite3HexToInt(z[k]); } memcpy(pOut, &u, 8); - return (z[k]==0 && k-i<=16) ? 0 : 2; + if( k-i>16 ) return 2; + if( z[k]!=0 ) return 1; + return 0; }else #endif /* SQLITE_OMIT_HEX_INTEGER */ { From 1371df21178ff21728a2d06582ac849aefdb0159 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 19:13:20 +0000 Subject: [PATCH 18/37] Faster implementation of numberic literal parsing in JSON. FossilOrigin-Name: 58398292e8473b0b9e4b77dd5ed27334bc24a85d0c399e8d0b86de6fb59dadce --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 44 ++++++++++++++++++++++++-------------------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/manifest b/manifest index 1118208d40..ecb7db6751 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssupport\sfor\shexadecimal\sinteger\sliterals\sin\sJSON. -D 2023-04-27T18:28:10.882 +C Faster\simplementation\sof\snumberic\sliteral\sparsing\sin\sJSON. +D 2023-04-27T19:13:20.680 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c e2830f713ac361dbdabcd9017acfb8e8e4a783ff5ca0f3925626edd8c7b11909 +F src/json.c dc5befb58a71eca4c3703b2b62f4ef7bfe2087f261a21f84c0aa0f0d930e04f3 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c13346afbecb92275e741252897d00478dab4be2d158889bc735e80efd9444f5 -R 6e312c065e46414b9040d6fd248d4cee +P 85e00c9e68d0695592e8f72555ee133c096bfca5a860a8e21d1e0ef756705aaf +R 2024c2bf9c60e37e973abadb1fa7e9e0 U drh -Z e7a3b5e2b439e9e4434717ea6eca66ea +Z 1a9a05d48349450ebaef68906cd35ee3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 30c4114d5d..b162c93512 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -85e00c9e68d0695592e8f72555ee133c096bfca5a860a8e21d1e0ef756705aaf \ No newline at end of file +58398292e8473b0b9e4b77dd5ed27334bc24a85d0c399e8d0b86de6fb59dadce \ No newline at end of file diff --git a/src/json.c b/src/json.c index 10f6b9839d..ce33f83d54 100644 --- a/src/json.c +++ b/src/json.c @@ -1292,7 +1292,7 @@ json_parse_restart: /* Parse number */ jnFlags = 0; parse_number: - seenDP = 0; + seenDP = JSON_INT; seenE = 0; assert( '-' < '0' ); assert( '+' < '0' ); @@ -1304,6 +1304,12 @@ json_parse_restart: if( sqlite3Isdigit(z[i+1]) ){ pParse->has5 = 1; jnFlags = JNODE_JSON5; + }else if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ + assert( seenDP==JSON_INT ); + pParse->has5 = 1; + jnFlags |= JNODE_JSON5; + for(j=i+3; sqlite3Isxdigit(z[j]); j++){} + goto parse_number_finish; } }else{ if( !sqlite3Isdigit(z[i+1]) ){ @@ -1335,20 +1341,27 @@ json_parse_restart: #endif return -1; } - if( z[i+1]=='0' && sqlite3Isdigit(z[i+2]) ){ - pParse->has5 = 1; - jnFlags = JNODE_JSON5; + if( z[i+1]=='0' ){ + if( sqlite3Isdigit(z[i+2]) ){ + pParse->has5 = 1; + jnFlags = JNODE_JSON5; + }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ + pParse->has5 = 1; + jnFlags |= JNODE_JSON5; + for(j=i+4; sqlite3Isxdigit(z[j]); j++){} + goto parse_number_finish; + } } } } parse_number_2: for(j=i+1;; j++){ c = z[j]; - if( c>='0' && c<='9' ) continue; + if( sqlite3Isdigit(c) ) continue; if( c=='.' ){ if( z[j-1]=='-' ) return -1; - if( seenDP ) return -1; - seenDP = 1; + if( seenDP==JSON_REAL ) return -1; + seenDP = JSON_REAL; continue; } if( c=='e' || c=='E' ){ @@ -1361,7 +1374,8 @@ json_parse_restart: } } if( seenE ) return -1; - seenDP = seenE = 1; + seenDP = JSON_REAL; + seenE = 1; c = z[j+1]; if( c=='+' || c=='-' ){ j++; @@ -1370,16 +1384,6 @@ json_parse_restart: if( c<'0' || c>'9' ) return -1; continue; } - if( (c=='x' || c=='X') - && (j==i+1 || (j==i+2 && (z[i]=='-' || z[i]=='+'))) - && z[j-1]=='0' - && sqlite3Isxdigit(z[j+1]) - ){ - assert( seenDP==0 ); - pParse->has5 = 1; - jnFlags |= JNODE_JSON5; - for(j=j+2; sqlite3Isxdigit(z[j]); j++){} - } break; } if( z[j-1]<'0' ){ @@ -1390,8 +1394,8 @@ json_parse_restart: return -1; } } - jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, - j - i, &z[i]); + parse_number_finish: + jsonParseAddNode(pParse, seenDP, j - i, &z[i]); if( jnFlags && !pParse->oom ){ pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; } From ae31f48826a250096945ab1494dad4c1c30852ee Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 19:30:53 +0000 Subject: [PATCH 19/37] Further optimizations to the JSON parser. FossilOrigin-Name: bb8f1c16f244f893170f3d03bc445bd15fc337804c7c3e76c548397f5b95b39a --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 17 +++++------------ 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index ecb7db6751..a630d6827f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Faster\simplementation\sof\snumberic\sliteral\sparsing\sin\sJSON. -D 2023-04-27T19:13:20.680 +C Further\soptimizations\sto\sthe\sJSON\sparser. +D 2023-04-27T19:30:53.107 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c dc5befb58a71eca4c3703b2b62f4ef7bfe2087f261a21f84c0aa0f0d930e04f3 +F src/json.c 1c1cc509dd866dc05973eea6bd390c318c57fdad63c239ed3e2d214f78560dd5 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 85e00c9e68d0695592e8f72555ee133c096bfca5a860a8e21d1e0ef756705aaf -R 2024c2bf9c60e37e973abadb1fa7e9e0 +P 58398292e8473b0b9e4b77dd5ed27334bc24a85d0c399e8d0b86de6fb59dadce +R 89caa52021148a7b8eb691f84233609e U drh -Z 1a9a05d48349450ebaef68906cd35ee3 +Z e1f8b732b22a9ab7f249168903fe39b6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b162c93512..29f502947f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -58398292e8473b0b9e4b77dd5ed27334bc24a85d0c399e8d0b86de6fb59dadce \ No newline at end of file +bb8f1c16f244f893170f3d03bc445bd15fc337804c7c3e76c548397f5b95b39a \ No newline at end of file diff --git a/src/json.c b/src/json.c index ce33f83d54..72ec1af10e 100644 --- a/src/json.c +++ b/src/json.c @@ -891,8 +891,8 @@ static int jsonParseAddNode( return jsonParseAddNodeExpand(pParse, eType, n, zContent); } p = &pParse->aNode[pParse->nNode]; - p->eType = (u8)eType; - p->jnFlags = 0; + p->eType = (u8)(eType & 0xff); + p->jnFlags = (u8)(eType >> 8); VVA( p->eU = zContent ? 1 : 0 ); p->n = n; p->u.zJContent = zContent; @@ -1092,10 +1092,7 @@ json_parse_restart: if( sqlite3Isalpha(z[j]) || z[j]=='_' || z[j]=='$' ){ int k; for(k=j+1; sqlite3Isalnum(z[k]) || z[k]=='_' || z[k]=='$'; k++){} - jsonParseAddNode(pParse, JSON_STRING, k-j, &z[j]); - if( !pParse->oom ){ - pParse->aNode[pParse->nNode-1].jnFlags = JNODE_RAW; - } + jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]); pParse->has5 = 1; x = k; }else{ @@ -1239,8 +1236,7 @@ json_parse_restart: } j++; } - jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]); - if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; + jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]); return j+1; } case 'n': { @@ -1395,10 +1391,7 @@ json_parse_restart: } } parse_number_finish: - jsonParseAddNode(pParse, seenDP, j - i, &z[i]); - if( jnFlags && !pParse->oom ){ - pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; - } + jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]); return j; } case 'N': { From 52da6d26074a6aa3e3aed0e16b18a91b068490ca Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Apr 2023 23:29:09 +0000 Subject: [PATCH 20/37] Test cases added, and some bugs fixed. FossilOrigin-Name: bc84a82e4ddc1b71025c56c49e62a44f0b12fa87a6417ad61967d9d3121a0d4e --- manifest | 13 +- manifest.uuid | 2 +- src/json.c | 18 ++- test/json501.test | 307 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 328 insertions(+), 12 deletions(-) create mode 100644 test/json501.test diff --git a/manifest b/manifest index a630d6827f..dcbc60c91c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\soptimizations\sto\sthe\sJSON\sparser. -D 2023-04-27T19:30:53.107 +C Test\scases\sadded,\sand\ssome\sbugs\sfixed. +D 2023-04-27T23:29:09.447 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 1c1cc509dd866dc05973eea6bd390c318c57fdad63c239ed3e2d214f78560dd5 +F src/json.c 76c7d11f7a5f29eaf3180c6ffcdf7fa808e931f519295d0725a736be8956a5cd F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -1258,6 +1258,7 @@ F test/json102.test 1f61f469d763ff26430dbee76bc75e0aa73084ca84f10e58744fdb899e56 F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d +F test/json501.test 43614faefc9ac708e29d55cdc290b5cea336c151030196885708ea3d132fce63 F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 @@ -2063,8 +2064,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 58398292e8473b0b9e4b77dd5ed27334bc24a85d0c399e8d0b86de6fb59dadce -R 89caa52021148a7b8eb691f84233609e +P bb8f1c16f244f893170f3d03bc445bd15fc337804c7c3e76c548397f5b95b39a +R 6a60f8ad3d57ac84f5b8c0860a0c4b76 U drh -Z e1f8b732b22a9ab7f249168903fe39b6 +Z d1a0a8388ccda2f713b3fe179f34f0f5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 29f502947f..89e8dac194 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bb8f1c16f244f893170f3d03bc445bd15fc337804c7c3e76c548397f5b95b39a \ No newline at end of file +bc84a82e4ddc1b71025c56c49e62a44f0b12fa87a6417ad61967d9d3121a0d4e \ No newline at end of file diff --git a/src/json.c b/src/json.c index 72ec1af10e..6bac63ec51 100644 --- a/src/json.c +++ b/src/json.c @@ -816,7 +816,7 @@ static void jsonReturn( }else if( 0xe2==(u8)c ){ assert( 0x80==(u8)z[i+1] ); assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); - i+= 2; + i += 2; continue; }else{ continue; @@ -1221,13 +1221,17 @@ json_parse_restart: || c=='n' || c=='r' || c=='t' || (c=='u' && jsonIs4Hex(&z[j+1])) ){ jnFlags |= JNODE_ESCAPE; - }else if( c=='\'' || c=='0' || c=='v' - || c=='\r' || c=='\n' + }else if( c=='\'' || c=='0' || c=='v' || c=='\n' || (0xe2==(u8)c && 0x80==(u8)z[j+1] && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) || (c=='x' && jsonIs2Hex(&z[j+1])) ){ jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); pParse->has5 = 1; + }else if( c=='\r' ){ + j++; + if( z[j+1]=='\n' ) j++; + jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); + pParse->has5 = 1; }else{ return -1; } @@ -1270,7 +1274,7 @@ json_parse_restart: pParse->has5 = 1; jnFlags = JNODE_JSON5; seenE = 0; - seenDP = 1; + seenDP = JSON_REAL; goto parse_number_2; } return -1; @@ -1335,6 +1339,11 @@ json_parse_restart: return i+4; } #endif + if( z[i+1]=='.' ){ + pParse->has5 = 1; + jnFlags |= JNODE_JSON5; + goto parse_number_2; + } return -1; } if( z[i+1]=='0' ){ @@ -1355,7 +1364,6 @@ json_parse_restart: c = z[j]; if( sqlite3Isdigit(c) ) continue; if( c=='.' ){ - if( z[j-1]=='-' ) return -1; if( seenDP==JSON_REAL ) return -1; seenDP = JSON_REAL; continue; diff --git a/test/json501.test b/test/json501.test new file mode 100644 index 0000000000..fbf7bfe9db --- /dev/null +++ b/test/json501.test @@ -0,0 +1,307 @@ +# 2023-04-27 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements tests for the JSON5 enhancements to the +# JSON SQL functions extension to the SQLite library. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix json501 + +# From https://spec.json5.org/#introduction +# +#----------------------------------------------------------------------------- +# Summary of Features +# +# The following ECMAScript 5.1 features, which are not supported in JSON, have +# been extended to JSON5. +# +# Objects +# +# 1) Object keys may be an ECMAScript 5.1 IdentifierName. +# 2) Objects may have a single trailing comma. +# +# Arrays +# +# 3) Arrays may have a single trailing comma. +# +# Strings +# +# 4) Strings may be single quoted. +# 5) Strings may span multiple lines by escaping new line characters. +# 6) Strings may include character escapes. +# +# Numbers +# +# 7) Numbers may be hexadecimal. +# 8) Numbers may have a leading or trailing decimal point. +# 9) Numbers may be IEEE 754 positive infinity, negative infinity, and NaN. +# 10) Numbers may begin with an explicit plus sign. +# +# Comments +# +# 11) Single and multi-line comments are allowed. +# +# White Space +# +# 12) Additional white space characters are allowed. +#----------------------------------------------------------------------------- +# +# Test number in this file are of the form X.Y where X is one of the item +# numbers in the feature list above and Y is the test sequence number. +# + +############################################################################### +# 1) Object keys may be an ECMAScript 5.1 IdentifierName. +do_execsql_test 1.1 { + WITH c(x) AS (VALUES('{a:5,b:6}')) + SELECT x->>'a', json(x), json_valid(x), json_valid5(x) FROM c; +} {5 {{"a":5,"b":6}} 0 1} +do_execsql_test 1.2 { + SELECT '[7,null,{a:5,b:6},[8,9]]'->>'$[2].b'; +} {6} +do_execsql_test 1.3 { + SELECT '{ $123 : 789 }'->>'$."$123"'; +} 789 +do_execsql_test 1.4 { + SELECT '{ _123$xyz : 789 }'->>'$."_123$xyz"'; +} 789 +do_execsql_test 1.5 { + SELECT '{ MNO_123$xyz : 789 }'->>'$."MNO_123$xyz"'; +} 789 + +do_execsql_test 1.6 { + SELECT json('{ MNO_123$xyz : 789 }'); +} [list {{"MNO_123$xyz":789}}] + +do_catchsql_test 1.10 { + SELECT json('{ MNO_123/xyz : 789 }'); +} {1 {malformed JSON}} + +# Contra the JSON5 standard, SQLite does not allow non-ASCII characters in +# an unquoted object label. +# +do_catchsql_test 1.11 { + SELECT json('{ MNO_123æxyz : 789 }'); +} {1 {malformed JSON}} + +############################################################################### +# 2) Objects may have a single trailing comma. + +do_execsql_test 2.1 { + WITH c(x) AS (VALUES('{"a":5, "b":6, }')) + SELECT x->>'b', json(x), json_valid(x), json_valid5(x) FROM c; +} {6 {{"a":5,"b":6}} 0 1} +do_execsql_test 2.2 { + SELECT '{a:5, b:6 , }'->>'b'; +} 6 +do_catchsql_test 2.3 { + SELECT '{a:5, b:6 ,, }'->>'b'; +} {1 {malformed JSON}} +do_catchsql_test 2.4 { + SELECT '{a:5, b:6, ,}'->>'b'; +} {1 {malformed JSON}} + +############################################################################### +# 3) Arrays may have a single trailing comma. + +do_execsql_test 3.1 { + WITH c(x) AS (VALUES('[5, 6,]')) + SELECT x->>1, json(x), json_valid(x), json_valid5(x) FROM c; +} {6 {[5,6]} 0 1} +do_execsql_test 3.2 { + SELECT '[5, 6 , ]'->>1; +} 6 +do_catchsql_test 3.3 { + SELECT '[5, 6,,]'->>1; +} {1 {malformed JSON}} +do_catchsql_test 3.4 { + SELECT '[5, 6 , , ]'->>1; +} {1 {malformed JSON}} + +############################################################################### +# 4) Strings may be single quoted. + +do_execsql_test 4.1 { + WITH c(x) AS (VALUES('{"a": ''abcd''}')) + SELECT x->>'a', json(x), json_valid(x), json_valid5(x) FROM c; +} {abcd {{"a":"abcd"}} 0 1} +do_execsql_test 4.2 { + SELECT '{b: 123, ''a'': ''ab\''cd''}'->>'a'; +} {ab'cd} + +############################################################################### +# 5) Strings may span multiple lines by escaping new line characters. + +do_execsql_test 5.1 { + WITH c(x) AS (VALUES('{a: "abc'||char(0x5c,0x0a)||'xyz"}')) + SELECT x->>'a', json(x), json_valid(x), json_valid5(x) FROM c; +} {abcxyz {{"a":"abcxyz"}} 0 1} +do_execsql_test 5.2 { + SELECT ('{a: "abc'||char(0x5c,0x0d)||'xyz"}')->>'a'; +} {abcxyz} +do_execsql_test 5.3 { + SELECT ('{a: "abc'||char(0x5c,0x0d,0x0a)||'xyz"}')->>'a'; +} {abcxyz} +do_execsql_test 5.4 { + SELECT ('{a: "abc'||char(0x5c,0x2028)||'xyz"}')->>'a'; +} {abcxyz} +do_execsql_test 5.5 { + SELECT ('{a: "abc'||char(0x5c,0x2029)||'xyz"}')->>'a'; +} {abcxyz} + + +############################################################################### +# 6) Strings may include character escapes. + +do_execsql_test 6.1 { + SELECT ('{a: "abc'||char(0x5c,0x27)||'xyz"}')->>'a'; +} {abc'xyz} +do_execsql_test 6.2 { + SELECT ('{a: "abc'||char(0x5c,0x22)||'xyz"}')->>'a'; +} {abc"xyz} +do_execsql_test 6.3 { + SELECT ('{a: "abc'||char(0x5c,0x5c)||'xyz"}')->>'a'; +} {{abc\xyz}} +do_execsql_test 6.4 { + SELECT hex(('{a: "abc\bxyz"}')->>'a'); +} {6162630878797A} +do_execsql_test 6.5 { + SELECT hex(('{a: "abc\f\n\r\t\vxyz"}')->>'a'); +} {6162630C0A0D090B78797A} +do_execsql_test 6.6 { + SELECT hex(('{a: "abc\0xyz"}')->>'a'); +} {6162630078797A} +do_execsql_test 6.7 { + SELECT '{a: "abc\x35\x4f\x6Exyz"}'->>'a'; +} {abc5Onxyz} +do_execsql_test 6.8 { + SELECT '{a: "\x6a\x6A\x6b\x6B\x6c\x6C\x6d\x6D\x6e\x6E\x6f\x6F"}'->>'a'; +} {jjkkllmmnnoo} + +############################################################################### +# 7) Numbers may be hexadecimal. + +do_execsql_test 7.1 { + SELECT '{a: 0x0}'->>'a'; +} 0 +do_execsql_test 7.2 { + SELECT '{a: -0x0}'->>'a'; +} 0 +do_execsql_test 7.3 { + SELECT '{a: +0x0}'->>'a'; +} 0 +do_execsql_test 7.4 { + SELECT '{a: 0xabcdef}'->>'a'; +} 11259375 +do_execsql_test 7.5 { + SELECT '{a: -0xaBcDeF}'->>'a'; +} -11259375 +do_execsql_test 7.6 { + SELECT '{a: +0xABCDEF}'->>'a'; +} 11259375 + +############################################################################### +# 8) Numbers may have a leading or trailing decimal point. + +do_execsql_test 8.1 { + WITH c(x) AS (VALUES('{x: 4.}')) SELECT x->>'x', json(x) FROM c; +} {4.0 {{"x":4.0}}} +do_execsql_test 8.2 { + WITH c(x) AS (VALUES('{x: +4.}')) SELECT x->>'x', json(x) FROM c; +} {4.0 {{"x":4.0}}} +do_execsql_test 8.3 { + WITH c(x) AS (VALUES('{x: -4.}')) SELECT x->>'x', json(x) FROM c; +} {-4.0 {{"x":-4.0}}} +do_execsql_test 8.3 { + WITH c(x) AS (VALUES('{x: .5}')) SELECT x->>'x', json(x) FROM c; +} {0.5 {{"x":0.5}}} +do_execsql_test 8.4 { + WITH c(x) AS (VALUES('{x: -.5}')) SELECT x->>'x', json(x) FROM c; +} {-0.5 {{"x":-0.5}}} +do_execsql_test 8.5 { + WITH c(x) AS (VALUES('{x: +.5}')) SELECT x->>'x', json(x) FROM c; +} {0.5 {{"x":0.5}}} +do_execsql_test 8.6 { + WITH c(x) AS (VALUES('{x: 4.e0}')) SELECT x->>'x', json(x) FROM c; +} {4.0 {{"x":4.0e0}}} +do_execsql_test 8.7 { + WITH c(x) AS (VALUES('{x: +4.e1}')) SELECT x->>'x', json(x) FROM c; +} {40.0 {{"x":4.0e1}}} +do_execsql_test 8.8 { + WITH c(x) AS (VALUES('{x: -4.e2}')) SELECT x->>'x', json(x) FROM c; +} {-400.0 {{"x":-4.0e2}}} +do_execsql_test 8.9 { + WITH c(x) AS (VALUES('{x: .5e3}')) SELECT x->>'x', json(x) FROM c; +} {500.0 {{"x":0.5e3}}} +do_execsql_test 8.10 { + WITH c(x) AS (VALUES('{x: -.5e-1}')) SELECT x->>'x', json(x) FROM c; +} {-0.05 {{"x":-0.5e-1}}} +do_execsql_test 8.11 { + WITH c(x) AS (VALUES('{x: +.5e-2}')) SELECT x->>'x', json(x) FROM c; +} {0.005 {{"x":0.5e-2}}} + + +############################################################################### +# 9) Numbers may be IEEE 754 positive infinity, negative infinity, and NaN. + +do_execsql_test 9.1 { + WITH c(x) AS (VALUES('{x: +Infinity}')) SELECT x->>'x', json(x) FROM c; +} {Inf {{"x":9.0e999}}} +do_execsql_test 9.2 { + WITH c(x) AS (VALUES('{x: -Infinity}')) SELECT x->>'x', json(x) FROM c; +} {-Inf {{"x":-9.0e999}}} +do_execsql_test 9.3 { + WITH c(x) AS (VALUES('{x: Infinity}')) SELECT x->>'x', json(x) FROM c; +} {Inf {{"x":9.0e999}}} +do_execsql_test 9.4 { + WITH c(x) AS (VALUES('{x: NaN}')) SELECT x->>'x', json(x) FROM c; +} {{} {{"x":null}}} + +############################################################################### +# 10) Numbers may begin with an explicit plus sign. + +do_execsql_test 10.1 { + SELECT '{a: +123}'->'a'; +} 123 + +############################################################################### +# 11) Single and multi-line comments are allowed. + +do_execsql_test 11.1 { + SELECT ' /* abc */ { /*def*/ aaa /* xyz */ : // to the end of line + 123 /* xyz */ , /* 123 */ }'->>'aaa'; +} 123 + +############################################################################### +# 12) Additional white space characters are allowed. + +do_execsql_test 12.1 { + SELECT (char(0x09,0x0a,0x0b,0x0c,0x0d,0x20,0xa0,0x2028,0x2029) + || '{a: "xyz"}')->>'a'; +} xyz +do_execsql_test 12.2 { + SELECT ('{a:' || char(0x09,0x0a,0x0b,0x0c,0x0d,0x20,0xa0,0x2028,0x2029) + || '"xyz"}')->>'a'; +} xyz +do_execsql_test 12.3 { + SELECT (char(0x1680,0x2000,0x2001,0x2002,0x2003,0x2004,0x2005, + 0x2006,0x2007,0x2008,0x2009,0x200a,0x3000,0xfeff) + || '{a: "xyz"}')->>'a'; +} xyz +do_execsql_test 12.4 { + SELECT ('{a: ' ||char(0x1680,0x2000,0x2001,0x2002,0x2003,0x2004,0x2005, + 0x2006,0x2007,0x2008,0x2009,0x200a,0x3000,0xfeff) + || ' "xyz"}')->>'a'; +} xyz + + +finish_test From 1170fb52e12c5476ac8cf03216609c6824953982 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Apr 2023 11:02:01 +0000 Subject: [PATCH 21/37] Fix json_tree() so that it is able to deal with the JNODE_RAW labels of a JSON5 object. FossilOrigin-Name: f56528d413d8e622f7c4f18b2f9f2e620bfb441c020461299b35a90072ee6c13 --- manifest | 13 +++++++------ manifest.uuid | 2 +- src/json.c | 26 +++++++++++--------------- test/json502.test | 25 +++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 test/json502.test diff --git a/manifest b/manifest index b8b8e777ef..90577f373d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\sthe\slatest\strunk\sfixes\sand\senhancements\sinto\sthe\sjson5\sbranch. -D 2023-04-28T10:23:01.697 +C Fix\sjson_tree()\sso\sthat\sit\sis\sable\sto\sdeal\swith\sthe\sJNODE_RAW\slabels\sof\na\sJSON5\sobject. +D 2023-04-28T11:02:01.468 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -593,7 +593,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 76c7d11f7a5f29eaf3180c6ffcdf7fa808e931f519295d0725a736be8956a5cd +F src/json.c 7d03a1185d7aee74f53a3f2381355ff4b71d955f287d29d30c0b0979afc96b67 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -1260,6 +1260,7 @@ F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a888011 F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d F test/json501.test 43614faefc9ac708e29d55cdc290b5cea336c151030196885708ea3d132fce63 +F test/json502.test 66d150cc098674b8bf4354526a8dd411b926f43ca892306bcb3b6d3f93fef7be F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 @@ -2065,8 +2066,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P bc84a82e4ddc1b71025c56c49e62a44f0b12fa87a6417ad61967d9d3121a0d4e 91fee79a01971259b21478e60a069a711a00efc79ddfececa6224a152cd8d09a -R 454da0ecccffebadc86f13fb2a470b64 +P b5ca15cfc19380cf870b70be6a86e70f2026cc3d6d89005b45891d58c4f11c2d +R fca31e8533435392332b7b8d5d313d4f U drh -Z dbd4148e71a7855c43311d70f48a2c21 +Z 70e5d0234b5092ca91c1a96a87fa5d9d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index eb3f235ab7..3b090148dc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b5ca15cfc19380cf870b70be6a86e70f2026cc3d6d89005b45891d58c4f11c2d \ No newline at end of file +f56528d413d8e622f7c4f18b2f9f2e620bfb441c020461299b35a90072ee6c13 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6bac63ec51..1152d9f916 100644 --- a/src/json.c +++ b/src/json.c @@ -726,17 +726,11 @@ static void jsonReturn( break; } case JSON_STRING: { -#if 0 /* Never happens because JNODE_RAW is only set by json_set(), - ** json_insert() and json_replace() and those routines do not - ** call jsonReturn() */ if( pNode->jnFlags & JNODE_RAW ){ assert( pNode->eU==1 ); sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, SQLITE_TRANSIENT); - }else -#endif - assert( (pNode->jnFlags & JNODE_RAW)==0 ); - if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ + }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ /* JSON formatted without any backslash-escapes */ assert( pNode->eU==1 ); sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, @@ -2872,14 +2866,16 @@ static void jsonAppendObjectPathElement( assert( pNode->eU==1 ); z = pNode->u.zJContent; nn = pNode->n; - assert( nn>=2 ); - assert( z[0]=='"' ); - assert( z[nn-1]=='"' ); - if( nn>2 && sqlite3Isalpha(z[1]) ){ - for(jj=2; jjjnFlags & JNODE_RAW)==0 ){ + assert( nn>=2 ); + assert( z[0]=='"' ); + assert( z[nn-1]=='"' ); + if( nn>2 && sqlite3Isalpha(z[1]) ){ + for(jj=2; jj Date: Fri, 28 Apr 2023 13:25:35 +0000 Subject: [PATCH 22/37] Fix indentation and omit trailing whitespace in the random JSON generator script. FossilOrigin-Name: 629db09fceb7bf37561b52ccee06ebf4df261291e9a8ffcca82b243f6db5ff07 --- manifest | 12 +- manifest.uuid | 2 +- test/json/json-generator.tcl | 540 +++++++++++++++++------------------ 3 files changed, 277 insertions(+), 277 deletions(-) diff --git a/manifest b/manifest index 90577f373d..e9188432cd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sjson_tree()\sso\sthat\sit\sis\sable\sto\sdeal\swith\sthe\sJNODE_RAW\slabels\sof\na\sJSON5\sobject. -D 2023-04-28T11:02:01.468 +C Fix\sindentation\sand\somit\strailing\swhitespace\sin\sthe\srandom\sJSON\sgenerator\nscript. +D 2023-04-28T13:25:35.641 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1251,7 +1251,7 @@ F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json/README.md 506af1f54574b524106acb50d1a341ab5ddfa6d83fe25095007892b07e663e85 -F test/json/json-generator.tcl 229bd293f1865f787c160886cadd282631721925cca2947aaa54bbcd7f65cef7 +F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd28656fb261bddc8a3f F test/json/json-q1.txt 335a7c8ab291d354f33b7decc9559e99a2823d4142291c4be7aa339a631f3c2d F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3b1f1511d21af6737 x F test/json101.test de9c93169b84ac96fd5836c638a2ae1f00e4afbd4003c6b596692d7f05e1cd69 @@ -2066,8 +2066,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b5ca15cfc19380cf870b70be6a86e70f2026cc3d6d89005b45891d58c4f11c2d -R fca31e8533435392332b7b8d5d313d4f +P f56528d413d8e622f7c4f18b2f9f2e620bfb441c020461299b35a90072ee6c13 +R 5da141bb06598ed103d496559df7ae36 U drh -Z 70e5d0234b5092ca91c1a96a87fa5d9d +Z 790f8e2b554134402e5889a19faba52a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3b090148dc..d1f6aa531d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f56528d413d8e622f7c4f18b2f9f2e620bfb441c020461299b35a90072ee6c13 \ No newline at end of file +629db09fceb7bf37561b52ccee06ebf4df261291e9a8ffcca82b243f6db5ff07 \ No newline at end of file diff --git a/test/json/json-generator.tcl b/test/json/json-generator.tcl index e99d6bccb0..d499bc7300 100644 --- a/test/json/json-generator.tcl +++ b/test/json/json-generator.tcl @@ -10,276 +10,276 @@ # expr srand(12345678) set wordlist { -ability able abroad access account act - action active actor add address adept - adroit advance advice affect age ageless - agency agent agile agree air airfare - airline airport alert almond alpha always - amend amount amplify analyst anchor angel - angelic angle ankle annual answer antique - anybody anyhow appeal apple apricot apt - area argon arm army arrival arsenic - art artful article arugula aside ask - aspect assist assume atom atone attempt - author autumn average avocado award awl - azure back bacon bag bagel bake - baker balance ball balloon bamboo banana - band banjo bank barium base basil - basin basis basket bass bat bath - battery beach beak bean bear bearcub - beauty beef beet beige being bell - belly belt bench bend benefit best - beta better beyond bicycle bid big - bike bill bird biscuit bismuth bisque - bit black blank blest blind bliss - block bloom blue board boat body - bokchoy bone bonus book bookish boot - border boron boss bossy bottle bottom - bow bowl bowtie box brain brainy - branch brave bravely bread break breath - breezy brick bridge brie brief briefly - bright broad broil bromine bronze brother - brow brown brush buddy budget buffalo - bug bugle bull bunch burger burly - burrito bus busy butter button buy - buyer byte cab cabbage cabinet cable - cadet cadmium caesium cake calcium caliper - call caller calm calmly camera camp - can canary cancel candle candy cap - capable caper capital captain car carbon - card care career careful carp carpet - carrot carry case cash cassava casual - cat catch catfish catsear catsup cause - cave celery cell century chain chair - chalk chance change channel chapter chard - charge charity chart check cheddar cheery - cheese chicken chicory chiffon child chin - chip chives choice chowder chum church - circle city claim clam class classic - classy clay clean cleaner clear clearly - clerk click client climate clock clorine - closet clothes cloud clown club clue - cluster coach coast coat cobbler cobolt - cod code coffee colby cold collar - college comb combine comet comfort command - comment common company complex concept concern - concert conduit consist contact contest context - control convert cook cookie copilot copper - copy coral cordial corn corner corny - correct cost count counter country county - couple courage course court cover cow - cowbird crab crack craft crash crazy - cream credit creek cress crevice crew - crimson croaker crop cross crowd cube - cuckoo cuisine culture cup current curve - cut cyan cycle dagger daily dance - dare darter data date day daylily - deal dear dearly debate debit decade - decimal deep deft deftly degree delay - deluxe deposit depth design desk detail - device dew diamond diet dig dill - dinner dip direct dirt dish disk - display diver divide divine doctor dodger - donut door dot double dough draft - drag dragon drama draw drawer drawing - dream drill drink drive driver drop - drum dry dryer drywall duck due - dump dusk dust duty dye eagle - ear earring earth ease east easy - eat economy edge editor eel effect - effort egg eight elbow elegant element - elf elk email emerald employ end - endive endless energy engine enjoy enter - entry equal equip error escape essay - eternal evening event exam example excuse - exit expert extent extreme eye face - fact factor factual fail failure fair - fajita fall family fan fang farm - farmer fat fault feature feed feel - feeling fench fennel festive few fiber - field fig figure file fill film - filter final finance finding finger finish - fire fish fishing fit fitting five - fix flier flight floor floral florine - flour flow flower fly flying focus - fold folding food foot force forest - forever forgive form formal format fortune - forum frame free freedom freely fresh - friend frog front fruit fuchsia fuel - fun funny future gain galaxy gallium - game gamma gap garage garden garlic - gas gate gather gauge gear gem - gene general gentle gently gherkin ghost - gift give glad glass gleeful glossy - glove glue goal goat goby gold - goldeye golf good gouda goulash gourd - grab grace grade gram grand grape - grapes grass gravy gray great green - grits grocery ground group grouper grout - growth guard guave guess guest guide - guitar gumbo guppy habit hacksaw haddock - hafnium hagfish hair half halibut hall - hammer hand handle handy hanger happy - hat havarti hay haybale head health - healthy hearing heart hearty heat heavy - heel height helium hello help helpful - herald herring hide high highly highway - hill hip hipster hire history hit - hoki hold hole holiday holly home - honest honey hook hope hopeful horizon - horn horse host hotel hour house - housing human humane humor hunt hurry - ice icecube icefish icy idea ideal - image impact impress inch income indigo - initial inkpen insect inside intense invite - iodine iridium iron island issue item - ivory jacket jargon javelin jello jelly - jewel job jocund join joint joke - jovial joy joyful joyous judge juice - jump junior jury just justice kale - keel keep kelp ketchup key keyhole - keyway khaki kick kid kidney kiloohm - kind kindly king kitchen kite kiwi - knee knife krill krypton kumquat lab - lace lack ladder lake lamp lamprey - land laser laugh law lawn lawyer - layer lead leader leading leaf leafy - league leather leave lecture leek leg - lemon length lentil lesson let letter - lettuce level library life lift light - lily lime limit line linen link - lip list listen lithium lively living - lizard load loan lobster local lock - log long longfin look lotus love - lovely loving low lucid luck luffa - lunch lung machine magenta magnet mail - main major make mall manager mango - manner many map march market maroon - martian master match math matter maximum - maybe meal meaning meat media medium - meet meeting melody melon member memory - mention menu mercury merry mess message - messy metal meter method micron middle - might mile milk mind mine minimum - minnow minor mint minute mirror miss - mission misty mix mixer mixture mobile - mode model moment monitor monk month - moon moray morning most motor mouse - mouth move mover movie much mud - mudfish muffin mullet munster muon muscle - music mustard nail name nation native - natural nature navy neat neatly nebula - neck needle neon nerve net network - neutron news nibble nice nickel night - niobium nobody noise noodle normal north - nose note nothing notice nova novel - number nurse nursery oar object offer - office officer oil okay okra old - olive one onion open opening opinion - option orange orbit orchid order oregano - other ounce outcome outside oven owner - oxygen oyster pace pack package page - pager paint pair pale pan pancake - papaya paper pardon parent park parking - parsley parsnip part partner party pass - passage past pasta path patient pattern - pause pay pea peace peach peacock - peahen peak peanut pear pearl pen - penalty pencil pension people pepper perch - perfect period permit person phase phone - photo phrase physics piano pick picture - pie piece pigeon pike pilot pin - pink pinkie pious pipe pitch pizza - place plan plane planet plant planter - plastic plate play player playful plenty - pliers plum pod poem poet poetry - point police policy pollock pony pool - pop popover poptart pork port portal - post pot potato pound powder power - present press price pride primary print - prior private prize problem process produce - product profile profit program project promise - prompt proof proper protein proton public - puff puffer pull pumpkin pup pupfish - pure purple purpose push put quality - quark quarter quiet quill quit quote - rabbit raccoon race radiant radio radish - radium radon rain rainbow raise ramp - ranch range rasp rate ratio ray - razor reach read reading real reality - reason recipe record recover red redeem - reed reef refuse region regret regular - relaxed release relief relish remote remove - rent repair repeat reply report request - reserve resist resolve resort rest result - return reveal review reward ribbon rice - rich ride ridge right ring rise - risk river rivet road roast rock - rocket role roll roof room rope - rose rough roughy round row royal - rub ruby rudder ruin rule run - runner rush rust sacred saddle safe - safety sail salad salami sale salmon - salt sample sand sander sandy sauce - save saving saw scale scampi scene - scheme school score screen script sea - search season seat second secret sector - seemly self sell senate senior sense - series serve set shake shape share - shark shell shift shine shiny ship - shock shoe shoot shop shovel show - side sign signal silk silly silver - simple sing singer single sink site - size skill skin sky slate sleep - sleepy slice slide slip smart smell - smelt smile smoke smooth snap snipe - snow snowy sock socket sodium soft - softly soil sole solid song sorrel - sort soul sound soup source south - space spare speech speed spell spend - sphere spice spider spirit spite split - spoon sport spot spray spread spring - squab square squash stable staff stage - stand staple star start state status - stay steak steel step stern stew - stick still stock stone stop store - storm story strain street stress strike - string stroke strong studio study stuff - style sugar suit sulfur summer sun - sunny sunset super superb surf survey - sweet swim swing switch symbol system - table tackle tail tale talk tan - tank tap tape target task taste - tau tea teach teal team tear - tell ten tender tennis tent term - test tetra text thanks theme theory - thing think thread throat thumb ticket - tidy tie tiger till time timely - tin tip title toast today toe - tomato tone tongue tool tooth top - topic total touch tough tour towel - tower town track trade train trash - travel tray treat tree trick trip - trout trowel truck trupet trust truth - try tube tuna tune turf turkey - turn turnip tutor tux tweet twist - two type union unique unit upbeat - upper use useful user usual valley - value van vase vast veil vein - velvet verse very vessel vest video - view violet visit visual vivid voice - volume vowel voyage waffle wait wake - walk wall warm warmth wasabi wash - watch water wave wax way wealth - wear web wedge week weekly weight - west whale what wheat wheel when - where while who whole why will - win wind window wing winner winter - wire wish witty wolf wonder wood - wool woolly word work worker world - worry worth worthy wrap wrench wrist - writer xenon yak yam yard yarrow - year yearly yellow yew yogurt young + ability able abroad access account act + action active actor add address adept + adroit advance advice affect age ageless + agency agent agile agree air airfare + airline airport alert almond alpha always + amend amount amplify analyst anchor angel + angelic angle ankle annual answer antique + anybody anyhow appeal apple apricot apt + area argon arm army arrival arsenic + art artful article arugula aside ask + aspect assist assume atom atone attempt + author autumn average avocado award awl + azure back bacon bag bagel bake + baker balance ball balloon bamboo banana + band banjo bank barium base basil + basin basis basket bass bat bath + battery beach beak bean bear bearcub + beauty beef beet beige being bell + belly belt bench bend benefit best + beta better beyond bicycle bid big + bike bill bird biscuit bismuth bisque + bit black blank blest blind bliss + block bloom blue board boat body + bokchoy bone bonus book bookish boot + border boron boss bossy bottle bottom + bow bowl bowtie box brain brainy + branch brave bravely bread break breath + breezy brick bridge brie brief briefly + bright broad broil bromine bronze brother + brow brown brush buddy budget buffalo + bug bugle bull bunch burger burly + burrito bus busy butter button buy + buyer byte cab cabbage cabinet cable + cadet cadmium caesium cake calcium caliper + call caller calm calmly camera camp + can canary cancel candle candy cap + capable caper capital captain car carbon + card care career careful carp carpet + carrot carry case cash cassava casual + cat catch catfish catsear catsup cause + cave celery cell century chain chair + chalk chance change channel chapter chard + charge charity chart check cheddar cheery + cheese chicken chicory chiffon child chin + chip chives choice chowder chum church + circle city claim clam class classic + classy clay clean cleaner clear clearly + clerk click client climate clock clorine + closet clothes cloud clown club clue + cluster coach coast coat cobbler cobolt + cod code coffee colby cold collar + college comb combine comet comfort command + comment common company complex concept concern + concert conduit consist contact contest context + control convert cook cookie copilot copper + copy coral cordial corn corner corny + correct cost count counter country county + couple courage course court cover cow + cowbird crab crack craft crash crazy + cream credit creek cress crevice crew + crimson croaker crop cross crowd cube + cuckoo cuisine culture cup current curve + cut cyan cycle dagger daily dance + dare darter data date day daylily + deal dear dearly debate debit decade + decimal deep deft deftly degree delay + deluxe deposit depth design desk detail + device dew diamond diet dig dill + dinner dip direct dirt dish disk + display diver divide divine doctor dodger + donut door dot double dough draft + drag dragon drama draw drawer drawing + dream drill drink drive driver drop + drum dry dryer drywall duck due + dump dusk dust duty dye eagle + ear earring earth ease east easy + eat economy edge editor eel effect + effort egg eight elbow elegant element + elf elk email emerald employ end + endive endless energy engine enjoy enter + entry equal equip error escape essay + eternal evening event exam example excuse + exit expert extent extreme eye face + fact factor factual fail failure fair + fajita fall family fan fang farm + farmer fat fault feature feed feel + feeling fench fennel festive few fiber + field fig figure file fill film + filter final finance finding finger finish + fire fish fishing fit fitting five + fix flier flight floor floral florine + flour flow flower fly flying focus + fold folding food foot force forest + forever forgive form formal format fortune + forum frame free freedom freely fresh + friend frog front fruit fuchsia fuel + fun funny future gain galaxy gallium + game gamma gap garage garden garlic + gas gate gather gauge gear gem + gene general gentle gently gherkin ghost + gift give glad glass gleeful glossy + glove glue goal goat goby gold + goldeye golf good gouda goulash gourd + grab grace grade gram grand grape + grapes grass gravy gray great green + grits grocery ground group grouper grout + growth guard guave guess guest guide + guitar gumbo guppy habit hacksaw haddock + hafnium hagfish hair half halibut hall + hammer hand handle handy hanger happy + hat havarti hay haybale head health + healthy hearing heart hearty heat heavy + heel height helium hello help helpful + herald herring hide high highly highway + hill hip hipster hire history hit + hoki hold hole holiday holly home + honest honey hook hope hopeful horizon + horn horse host hotel hour house + housing human humane humor hunt hurry + ice icecube icefish icy idea ideal + image impact impress inch income indigo + initial inkpen insect inside intense invite + iodine iridium iron island issue item + ivory jacket jargon javelin jello jelly + jewel job jocund join joint joke + jovial joy joyful joyous judge juice + jump junior jury just justice kale + keel keep kelp ketchup key keyhole + keyway khaki kick kid kidney kiloohm + kind kindly king kitchen kite kiwi + knee knife krill krypton kumquat lab + lace lack ladder lake lamp lamprey + land laser laugh law lawn lawyer + layer lead leader leading leaf leafy + league leather leave lecture leek leg + lemon length lentil lesson let letter + lettuce level library life lift light + lily lime limit line linen link + lip list listen lithium lively living + lizard load loan lobster local lock + log long longfin look lotus love + lovely loving low lucid luck luffa + lunch lung machine magenta magnet mail + main major make mall manager mango + manner many map march market maroon + martian master match math matter maximum + maybe meal meaning meat media medium + meet meeting melody melon member memory + mention menu mercury merry mess message + messy metal meter method micron middle + might mile milk mind mine minimum + minnow minor mint minute mirror miss + mission misty mix mixer mixture mobile + mode model moment monitor monk month + moon moray morning most motor mouse + mouth move mover movie much mud + mudfish muffin mullet munster muon muscle + music mustard nail name nation native + natural nature navy neat neatly nebula + neck needle neon nerve net network + neutron news nibble nice nickel night + niobium nobody noise noodle normal north + nose note nothing notice nova novel + number nurse nursery oar object offer + office officer oil okay okra old + olive one onion open opening opinion + option orange orbit orchid order oregano + other ounce outcome outside oven owner + oxygen oyster pace pack package page + pager paint pair pale pan pancake + papaya paper pardon parent park parking + parsley parsnip part partner party pass + passage past pasta path patient pattern + pause pay pea peace peach peacock + peahen peak peanut pear pearl pen + penalty pencil pension people pepper perch + perfect period permit person phase phone + photo phrase physics piano pick picture + pie piece pigeon pike pilot pin + pink pinkie pious pipe pitch pizza + place plan plane planet plant planter + plastic plate play player playful plenty + pliers plum pod poem poet poetry + point police policy pollock pony pool + pop popover poptart pork port portal + post pot potato pound powder power + present press price pride primary print + prior private prize problem process produce + product profile profit program project promise + prompt proof proper protein proton public + puff puffer pull pumpkin pup pupfish + pure purple purpose push put quality + quark quarter quiet quill quit quote + rabbit raccoon race radiant radio radish + radium radon rain rainbow raise ramp + ranch range rasp rate ratio ray + razor reach read reading real reality + reason recipe record recover red redeem + reed reef refuse region regret regular + relaxed release relief relish remote remove + rent repair repeat reply report request + reserve resist resolve resort rest result + return reveal review reward ribbon rice + rich ride ridge right ring rise + risk river rivet road roast rock + rocket role roll roof room rope + rose rough roughy round row royal + rub ruby rudder ruin rule run + runner rush rust sacred saddle safe + safety sail salad salami sale salmon + salt sample sand sander sandy sauce + save saving saw scale scampi scene + scheme school score screen script sea + search season seat second secret sector + seemly self sell senate senior sense + series serve set shake shape share + shark shell shift shine shiny ship + shock shoe shoot shop shovel show + side sign signal silk silly silver + simple sing singer single sink site + size skill skin sky slate sleep + sleepy slice slide slip smart smell + smelt smile smoke smooth snap snipe + snow snowy sock socket sodium soft + softly soil sole solid song sorrel + sort soul sound soup source south + space spare speech speed spell spend + sphere spice spider spirit spite split + spoon sport spot spray spread spring + squab square squash stable staff stage + stand staple star start state status + stay steak steel step stern stew + stick still stock stone stop store + storm story strain street stress strike + string stroke strong studio study stuff + style sugar suit sulfur summer sun + sunny sunset super superb surf survey + sweet swim swing switch symbol system + table tackle tail tale talk tan + tank tap tape target task taste + tau tea teach teal team tear + tell ten tender tennis tent term + test tetra text thanks theme theory + thing think thread throat thumb ticket + tidy tie tiger till time timely + tin tip title toast today toe + tomato tone tongue tool tooth top + topic total touch tough tour towel + tower town track trade train trash + travel tray treat tree trick trip + trout trowel truck trupet trust truth + try tube tuna tune turf turkey + turn turnip tutor tux tweet twist + two type union unique unit upbeat + upper use useful user usual valley + value van vase vast veil vein + velvet verse very vessel vest video + view violet visit visual vivid voice + volume vowel voyage waffle wait wake + walk wall warm warmth wasabi wash + watch water wave wax way wealth + wear web wedge week weekly weight + west whale what wheat wheel when + where while who whole why will + win wind window wing winner winter + wire wish witty wolf wonder wood + wool woolly word work worker world + worry worth worthy wrap wrench wrist + writer xenon yak yam yard yarrow + year yearly yellow yew yogurt young youth zebra zephyr zinc zone zoo } set nwordlist [llength $wordlist] From 272ae627c535b1ee11b82ba045c641945e34bdc5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Apr 2023 14:48:11 +0000 Subject: [PATCH 23/37] Add the json_error(X) function that returns the 1-based character offset to the first syntax error in JSON5 string X, or 0 if there are no errors. FossilOrigin-Name: 901ad995d5a722ca2672516205ff488e9acd703a828ca5fc43f11fca5f2af120 --- manifest | 12 +++--- manifest.uuid | 2 +- src/json.c | 115 +++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 111 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index e9188432cd..77de439258 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sindentation\sand\somit\strailing\swhitespace\sin\sthe\srandom\sJSON\sgenerator\nscript. -D 2023-04-28T13:25:35.641 +C Add\sthe\sjson_error(X)\sfunction\sthat\sreturns\sthe\s1-based\scharacter\soffset\sto\nthe\sfirst\ssyntax\serror\sin\sJSON5\sstring\sX,\sor\s0\sif\sthere\sare\sno\serrors. +D 2023-04-28T14:48:11.699 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -593,7 +593,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 7d03a1185d7aee74f53a3f2381355ff4b71d955f287d29d30c0b0979afc96b67 +F src/json.c b532d42d310e570d7f620a692a26f21dc7306063b866088c4e43c647a17118b3 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2066,8 +2066,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f56528d413d8e622f7c4f18b2f9f2e620bfb441c020461299b35a90072ee6c13 -R 5da141bb06598ed103d496559df7ae36 +P 629db09fceb7bf37561b52ccee06ebf4df261291e9a8ffcca82b243f6db5ff07 +R cda10fe16274848e02e4d5ea3713e4b9 U drh -Z 790f8e2b554134402e5889a19faba52a +Z 80f17384e6d82e3c8657040553f2124e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d1f6aa531d..0b6e554ed2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -629db09fceb7bf37561b52ccee06ebf4df261291e9a8ffcca82b243f6db5ff07 \ No newline at end of file +901ad995d5a722ca2672516205ff488e9acd703a828ca5fc43f11fca5f2af120 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1152d9f916..d1a057b48e 100644 --- a/src/json.c +++ b/src/json.c @@ -1073,7 +1073,10 @@ json_parse_restart: iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; for(j=i+1;;j++){ - if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ + pParse->iErr = j; + return -1; + } x = jsonParseValue(pParse, j); if( x<=0 ){ if( x==(-2) ){ @@ -1090,6 +1093,7 @@ json_parse_restart: pParse->has5 = 1; x = k; }else{ + pParse->iErr = j; return -1; } } @@ -1109,13 +1113,19 @@ json_parse_restart: } } x = jsonParseValue(pParse, j); - if( x!=(-5) ) return -1; + if( x!=(-5) ){ + pParse->iErr = j; + return -1; + } j = pParse->iErr+1; } parse_object_value: x = jsonParseValue(pParse, j); pParse->iDepth--; - if( x<=0 ) return -1; + if( x<=0 ){ + pParse->iErr = j; + return -1; + } j = x; if( z[j]==',' ){ continue; @@ -1140,6 +1150,7 @@ json_parse_restart: break; } } + pParse->iErr = j; return -1; } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; @@ -1151,7 +1162,10 @@ json_parse_restart: if( iThis<0 ) return -1; memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ + pParse->iErr = j; + return -1; + } x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<=0 ){ @@ -1160,6 +1174,7 @@ json_parse_restart: if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; break; } + pParse->iErr = j; return -1; } j = x; @@ -1186,6 +1201,7 @@ json_parse_restart: break; } } + pParse->iErr = j; return -1; } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; @@ -1207,6 +1223,7 @@ json_parse_restart: c = z[j]; if( (c & ~0x1f)==0 ){ /* Control characters are not allowed in strings */ + pParse->iErr = j; return -1; } if( c=='\\' ){ @@ -1227,6 +1244,7 @@ json_parse_restart: jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); pParse->has5 = 1; }else{ + pParse->iErr = j; return -1; } }else if( c==cDelim ){ @@ -1242,6 +1260,7 @@ json_parse_restart: jsonParseAddNode(pParse, JSON_NULL, 0, 0); return i+4; } + pParse->iErr = i; return -1; } case 't': { @@ -1249,6 +1268,7 @@ json_parse_restart: jsonParseAddNode(pParse, JSON_TRUE, 0, 0); return i+4; } + pParse->iErr = i; return -1; } case 'f': { @@ -1256,6 +1276,7 @@ json_parse_restart: jsonParseAddNode(pParse, JSON_FALSE, 0, 0); return i+5; } + pParse->iErr = i; return -1; } case '+': { @@ -1271,6 +1292,7 @@ json_parse_restart: seenDP = JSON_REAL; goto parse_number_2; } + pParse->iErr = i; return -1; case '-': case '0': @@ -1338,6 +1360,7 @@ json_parse_restart: jnFlags |= JNODE_JSON5; goto parse_number_2; } + pParse->iErr = i; return -1; } if( z[i+1]=='0' ){ @@ -1358,7 +1381,10 @@ json_parse_restart: c = z[j]; if( sqlite3Isdigit(c) ) continue; if( c=='.' ){ - if( seenDP==JSON_REAL ) return -1; + if( seenDP==JSON_REAL ){ + pParse->iErr = j; + return -1; + } seenDP = JSON_REAL; continue; } @@ -1368,10 +1394,14 @@ json_parse_restart: pParse->has5 = 1; jnFlags |= JNODE_JSON5; }else{ + pParse->iErr = j; return -1; } } - if( seenE ) return -1; + if( seenE ){ + pParse->iErr = j; + return -1; + } seenDP = JSON_REAL; seenE = 1; c = z[j+1]; @@ -1379,7 +1409,10 @@ json_parse_restart: j++; c = z[j+1]; } - if( c<'0' || c>'9' ) return -1; + if( c<'0' || c>'9' ){ + pParse->iErr = j; + return -1; + } continue; } break; @@ -1389,6 +1422,7 @@ json_parse_restart: pParse->has5 = 1; jnFlags |= JNODE_JSON5; }else{ + pParse->iErr = j; return -1; } } @@ -1402,6 +1436,7 @@ json_parse_restart: pParse->has5 = 1; return i+3; } + pParse->iErr = i; return -1; } case 'I': { @@ -1410,6 +1445,7 @@ json_parse_restart: pParse->has5 = 1; return i+8; } + pParse->iErr = i; return -1; } case '}': { @@ -1453,6 +1489,7 @@ json_parse_restart: pParse->has5 = 1; goto json_parse_restart; } + pParse->iErr = i; return -1; } default: { @@ -1471,6 +1508,7 @@ json_parse_restart: return i + nn; } #endif + pParse->iErr = i; return -1; /* Syntax error */ } } /* End switch(z[i]) */ @@ -1575,6 +1613,15 @@ static int jsonParseFindParents(JsonParse *pParse){ ** is no longer valid, parse the JSON again and return the new parse, ** and also register the new parse so that it will be available for ** future sqlite3_get_auxdata() calls. +** +** If an error occurs and pErrCtx!=0 then report the error on pErrCtx +** and return NULL. +** +** If an error occurs and pErrCtx==0 then return the Parse object with +** JsonParse.nErr non-zero. If the caller invokes this routine with +** pErrCtx==0 and it gets back a JsonParse with nErr!=0, then the caller +** is responsible for invoking jsonParseFree() on the returned value. +** But the caller may invoke jsonParseFree() *only* if pParse->nErr!=0. */ static JsonParse *jsonParseCached( sqlite3_context *pCtx, @@ -1624,6 +1671,10 @@ static JsonParse *jsonParseCached( p->zJson = (char*)&p[1]; memcpy((char*)p->zJson, zJson, nJson+1); if( jsonParse(p, pErrCtx, p->zJson) ){ + if( pErrCtx==0 ){ + p->nErr = 1; + return p; + } sqlite3_free(p); return 0; } @@ -2501,8 +2552,8 @@ static void jsonTypeFunc( /* ** json_valid(JSON) ** -** Return 1 if JSON is a well-formed JSON string according to RFC-7159. -** Return 0 otherwise. +** Return 1 if JSON is a well-formed canonical JSON string according +** to RFC-7159. Return 0 otherwise. */ static void jsonValidFunc( sqlite3_context *ctx, @@ -2512,8 +2563,17 @@ static void jsonValidFunc( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); - sqlite3_result_int(ctx, p!=0 && p->has5==0); + sqlite3_result_int(ctx, p!=0 && p->nErr==0 && p->has5==0); + if( p!=0 && p->nErr ) jsonParseFree(p); } + + +/* +** json_valid5(JSON) +** +** Return 1 if JSON is a well-formed JSON5 string. +** Return 0 otherwise. +*/ static void jsonValid5Func( sqlite3_context *ctx, int argc, @@ -2522,7 +2582,39 @@ static void jsonValid5Func( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); - sqlite3_result_int(ctx, p!=0); + sqlite3_result_int(ctx, p!=0 && p->nErr==0); + if( p!=0 && p->nErr ) jsonParseFree(p); +} + + + +/* +** json_error(JSON) +** +** If JSON is not a well-formed JSON5 string, then return the 1-based +** character offset to the location of the first error in that string. +** Return 0 otherwise. +*/ +static void jsonErrorFunc( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + JsonParse *p; /* The parse */ + UNUSED_PARAMETER(argc); + p = jsonParseCached(ctx, argv, 0); + if( p==0 || p->oom || p->nErr==0 ){ + sqlite3_result_int(ctx, 0); + }else{ + int n = 0; + int i; + const char *z = p->zJson; + for(i=0; iiErr && z[i]; i++){ + if( (z[i]&0xc0)!=80 ) n++; + } + sqlite3_result_int(ctx, n); + jsonParseFree(p); + } } @@ -3235,6 +3327,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_array, -1, 0, jsonArrayFunc), JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), + JFUNCTION(json_error, 1, 0, jsonErrorFunc), JFUNCTION(json_extract, -1, 0, jsonExtractFunc), JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), From 8e7efe37645453fac5ca98f8a9837f34d14f75b6 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Apr 2023 17:38:35 +0000 Subject: [PATCH 24/37] Improvements to the accuracy of json_error(). Add the extension SQL functions random_json(SEED) and random_json5(SEED). FossilOrigin-Name: 8d09dc1c45a8026b94f70273d064e47939f30cadedc17548b5a26ba054a8d3a7 --- ext/misc/randomjson.c | 202 ++++++++++++++++++++++++++++++++++++++++++ manifest | 13 +-- manifest.uuid | 2 +- src/json.c | 8 +- 4 files changed, 214 insertions(+), 11 deletions(-) create mode 100644 ext/misc/randomjson.c diff --git a/ext/misc/randomjson.c b/ext/misc/randomjson.c new file mode 100644 index 0000000000..3a6f545fe6 --- /dev/null +++ b/ext/misc/randomjson.c @@ -0,0 +1,202 @@ +/* +** 2023-04-28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This SQLite extension implements a the random_json(SEED) and +** random_json5(SEED) functions. Given a numeric SEED value, these +** routines generate pseudo-random JSON or JSON5, respectively. The +** same value is always generated for the same seed. +** +** These SQL functions are intended for testing. They do not have any +** practical real-world use, that we know of. +** +** COMPILE: +** +** gcc --shared -fPIC -o randomjson.so -I. ext/misc/randomjson.c +** +** USING FROM THE CLI: +** +** .load ./randomjson +** SELECT random_json(1); +*/ +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#include +#include +#include + +/* Pseudo-random number generator */ +typedef struct Prng { + unsigned int x, y; +} Prng; + +/* Reseed the PRNG */ +static void prngSeed(Prng *p, unsigned int iSeed){ + p->x = iSeed | 1; + p->y = iSeed; +} + +/* Extract a random number */ +static unsigned int prngInt(Prng *p){ + p->x = (p->x>>1) ^ ((1+~(p->x&1)) & 0xd0000001); + p->y = p->y*1103515245 + 12345; + return p->x ^ p->y; +} + +static const char *azJsonAtoms[] = { + /* JSON /* JSON-5 */ + "0", "0", + "1", "1", + "-1", "-1", + "2", "+2", + "3", "3", + "2.5", "2.5", + "0.75", ".75", + "-4.0e2", "-4.e2", + "5.0e-3", "+5e-3", + "0", "0x0", + "512", "0x200", + "256", "+0x100", + "-2748", "-0xabc", + "true", "true", + "false", "false", + "null", "null", + "9.0e999", "Infinity", + "-9.0e999", "-Infinity", + "9.0e999", "+Infinity", + "null", "NaN", + "-0.0005123", "-0.0005123", + "4.35e-3", "+4.35e-3", + "\"gem\\\"hay\"", "\"gem\\\"hay\"", + "\"icy'joy\"", "'icy\\'joy\'", + "\"keylog\"", "\"key\\\nlog\"", + "\"mix\\\\\\tnet\"", "\"mix\\\\\\tnet\"", + "{}", "{}", + "[]", "[]", + "[]", "[/*empty*/]", + "{}", "{//empty\n}", + "\"ask\"", "\"ask\"", + "\"bag\"", "\"bag\"", + "\"can\"", "\"can\"", + "\"day\"", "\"day\"", + "\"end\"", "'end'", + "\"fly\"", "\"fly\"", + "\"\"", "\"\"", +}; +static const char *azJsonTemplate[] = { + /* JSON JSON-5 */ + "{\"a\":%,\"b\":%,\"c\":%}", "{a:%,b:%,c:%}", + "{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"e\":%}", "{a:%,b:%,c:%,d:%,e:%}", + "{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"\":%}", "{a:%,b:%,c:%,d:%,\"\":%}", + "{\"d\":%}", "{d:%}", + "{\"eeee\":%, \"ffff\":%}", "{eeee:% /*and*/, ffff:%}", + "{\"$g\":%,\"_h_\":%}", "{$g:%,_h_:%,}", + "{\"x\":%,\n \"y\":%}", "{\"x\":%,\n \"y\":%}", + "{\"a b c d\":%,\"e\":%,\"f\":%,\"x\":%,\"y\":%}", + "{\"a b c d\":%,e:%,f:%,x:%,y:%}", + "{\"Z\":%}", "{Z:%,}", + "[%]", "[%,]", + "[%,%]", "[%,%]", + "[%,%,%]", "[%,%,%,]", + "[%,%,%,%]", "[%,%,%,%]", + "[%,%,%,%,%]", "[%,%,%,%,%]", +}; + +#define count(X) (sizeof(X)/sizeof(X[0])) + +#define STRSZ 10000 + +static void jsonExpand( + const char *zSrc, + char *zDest, + Prng *p, + int eType, /* 0 for JSON, 1 for JSON5 */ + unsigned int r /* Growth probability 0..1000. 0 means no growth */ +){ + unsigned int i, j, k; + const char *z; + size_t n; + + j = 0; + if( zSrc==0 ){ + k = prngInt(p)%(count(azJsonTemplate)/2); + k = k*2 + eType; + zSrc = azJsonTemplate[k]; + } + if( strlen(zSrc)>=STRSZ/10 ) r = 0; + for(i=0; zSrc[i]; i++){ + if( zSrc[i]!='%' ){ + if( jhas5 = 1; x = k; }else{ - pParse->iErr = j; + if( x!=-1 ) pParse->iErr = j; return -1; } } @@ -1114,7 +1114,7 @@ json_parse_restart: } x = jsonParseValue(pParse, j); if( x!=(-5) ){ - pParse->iErr = j; + if( x!=(-1) ) pParse->iErr = j; return -1; } j = pParse->iErr+1; @@ -1123,7 +1123,7 @@ json_parse_restart: x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<=0 ){ - pParse->iErr = j; + if( x!=(-1) ) pParse->iErr = j; return -1; } j = x; @@ -1174,7 +1174,7 @@ json_parse_restart: if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; break; } - pParse->iErr = j; + if( x!=(-1) ) pParse->iErr = j; return -1; } j = x; From 6e2de57963198789e6df238962351fa4b840851e Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Apr 2023 23:19:11 +0000 Subject: [PATCH 25/37] Bug fix in the offset computation for json_error(). FossilOrigin-Name: 8f7ab5d9210490eb451534df246b82081a5aa252a1bd662659df3da429290837 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index c10ebff82d..0d0cafc8eb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\sthe\saccuracy\sof\sjson_error().\s\sAdd\sthe\sextension\sSQL\nfunctions\srandom_json(SEED)\sand\srandom_json5(SEED). -D 2023-04-28T17:38:35.385 +C Bug\sfix\sin\sthe\soffset\scomputation\sfor\sjson_error(). +D 2023-04-28T23:19:11.271 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -594,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 03eba427c0e8700db2895b785642f4dba8440020658d8eef97ec4fadf3836cdf +F src/json.c cc36f7df741f617624abc73adba6ffea1f3358196e1b78b43124265e941d332a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 901ad995d5a722ca2672516205ff488e9acd703a828ca5fc43f11fca5f2af120 -R c710d125f016d21291562e41606e0d2f +P 8d09dc1c45a8026b94f70273d064e47939f30cadedc17548b5a26ba054a8d3a7 +R 85ded77d7e850b8c4e5d880f9f34a021 U drh -Z 98bfef108d9db21095967c25b17439f3 +Z 6b839a1d682f3ab7c8bf4f474d197593 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2d0bc4ddaf..7bde04d486 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8d09dc1c45a8026b94f70273d064e47939f30cadedc17548b5a26ba054a8d3a7 \ No newline at end of file +8f7ab5d9210490eb451534df246b82081a5aa252a1bd662659df3da429290837 \ No newline at end of file diff --git a/src/json.c b/src/json.c index f76d7dc5a7..83b02bf5a4 100644 --- a/src/json.c +++ b/src/json.c @@ -2606,11 +2606,11 @@ static void jsonErrorFunc( if( p==0 || p->oom || p->nErr==0 ){ sqlite3_result_int(ctx, 0); }else{ - int n = 0; + int n = 1; int i; const char *z = p->zJson; for(i=0; iiErr && z[i]; i++){ - if( (z[i]&0xc0)!=80 ) n++; + if( (z[i]&0xc0)!=0x80 ) n++; } sqlite3_result_int(ctx, n); jsonParseFree(p); From 8f3fe2ead2103c766380081520b1579481f661d4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Apr 2023 23:38:54 +0000 Subject: [PATCH 26/37] Bug fixes in the logic to skip over JSON5 comments. FossilOrigin-Name: c736b77a2fc5f8f2ba8f770adf05443dfdc3a9847ee519f411e661f76756d611 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 16 +++++++++++++--- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 0d0cafc8eb..e5775f3c07 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bug\sfix\sin\sthe\soffset\scomputation\sfor\sjson_error(). -D 2023-04-28T23:19:11.271 +C Bug\sfixes\sin\sthe\slogic\sto\sskip\sover\sJSON5\scomments. +D 2023-04-28T23:38:54.056 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -594,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c cc36f7df741f617624abc73adba6ffea1f3358196e1b78b43124265e941d332a +F src/json.c 92a84ce1c933ef09296a0a9fdaea95c07d5548ef2f1e540197cfbdef9b38a60a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8d09dc1c45a8026b94f70273d064e47939f30cadedc17548b5a26ba054a8d3a7 -R 85ded77d7e850b8c4e5d880f9f34a021 +P 8f7ab5d9210490eb451534df246b82081a5aa252a1bd662659df3da429290837 +R cb0f54422021b51368018226423f13cc U drh -Z 6b839a1d682f3ab7c8bf4f474d197593 +Z 7078eb5b44d703d85936a37156ec4b2c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7bde04d486..88b9340307 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8f7ab5d9210490eb451534df246b82081a5aa252a1bd662659df3da429290837 \ No newline at end of file +c736b77a2fc5f8f2ba8f770adf05443dfdc3a9847ee519f411e661f76756d611 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 83b02bf5a4..f2856a841e 100644 --- a/src/json.c +++ b/src/json.c @@ -963,12 +963,22 @@ static int json5Whitespace(const char *zIn){ for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){ if( z[j]==0 ) goto whitespace_done; } - n += j+1; + n = j+1; break; }else if( z[n+1]=='/' ){ int j; - for(j=n+2; z[j] && z[j]!='\n'; j++){} - n += j; + char c; + for(j=n+2; (c = z[j])!=0; j++){ + if( c=='\n' || c=='\r' ) break; + if( 0xe2==(u8)c && 0x80==(u8)z[j+1] + && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]) + ){ + j += 2; + break; + } + } + n = j; + if( z[n] ) n++; break; } goto whitespace_done; From f176776bd73a54e96aa83d9f2fefa4a919cea876 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 29 Apr 2023 00:59:22 +0000 Subject: [PATCH 27/37] Correctly recognize an isolated U+feff as a space character in JSON5. FossilOrigin-Name: 4473dc8e3ad18bb6185ed4a819baf881a7d26ac74bc70fae6ba23a0030be8316 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index e5775f3c07..5893cfd206 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bug\sfixes\sin\sthe\slogic\sto\sskip\sover\sJSON5\scomments. -D 2023-04-28T23:38:54.056 +C Correctly\srecognize\san\sisolated\sU+feff\sas\sa\sspace\scharacter\sin\sJSON5. +D 2023-04-29T00:59:22.682 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -594,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 92a84ce1c933ef09296a0a9fdaea95c07d5548ef2f1e540197cfbdef9b38a60a +F src/json.c 3535ddb77b6bea2374a1ca5dad44cf616969bf202f8143c76bc9d1e38d1b587a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8f7ab5d9210490eb451534df246b82081a5aa252a1bd662659df3da429290837 -R cb0f54422021b51368018226423f13cc +P c736b77a2fc5f8f2ba8f770adf05443dfdc3a9847ee519f411e661f76756d611 +R dbfd99e9ae655ae239188884ec879742 U drh -Z 7078eb5b44d703d85936a37156ec4b2c +Z 9ebd8716513a39a289a7550b0949e896 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 88b9340307..dc0a2012c6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c736b77a2fc5f8f2ba8f770adf05443dfdc3a9847ee519f411e661f76756d611 \ No newline at end of file +4473dc8e3ad18bb6185ed4a819baf881a7d26ac74bc70fae6ba23a0030be8316 \ No newline at end of file diff --git a/src/json.c b/src/json.c index f2856a841e..deeb107671 100644 --- a/src/json.c +++ b/src/json.c @@ -1492,7 +1492,8 @@ json_parse_restart: case 0xc2: case 0xe1: case 0xe2: - case 0xe3: { + case 0xe3: + case 0xef: { j = json5Whitespace(&z[i]); if( j>0 ){ i += j; From 64953f0fb8b5c0fdd2ffdb0ed144921c98303909 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 29 Apr 2023 12:13:27 +0000 Subject: [PATCH 28/37] Minor fixes to to the JSON% parser. FossilOrigin-Name: 2fe684cdcdc3cab4ec3348ca5aa5948e4472c562b739c29faebcb77397f8d969 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 21 +++++++++------------ 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index 5893cfd206..db8aa3f3d8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correctly\srecognize\san\sisolated\sU+feff\sas\sa\sspace\scharacter\sin\sJSON5. -D 2023-04-29T00:59:22.682 +C Minor\sfixes\sto\sto\sthe\sJSON%\sparser. +D 2023-04-29T12:13:27.600 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -594,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 3535ddb77b6bea2374a1ca5dad44cf616969bf202f8143c76bc9d1e38d1b587a +F src/json.c a25afd49aba4a2ac6b5c0fdabd82f376beccdec18f1946e66143bdb51fb1be33 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c736b77a2fc5f8f2ba8f770adf05443dfdc3a9847ee519f411e661f76756d611 -R dbfd99e9ae655ae239188884ec879742 +P 4473dc8e3ad18bb6185ed4a819baf881a7d26ac74bc70fae6ba23a0030be8316 +R c6f6cd1aa30c85d050651bc2038ace97 U drh -Z 9ebd8716513a39a289a7550b0949e896 +Z 73c28292d33e23cb810b7dd1773fc296 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index dc0a2012c6..fc7993d006 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4473dc8e3ad18bb6185ed4a819baf881a7d26ac74bc70fae6ba23a0030be8316 \ No newline at end of file +2fe684cdcdc3cab4ec3348ca5aa5948e4472c562b739c29faebcb77397f8d969 \ No newline at end of file diff --git a/src/json.c b/src/json.c index deeb107671..32a52cf1ee 100644 --- a/src/json.c +++ b/src/json.c @@ -377,10 +377,9 @@ static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ int rc = sqlite3DecOrHexToI64(zIn, &i); if( rc<=1 ){ jsonPrintf(100,p,"%lld",i); - }else if( rc==2 ){ - jsonAppendRaw(p, "9.0e999", 7); }else{ - jsonAppendRaw(p, "9223372036854775808", 18); + assert( rc==2 ); + jsonAppendRaw(p, "9.0e999", 7); } return; } @@ -1109,7 +1108,10 @@ json_parse_restart: } if( pParse->oom ) return -1; pNode = &pParse->aNode[pParse->nNode-1]; - if( pNode->eType!=JSON_STRING ) return -1; + if( pNode->eType!=JSON_STRING ){ + pParse->iErr = j; + return -1; + } pNode->jnFlags |= JNODE_LABEL; j = x; if( z[j]==':' ){ @@ -1249,7 +1251,6 @@ json_parse_restart: jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); pParse->has5 = 1; }else if( c=='\r' ){ - j++; if( z[j+1]=='\n' ) j++; jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); pParse->has5 = 1; @@ -1339,10 +1340,7 @@ json_parse_restart: } }else{ if( !sqlite3Isdigit(z[i+1]) ){ - if( z[i+1]=='I' - && (c=='-' || c=='+') - && strncmp(&z[i+1], "Infinity",8)==0 - ){ + if( z[i+1]=='I' && strncmp(&z[i+1], "Infinity",8)==0 ){ if( z[i]=='-' ){ jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); }else{ @@ -1354,7 +1352,6 @@ json_parse_restart: /* Non-standard JSON and JSON5: Allow "Inf" as an alternative ** spelling for "Infinity" and allow it to be in any case. */ if( (z[i+1]=='I' || z[i+1]=='i') - && (c=='-' || c=='+') && sqlite3StrNICmp(&z[i+1], "inf",3)==0 ){ if( z[i]=='-' ){ @@ -2971,8 +2968,8 @@ static void jsonAppendObjectPathElement( nn = pNode->n; if( (pNode->jnFlags & JNODE_RAW)==0 ){ assert( nn>=2 ); - assert( z[0]=='"' ); - assert( z[nn-1]=='"' ); + assert( z[0]=='"' || z[0]=='\'' ); + assert( z[nn-1]=='"' || z[0]=='\'' ); if( nn>2 && sqlite3Isalpha(z[1]) ){ for(jj=2; jj Date: Sat, 29 Apr 2023 16:00:20 +0000 Subject: [PATCH 29/37] Do not allow leading zeros on non-zero numeric literals in JSON. FossilOrigin-Name: 3e91494390ba88498eb243f61ce4ef4efa23b58326108a769bc72331d7d7d75b --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 14 +++++++------- test/json102.test | 12 ++++++------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/manifest b/manifest index db8aa3f3d8..bd3882b93b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sfixes\sto\sto\sthe\sJSON%\sparser. -D 2023-04-29T12:13:27.600 +C Do\snot\sallow\sleading\szeros\son\snon-zero\snumeric\sliterals\sin\sJSON. +D 2023-04-29T16:00:20.514 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -594,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c a25afd49aba4a2ac6b5c0fdabd82f376beccdec18f1946e66143bdb51fb1be33 +F src/json.c 1de9706f7bc22865237fa46a2f3e0de320e0a6afa24ff7b854c7522c0cc57c48 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -1256,7 +1256,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1.txt 335a7c8ab291d354f33b7decc9559e99a2823d4142291c4be7aa339a631f3c2d F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3b1f1511d21af6737 x F test/json101.test de9c93169b84ac96fd5836c638a2ae1f00e4afbd4003c6b596692d7f05e1cd69 -F test/json102.test 1f61f469d763ff26430dbee76bc75e0aa73084ca84f10e58744fdb899e56de3e +F test/json102.test 3bdf097757faefadfd88c5695ba8ab076ca8d8680eb8f33d73d44c07cbbc666d F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4473dc8e3ad18bb6185ed4a819baf881a7d26ac74bc70fae6ba23a0030be8316 -R c6f6cd1aa30c85d050651bc2038ace97 +P 2fe684cdcdc3cab4ec3348ca5aa5948e4472c562b739c29faebcb77397f8d969 +R ab5a5b4f8c2b54ec0b250293ed522d33 U drh -Z 73c28292d33e23cb810b7dd1773fc296 +Z 69b4a1c1b9499f71fc838cb1772a2121 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fc7993d006..b758f33f29 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2fe684cdcdc3cab4ec3348ca5aa5948e4472c562b739c29faebcb77397f8d969 \ No newline at end of file +3e91494390ba88498eb243f61ce4ef4efa23b58326108a769bc72331d7d7d75b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 32a52cf1ee..f603a82bf7 100644 --- a/src/json.c +++ b/src/json.c @@ -333,7 +333,7 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ jsonAppendRaw(p, "\\u0000", 6); break; case '\r': - if( N>=3 && zIn[2]=='\n' ){ + if( zIn[2]=='\n' ){ zIn++; N--; } @@ -1328,15 +1328,15 @@ json_parse_restart: if( c<='0' ){ if( c=='0' ){ - if( sqlite3Isdigit(z[i+1]) ){ - pParse->has5 = 1; - jnFlags = JNODE_JSON5; - }else if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ + if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ assert( seenDP==JSON_INT ); pParse->has5 = 1; jnFlags |= JNODE_JSON5; for(j=i+3; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; + }else if( sqlite3Isdigit(z[i+1]) ){ + pParse->iErr = i+1; + return -1; } }else{ if( !sqlite3Isdigit(z[i+1]) ){ @@ -1372,8 +1372,8 @@ json_parse_restart: } if( z[i+1]=='0' ){ if( sqlite3Isdigit(z[i+2]) ){ - pParse->has5 = 1; - jnFlags = JNODE_JSON5; + pParse->iErr = i+1; + return -1; }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ pParse->has5 = 1; jnFlags |= JNODE_JSON5; diff --git a/test/json102.test b/test/json102.test index d45ec11bf1..2594feafa1 100644 --- a/test/json102.test +++ b/test/json102.test @@ -302,18 +302,18 @@ for {set i 0} {$i<100} {incr i} { # fixed. # foreach {id j x0 x5} { - 1401 {'{"x":01}'} 0 1 - 1402 {'{"x":-01}'} 0 1 + 1401 {'{"x":01}'} 0 0 + 1402 {'{"x":-01}'} 0 0 1403 {'{"x":0}'} 1 1 1404 {'{"x":-0}'} 1 1 1405 {'{"x":0.1}'} 1 1 1406 {'{"x":-0.1}'} 1 1 1407 {'{"x":0.0000}'} 1 1 1408 {'{"x":-0.0000}'} 1 1 - 1409 {'{"x":01.5}'} 0 1 - 1410 {'{"x":-01.5}'} 0 1 - 1411 {'{"x":00}'} 0 1 - 1412 {'{"x":-00}'} 0 1 + 1409 {'{"x":01.5}'} 0 0 + 1410 {'{"x":-01.5}'} 0 0 + 1411 {'{"x":00}'} 0 0 + 1412 {'{"x":-00}'} 0 0 1413 {'{"x":+0}'} 0 1 1414 {'{"x":+5}'} 0 1 1415 {'{"x":+5.5}'} 0 1 From 399875f6c568bf7e674948c09686c0c7cabb21d9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 29 Apr 2023 16:31:08 +0000 Subject: [PATCH 30/37] Simplification of the logic that normalizes JSON5 integer literals into canonical JSON integer literals. Improved reporting of OOM. FossilOrigin-Name: 01ee613c07fcb87e7d7b7f1b1387982715d1343418f37f4a1dc90e43a76d20e8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 47 ++++++++++++++++++++++++++++------------------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index bd3882b93b..3a9b1fb48b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sallow\sleading\szeros\son\snon-zero\snumeric\sliterals\sin\sJSON. -D 2023-04-29T16:00:20.514 +C Simplification\sof\sthe\slogic\sthat\snormalizes\sJSON5\sinteger\sliterals\sinto\ncanonical\sJSON\sinteger\sliterals.\s\sImproved\sreporting\sof\sOOM. +D 2023-04-29T16:31:08.205 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -594,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 1de9706f7bc22865237fa46a2f3e0de320e0a6afa24ff7b854c7522c0cc57c48 +F src/json.c 6029d4ce8705ebad5817386a326b620772dcc165e1b23a8e4645061621679538 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 2fe684cdcdc3cab4ec3348ca5aa5948e4472c562b739c29faebcb77397f8d969 -R ab5a5b4f8c2b54ec0b250293ed522d33 +P 3e91494390ba88498eb243f61ce4ef4efa23b58326108a769bc72331d7d7d75b +R 9934ae2414640783ca534819ca587694 U drh -Z 69b4a1c1b9499f71fc838cb1772a2121 +Z fec85eb0c6f9c0d91d036f5cadb9198f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b758f33f29..beb328db1e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3e91494390ba88498eb243f61ce4ef4efa23b58326108a769bc72331d7d7d75b \ No newline at end of file +01ee613c07fcb87e7d7b7f1b1387982715d1343418f37f4a1dc90e43a76d20e8 \ No newline at end of file diff --git a/src/json.c b/src/json.c index f603a82bf7..17908fd976 100644 --- a/src/json.c +++ b/src/json.c @@ -371,21 +371,17 @@ static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ zIn++; N--; } - while( zIn[0]=='0' && N>1 ){ - if( zIn[1]=='x' || zIn[1]=='X' ){ - sqlite3_int64 i = 0; - int rc = sqlite3DecOrHexToI64(zIn, &i); - if( rc<=1 ){ - jsonPrintf(100,p,"%lld",i); - }else{ - assert( rc==2 ); - jsonAppendRaw(p, "9.0e999", 7); - } - return; + if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){ + sqlite3_int64 i = 0; + int rc = sqlite3DecOrHexToI64(zIn, &i); + if( rc<=1 ){ + jsonPrintf(100,p,"%lld",i); + }else{ + assert( rc==2 ); + jsonAppendRaw(p, "9.0e999", 7); } - zIn++; - N--; - } + return; + } jsonAppendRaw(p, zIn, N); } @@ -2571,8 +2567,13 @@ static void jsonValidFunc( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); - sqlite3_result_int(ctx, p!=0 && p->nErr==0 && p->has5==0); - if( p!=0 && p->nErr ) jsonParseFree(p); + if( p==0 || p->oom ){ + sqlite3_result_error_nomem(ctx); + sqlite3_free(p); + }else{ + sqlite3_result_int(ctx, p->nErr==0 && p->has5==0); + if( p->nErr ) jsonParseFree(p); + } } @@ -2590,8 +2591,13 @@ static void jsonValid5Func( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); - sqlite3_result_int(ctx, p!=0 && p->nErr==0); - if( p!=0 && p->nErr ) jsonParseFree(p); + if( p==0 || p->oom ){ + sqlite3_result_error_nomem(ctx); + sqlite3_free(p); + }else{ + sqlite3_result_int(ctx, p->nErr==0); + if( p->nErr ) jsonParseFree(p); + } } @@ -2611,7 +2617,10 @@ static void jsonErrorFunc( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); - if( p==0 || p->oom || p->nErr==0 ){ + if( p==0 || p->oom ){ + sqlite3_result_error_nomem(ctx); + sqlite3_free(p); + }else if( p->nErr==0 ){ sqlite3_result_int(ctx, 0); }else{ int n = 1; From f8ae2d2a2a23e4ad3f937712d30e2daf8d3b0c79 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 29 Apr 2023 17:13:39 +0000 Subject: [PATCH 31/37] Add ALWAYS macros on unreachable branches in the new JSON5 logic. FossilOrigin-Name: 91e15ed9d93e51b750172a074dbbcf206128e9476571ce6189b8ea0ba5ab4135 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 3a9b1fb48b..f13e0d0240 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplification\sof\sthe\slogic\sthat\snormalizes\sJSON5\sinteger\sliterals\sinto\ncanonical\sJSON\sinteger\sliterals.\s\sImproved\sreporting\sof\sOOM. -D 2023-04-29T16:31:08.205 +C Add\sALWAYS\smacros\son\sunreachable\sbranches\sin\sthe\snew\sJSON5\slogic. +D 2023-04-29T17:13:39.696 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -594,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 6029d4ce8705ebad5817386a326b620772dcc165e1b23a8e4645061621679538 +F src/json.c 0de525ac85b275cfbe6a7eb565c9c7849f884331153f60024715a47a18ed781e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3e91494390ba88498eb243f61ce4ef4efa23b58326108a769bc72331d7d7d75b -R 9934ae2414640783ca534819ca587694 +P 01ee613c07fcb87e7d7b7f1b1387982715d1343418f37f4a1dc90e43a76d20e8 +R 8691ebe0c22e2def3817b9ef68bc683b U drh -Z fec85eb0c6f9c0d91d036f5cadb9198f +Z 4bc62005656847ff8857de80fff89c9b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index beb328db1e..8139537284 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -01ee613c07fcb87e7d7b7f1b1387982715d1343418f37f4a1dc90e43a76d20e8 \ No newline at end of file +91e15ed9d93e51b750172a074dbbcf206128e9476571ce6189b8ea0ba5ab4135 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 17908fd976..7876bdcbc4 100644 --- a/src/json.c +++ b/src/json.c @@ -1393,7 +1393,7 @@ json_parse_restart: } if( c=='e' || c=='E' ){ if( z[j-1]<'0' ){ - if( z[j-1]=='.' && j-2>=i && sqlite3Isdigit(z[j-2]) ){ + if( ALWAYS(z[j-1]=='.') && j-2>=i && sqlite3Isdigit(z[j-2]) ){ pParse->has5 = 1; jnFlags |= JNODE_JSON5; }else{ @@ -1421,7 +1421,7 @@ json_parse_restart: break; } if( z[j-1]<'0' ){ - if( z[j-1]=='.' && j-2>=i && sqlite3Isdigit(z[j-2]) ){ + if( ALWAYS(z[j-1]=='.') && j-2>=i && sqlite3Isdigit(z[j-2]) ){ pParse->has5 = 1; jnFlags |= JNODE_JSON5; }else{ @@ -2626,7 +2626,7 @@ static void jsonErrorFunc( int n = 1; int i; const char *z = p->zJson; - for(i=0; iiErr && z[i]; i++){ + for(i=0; iiErr && ALWAYS(z[i]); i++){ if( (z[i]&0xc0)!=0x80 ) n++; } sqlite3_result_int(ctx, n); From 440f7b483306038f4a3ced85bc440c9debe46c54 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 29 Apr 2023 17:35:29 +0000 Subject: [PATCH 32/37] More ALWAYS() macros. FossilOrigin-Name: 770b09f7a795956be63a06847059370db8dfc88654878d742b7b826947029962 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index f13e0d0240..2bc4528324 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sALWAYS\smacros\son\sunreachable\sbranches\sin\sthe\snew\sJSON5\slogic. -D 2023-04-29T17:13:39.696 +C More\sALWAYS()\smacros. +D 2023-04-29T17:35:29.727 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -594,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 0de525ac85b275cfbe6a7eb565c9c7849f884331153f60024715a47a18ed781e +F src/json.c f76772b2d780f9711896e10bb6efa32d839d9c3fef225d3476e6f5b6683a04f3 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 01ee613c07fcb87e7d7b7f1b1387982715d1343418f37f4a1dc90e43a76d20e8 -R 8691ebe0c22e2def3817b9ef68bc683b +P 91e15ed9d93e51b750172a074dbbcf206128e9476571ce6189b8ea0ba5ab4135 +R b78f203ca73d8ea097a8cd3d3031aec7 U drh -Z 4bc62005656847ff8857de80fff89c9b +Z 43d50e401ed22df6c70dd6bacd839b80 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8139537284..6cdf339ed2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -91e15ed9d93e51b750172a074dbbcf206128e9476571ce6189b8ea0ba5ab4135 \ No newline at end of file +770b09f7a795956be63a06847059370db8dfc88654878d742b7b826947029962 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 7876bdcbc4..a78aa8e614 100644 --- a/src/json.c +++ b/src/json.c @@ -1393,7 +1393,7 @@ json_parse_restart: } if( c=='e' || c=='E' ){ if( z[j-1]<'0' ){ - if( ALWAYS(z[j-1]=='.') && j-2>=i && sqlite3Isdigit(z[j-2]) ){ + if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ pParse->has5 = 1; jnFlags |= JNODE_JSON5; }else{ @@ -1421,7 +1421,7 @@ json_parse_restart: break; } if( z[j-1]<'0' ){ - if( ALWAYS(z[j-1]=='.') && j-2>=i && sqlite3Isdigit(z[j-2]) ){ + if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ pParse->has5 = 1; jnFlags |= JNODE_JSON5; }else{ From 7be1473ccbd55a9b27d90a3cdf20e44d39c6e7a1 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 30 Apr 2023 19:34:41 +0000 Subject: [PATCH 33/37] Omit the json_valid() function. Change the name of json_error() to json_error_position(). Use "NOT json_error_position(X)" as a substitute for "json_valid5(X)". FossilOrigin-Name: 34c4e900a9cc51630eeaf01deef74bf5b18d66e0ab1dc61a2023ac8f837a5197 --- manifest | 18 ++++----- manifest.uuid | 2 +- src/json.c | 94 ++++++++++++++++++++++------------------------- test/json101.test | 26 ++++++------- test/json102.test | 2 +- test/json501.test | 10 ++--- 6 files changed, 73 insertions(+), 79 deletions(-) diff --git a/manifest b/manifest index ac7fedd268..c7969eb46e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sthe\sJSON5\sbranch\sto\nfacilitate\stesting\sof\sthe\sJSON5\sbranch. -D 2023-04-29T18:40:48.921 +C Omit\sthe\sjson_valid()\sfunction.\s\sChange\sthe\sname\sof\sjson_error()\sto\njson_error_position().\s\sUse\s"NOT\sjson_error_position(X)"\sas\sa\ssubstitute\nfor\s"json_valid5(X)". +D 2023-04-30T19:34:41.159 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -595,7 +595,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c f76772b2d780f9711896e10bb6efa32d839d9c3fef225d3476e6f5b6683a04f3 +F src/json.c d216510dbb7900072e4a0cbb1273a21e0a20db57da405b4397abb236a2f30392 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -1256,12 +1256,12 @@ F test/json/README.md 506af1f54574b524106acb50d1a341ab5ddfa6d83fe25095007892b07e F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd28656fb261bddc8a3f F test/json/json-q1.txt 335a7c8ab291d354f33b7decc9559e99a2823d4142291c4be7aa339a631f3c2d F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3b1f1511d21af6737 x -F test/json101.test de9c93169b84ac96fd5836c638a2ae1f00e4afbd4003c6b596692d7f05e1cd69 -F test/json102.test 3bdf097757faefadfd88c5695ba8ab076ca8d8680eb8f33d73d44c07cbbc666d +F test/json101.test 211d75638782370c07e10a847bd66e501ea1537f39c7da4447bfa055c0f261db +F test/json102.test 13dc9e7b7f359ecb861e02f9bd7019f7342a63d1c354273b0a8f3904050560a8 F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d -F test/json501.test 43614faefc9ac708e29d55cdc290b5cea336c151030196885708ea3d132fce63 +F test/json501.test 9b14d14f26659287277a1f6fcf2a932a0a2351e1aae522586b530498c977ce45 F test/json502.test 66d150cc098674b8bf4354526a8dd411b926f43ca892306bcb3b6d3f93fef7be F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b @@ -2068,8 +2068,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 770b09f7a795956be63a06847059370db8dfc88654878d742b7b826947029962 2e85b0e3dcae0915aa6472a3654c8ac72a6b2083c11747f3f657c79bbdaf530b -R 81b69133758950851391ffde4b6eeead +P 30d12edebad9b097cd5f0da355304d1cb2f8b70d7c7dff378fd7ad7c8ebf9279 +R 5a03c0d318d1b5343d158677e6158a89 U drh -Z 5f3a6d866b7662144df62f2a5c006f47 +Z 50768d32c83122dec00455a3ce19a1c8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1007d98b66..fa442e6283 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -30d12edebad9b097cd5f0da355304d1cb2f8b70d7c7dff378fd7ad7c8ebf9279 \ No newline at end of file +34c4e900a9cc51630eeaf01deef74bf5b18d66e0ab1dc61a2023ac8f837a5197 \ No newline at end of file diff --git a/src/json.c b/src/json.c index a78aa8e614..8fa08d2443 100644 --- a/src/json.c +++ b/src/json.c @@ -134,7 +134,7 @@ struct JsonParse { u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ u8 oom; /* Set to true if out of memory */ - u8 has5; /* True if input has JSON5 features */ + u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ int nJson; /* Length of the zJson string in bytes */ u32 iErr; /* Error location in zJson[] */ u32 iHold; /* Replace cache line with the lowest iHold value */ @@ -1086,7 +1086,7 @@ json_parse_restart: if( x<=0 ){ if( x==(-2) ){ j = pParse->iErr; - if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; + if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; pParse->iDepth--; break; } @@ -1095,7 +1095,7 @@ json_parse_restart: int k; for(k=j+1; sqlite3Isalnum(z[k]) || z[k]=='_' || z[k]=='$'; k++){} jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]); - pParse->has5 = 1; + pParse->hasNonstd = 1; x = k; }else{ if( x!=-1 ) pParse->iErr = j; @@ -1179,7 +1179,7 @@ json_parse_restart: if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; - if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1; + if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; break; } if( x!=(-1) ) pParse->iErr = j; @@ -1218,7 +1218,7 @@ json_parse_restart: case '\'': { u8 jnFlags; char cDelim; - pParse->has5 = 1; + pParse->hasNonstd = 1; jnFlags = JNODE_JSON5; goto parse_string; case '"': @@ -1245,11 +1245,11 @@ json_parse_restart: && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) || (c=='x' && jsonIs2Hex(&z[j+1])) ){ jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); - pParse->has5 = 1; + pParse->hasNonstd = 1; }else if( c=='\r' ){ if( z[j+1]=='\n' ) j++; jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); - pParse->has5 = 1; + pParse->hasNonstd = 1; }else{ pParse->iErr = j; return -1; @@ -1288,12 +1288,12 @@ json_parse_restart: } case '+': { u8 seenDP, seenE, jnFlags; - pParse->has5 = 1; + pParse->hasNonstd = 1; jnFlags = JNODE_JSON5; goto parse_number; case '.': if( sqlite3Isdigit(z[i+1]) ){ - pParse->has5 = 1; + pParse->hasNonstd = 1; jnFlags = JNODE_JSON5; seenE = 0; seenDP = JSON_REAL; @@ -1326,7 +1326,7 @@ json_parse_restart: if( c=='0' ){ if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ assert( seenDP==JSON_INT ); - pParse->has5 = 1; + pParse->hasNonstd = 1; jnFlags |= JNODE_JSON5; for(j=i+3; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; @@ -1359,7 +1359,7 @@ json_parse_restart: } #endif if( z[i+1]=='.' ){ - pParse->has5 = 1; + pParse->hasNonstd = 1; jnFlags |= JNODE_JSON5; goto parse_number_2; } @@ -1371,7 +1371,7 @@ json_parse_restart: pParse->iErr = i+1; return -1; }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ - pParse->has5 = 1; + pParse->hasNonstd = 1; jnFlags |= JNODE_JSON5; for(j=i+4; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; @@ -1394,7 +1394,7 @@ json_parse_restart: if( c=='e' || c=='E' ){ if( z[j-1]<'0' ){ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ - pParse->has5 = 1; + pParse->hasNonstd = 1; jnFlags |= JNODE_JSON5; }else{ pParse->iErr = j; @@ -1422,7 +1422,7 @@ json_parse_restart: } if( z[j-1]<'0' ){ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ - pParse->has5 = 1; + pParse->hasNonstd = 1; jnFlags |= JNODE_JSON5; }else{ pParse->iErr = j; @@ -1436,7 +1436,7 @@ json_parse_restart: case 'N': { if( strncmp(&z[i],"NaN",3)==0 ){ jsonParseAddNode(pParse, JSON_NULL, 4, "null"); - pParse->has5 = 1; + pParse->hasNonstd = 1; return i+3; } pParse->iErr = i; @@ -1445,7 +1445,7 @@ json_parse_restart: case 'I': { if( strncmp(&z[i],"Infinity",8)==0 ){ jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); - pParse->has5 = 1; + pParse->hasNonstd = 1; return i+8; } pParse->iErr = i; @@ -1490,7 +1490,7 @@ json_parse_restart: j = json5Whitespace(&z[i]); if( j>0 ){ i += j; - pParse->has5 = 1; + pParse->hasNonstd = 1; goto json_parse_restart; } pParse->iErr = i; @@ -1545,7 +1545,7 @@ static int jsonParse( jsonParseReset(pParse); return 1; } - pParse->has5 = 1; + pParse->hasNonstd = 1; } } if( i<=0 ){ @@ -2571,43 +2571,38 @@ static void jsonValidFunc( sqlite3_result_error_nomem(ctx); sqlite3_free(p); }else{ - sqlite3_result_int(ctx, p->nErr==0 && p->has5==0); + sqlite3_result_int(ctx, p->nErr==0 && p->hasNonstd==0); if( p->nErr ) jsonParseFree(p); } } - /* -** json_valid5(JSON) +** json_error_position(JSON) ** -** Return 1 if JSON is a well-formed JSON5 string. -** Return 0 otherwise. -*/ -static void jsonValid5Func( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv -){ - JsonParse *p; /* The parse */ - UNUSED_PARAMETER(argc); - p = jsonParseCached(ctx, argv, 0); - if( p==0 || p->oom ){ - sqlite3_result_error_nomem(ctx); - sqlite3_free(p); - }else{ - sqlite3_result_int(ctx, p->nErr==0); - if( p->nErr ) jsonParseFree(p); - } -} - - - -/* -** json_error(JSON) +** If the argument is not an interpretable JSON string, then return the 1-based +** character position at which the parser first recognized that the input +** was in error. The left-most character is 1. If the string is valid +** JSON, then return 0. ** -** If JSON is not a well-formed JSON5 string, then return the 1-based -** character offset to the location of the first error in that string. -** Return 0 otherwise. +** Note that json_valid() is only true for strictly conforming canonical JSON. +** But this routine returns zero if the input contains extension. Thus: +** +** (1) If the input X is strictly conforming canonical JSON: +** +** json_valid(X) returns true +** json_error_position(X) returns 0 +** +** (2) If the input X is JSON but it includes extension (such as JSON5) that +** are not part of RFC-8259: +** +** json_valid(X) returns false +** json_error_position(X) return 0 +** +** (3) If the input X cannot be interpreted as JSON even taking extensions +** into account: +** +** json_valid(X) return false +** json_error_position(X) returns 1 or more */ static void jsonErrorFunc( sqlite3_context *ctx, @@ -3344,7 +3339,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_array, -1, 0, jsonArrayFunc), JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), - JFUNCTION(json_error, 1, 0, jsonErrorFunc), + JFUNCTION(json_error_position,1, 0, jsonErrorFunc), JFUNCTION(json_extract, -1, 0, jsonExtractFunc), JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), @@ -3358,7 +3353,6 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_type, 1, 0, jsonTypeFunc), JFUNCTION(json_type, 2, 0, jsonTypeFunc), JFUNCTION(json_valid, 1, 0, jsonValidFunc), - JFUNCTION(json_valid5, 1, 0, jsonValid5Func), #if SQLITE_DEBUG JFUNCTION(json_parse, 1, 0, jsonParseFunc), JFUNCTION(json_test1, 1, 0, jsonTest1Func), diff --git a/test/json101.test b/test/json101.test index 3fc3923da3..298dc3bdbc 100644 --- a/test/json101.test +++ b/test/json101.test @@ -310,8 +310,8 @@ do_execsql_test json-6.1 { SELECT json_valid('{"a":55,"b":72,}'); } {0} do_execsql_test json-6.2 { - SELECT json_valid5('{"a":55,"b":72,}'); -} {1} + SELECT json_error_position('{"a":55,"b":72,}'); +} {0} do_execsql_test json-6.3 { SELECT json_valid(json('{"a":55,"b":72,}')); } {1} @@ -319,23 +319,23 @@ do_execsql_test json-6.4 { SELECT json_valid('{"a":55,"b":72 , }'); } {0} do_execsql_test json-6.5 { - SELECT json_valid5('{"a":55,"b":72 , }'); -} {1} -do_execsql_test json-6.6 { - SELECT json_valid5('{"a":55,"b":72,,}'); + SELECT json_error_position('{"a":55,"b":72 , }'); } {0} +do_execsql_test json-6.6 { + SELECT json_error_position('{"a":55,"b":72,,}'); +} {16} do_execsql_test json-6.7 { SELECT json_valid('{"a":55,"b":72}'); } {1} do_execsql_test json-6.8 { - SELECT json_valid5('["a",55,"b",72,]'); -} {1} -do_execsql_test json-6.9 { - SELECT json_valid5('["a",55,"b",72 , ]'); -} {1} -do_execsql_test json-6.10 { - SELECT json_valid5('["a",55,"b",72,,]'); + SELECT json_error_position('["a",55,"b",72,]'); } {0} +do_execsql_test json-6.9 { + SELECT json_error_position('["a",55,"b",72 , ]'); +} {0} +do_execsql_test json-6.10 { + SELECT json_error_position('["a",55,"b",72,,]'); +} {16} do_execsql_test json-6.11 { SELECT json_valid('["a",55,"b",72]'); } {1} diff --git a/test/json102.test b/test/json102.test index 2594feafa1..bfd5e7ed07 100644 --- a/test/json102.test +++ b/test/json102.test @@ -319,7 +319,7 @@ foreach {id j x0 x5} { 1415 {'{"x":+5.5}'} 0 1 } { do_execsql_test json102-$id " - SELECT json_valid($j), json_valid5($j); + SELECT json_valid($j), NOT json_error_position($j); " [list $x0 $x5] } diff --git a/test/json501.test b/test/json501.test index fbf7bfe9db..4d6a058936 100644 --- a/test/json501.test +++ b/test/json501.test @@ -63,7 +63,7 @@ set testprefix json501 # 1) Object keys may be an ECMAScript 5.1 IdentifierName. do_execsql_test 1.1 { WITH c(x) AS (VALUES('{a:5,b:6}')) - SELECT x->>'a', json(x), json_valid(x), json_valid5(x) FROM c; + SELECT x->>'a', json(x), json_valid(x), NOT json_error_position(x) FROM c; } {5 {{"a":5,"b":6}} 0 1} do_execsql_test 1.2 { SELECT '[7,null,{a:5,b:6},[8,9]]'->>'$[2].b'; @@ -98,7 +98,7 @@ do_catchsql_test 1.11 { do_execsql_test 2.1 { WITH c(x) AS (VALUES('{"a":5, "b":6, }')) - SELECT x->>'b', json(x), json_valid(x), json_valid5(x) FROM c; + SELECT x->>'b', json(x), json_valid(x), NOT json_error_position(x) FROM c; } {6 {{"a":5,"b":6}} 0 1} do_execsql_test 2.2 { SELECT '{a:5, b:6 , }'->>'b'; @@ -115,7 +115,7 @@ do_catchsql_test 2.4 { do_execsql_test 3.1 { WITH c(x) AS (VALUES('[5, 6,]')) - SELECT x->>1, json(x), json_valid(x), json_valid5(x) FROM c; + SELECT x->>1, json(x), json_valid(x), NOT json_error_position(x) FROM c; } {6 {[5,6]} 0 1} do_execsql_test 3.2 { SELECT '[5, 6 , ]'->>1; @@ -132,7 +132,7 @@ do_catchsql_test 3.4 { do_execsql_test 4.1 { WITH c(x) AS (VALUES('{"a": ''abcd''}')) - SELECT x->>'a', json(x), json_valid(x), json_valid5(x) FROM c; + SELECT x->>'a', json(x), json_valid(x), NOT json_error_position(x) FROM c; } {abcd {{"a":"abcd"}} 0 1} do_execsql_test 4.2 { SELECT '{b: 123, ''a'': ''ab\''cd''}'->>'a'; @@ -143,7 +143,7 @@ do_execsql_test 4.2 { do_execsql_test 5.1 { WITH c(x) AS (VALUES('{a: "abc'||char(0x5c,0x0a)||'xyz"}')) - SELECT x->>'a', json(x), json_valid(x), json_valid5(x) FROM c; + SELECT x->>'a', json(x), json_valid(x), NOT json_error_position(x) FROM c; } {abcxyz {{"a":"abcxyz"}} 0 1} do_execsql_test 5.2 { SELECT ('{a: "abc'||char(0x5c,0x0d)||'xyz"}')->>'a'; From 4a398c317d919321205668b010a4955e2325242c Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 30 Apr 2023 19:45:25 +0000 Subject: [PATCH 34/37] All JSON to understand floating point literals "Inf" and "QNaN" and "SNaN" in any case, without the SQLITE_EXTENDED_NAN_INF compile-time option. This extension is always available. FossilOrigin-Name: fb551145e0d84213b3343dc1bc7db70c898b9dea24a72b968240617f4b52d821 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 42 ++++++------------------------------------ 3 files changed, 13 insertions(+), 43 deletions(-) diff --git a/manifest b/manifest index c7969eb46e..0b4e7c7276 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Omit\sthe\sjson_valid()\sfunction.\s\sChange\sthe\sname\sof\sjson_error()\sto\njson_error_position().\s\sUse\s"NOT\sjson_error_position(X)"\sas\sa\ssubstitute\nfor\s"json_valid5(X)". -D 2023-04-30T19:34:41.159 +C All\sJSON\sto\sunderstand\sfloating\spoint\sliterals\s"Inf"\sand\s"QNaN"\sand\s"SNaN"\sin\nany\scase,\swithout\sthe\sSQLITE_EXTENDED_NAN_INF\scompile-time\soption.\s\sThis\nextension\sis\salways\savailable. +D 2023-04-30T19:45:25.975 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -595,7 +595,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c d216510dbb7900072e4a0cbb1273a21e0a20db57da405b4397abb236a2f30392 +F src/json.c bd22944b15d786a7a17f0faa672cdb3abeef1b59c505684df64caaca2a18a03a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2068,8 +2068,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 30d12edebad9b097cd5f0da355304d1cb2f8b70d7c7dff378fd7ad7c8ebf9279 -R 5a03c0d318d1b5343d158677e6158a89 +P 34c4e900a9cc51630eeaf01deef74bf5b18d66e0ab1dc61a2023ac8f837a5197 +R 6addb4498a8cf4456a809d957c22b7a6 U drh -Z 50768d32c83122dec00455a3ce19a1c8 +Z 050299ed4706f61a98d79f7a5528f481 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fa442e6283..db3c346403 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -34c4e900a9cc51630eeaf01deef74bf5b18d66e0ab1dc61a2023ac8f837a5197 \ No newline at end of file +fb551145e0d84213b3343dc1bc7db70c898b9dea24a72b968240617f4b52d821 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 8fa08d2443..c221193282 100644 --- a/src/json.c +++ b/src/json.c @@ -1029,8 +1029,6 @@ static int json5Whitespace(const char *zIn){ return n; } - -#ifdef SQLITE_EXTENDED_NAN_INF /* ** Extra floating-point literals to allow in JSON. */ @@ -1049,7 +1047,6 @@ static const struct NanInfName { { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, }; -#endif /* SQLITE_EXTENDED_NAN_INF */ /* ** Parse a single JSON value which begins at pParse->zJson[i]. Return the @@ -1336,28 +1333,20 @@ json_parse_restart: } }else{ if( !sqlite3Isdigit(z[i+1]) ){ - if( z[i+1]=='I' && strncmp(&z[i+1], "Infinity",8)==0 ){ - if( z[i]=='-' ){ - jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); - }else{ - jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); - } - return i+9; - } -#ifdef SQLITE_EXTENDED_NAN_INF - /* Non-standard JSON and JSON5: Allow "Inf" as an alternative - ** spelling for "Infinity" and allow it to be in any case. */ + /* JSON5 allows for "+Infinity" and "-Infinity" using exactly + ** that case. SQLite also allows these in any case and it allows + ** "+inf" and "-inf". */ if( (z[i+1]=='I' || z[i+1]=='i') && sqlite3StrNICmp(&z[i+1], "inf",3)==0 ){ + pParse->hasNonstd = 1; if( z[i]=='-' ){ jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); }else{ jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); } - return i+4; + return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); } -#endif if( z[i+1]=='.' ){ pParse->hasNonstd = 1; jnFlags |= JNODE_JSON5; @@ -1433,24 +1422,6 @@ json_parse_restart: jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]); return j; } - case 'N': { - if( strncmp(&z[i],"NaN",3)==0 ){ - jsonParseAddNode(pParse, JSON_NULL, 4, "null"); - pParse->hasNonstd = 1; - return i+3; - } - pParse->iErr = i; - return -1; - } - case 'I': { - if( strncmp(&z[i],"Infinity",8)==0 ){ - jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); - pParse->hasNonstd = 1; - return i+8; - } - pParse->iErr = i; - return -1; - } case '}': { pParse->iErr = i; return -2; /* End of {...} */ @@ -1497,7 +1468,6 @@ json_parse_restart: return -1; } default: { -#ifdef SQLITE_EXTENDED_NAN_INF int k, nn; c = z[i]; for(k=0; khasNonstd = 1; return i + nn; } -#endif pParse->iErr = i; return -1; /* Syntax error */ } From dae7ae359ed3632b56907435a9606c92e0d0070b Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 30 Apr 2023 20:37:49 +0000 Subject: [PATCH 35/37] Accept the full ECMAScript 5.1 IdentifyName syntax for keys in objects. FossilOrigin-Name: 9be2c87518b33713210e3e2fa56924888e19415c71329141d18150b275f6f25e --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/global.c | 2 +- src/json.c | 22 +++++++++++++++++----- src/sqliteInt.h | 4 ++++ test/json501.test | 9 +++------ 6 files changed, 35 insertions(+), 22 deletions(-) diff --git a/manifest b/manifest index 0b4e7c7276..5f77a9a3c6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C All\sJSON\sto\sunderstand\sfloating\spoint\sliterals\s"Inf"\sand\s"QNaN"\sand\s"SNaN"\sin\nany\scase,\swithout\sthe\sSQLITE_EXTENDED_NAN_INF\scompile-time\soption.\s\sThis\nextension\sis\salways\savailable. -D 2023-04-30T19:45:25.975 +C Accept\sthe\sfull\sECMAScript\s5.1\sIdentifyName\ssyntax\sfor\skeys\sin\sobjects. +D 2023-04-30T20:37:49.462 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -589,13 +589,13 @@ F src/expr.c 6353f4d92d9f67ec3466d8e6978cd31a45e34cb755c4d11e689077f03f7c0a15 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 03c134cc8bffe54835f742ddea0b72ebfc8f6b32773d175c71b8afeea6cb5c83 F src/func.c d187be57a886ddf4e6b7ef584a494361899be3df5eee6d4a747b68ff4aff4122 -F src/global.c 428d2580a1cdf5dbe1f356d1feab83710ae0cc862ece0fb57bc8259e43838c74 +F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30 F src/hash.c c6af5f96a7a76d000f07c5402c48c318c2566beecdee9e78b9d9f60ce7119565 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c bd22944b15d786a7a17f0faa672cdb3abeef1b59c505684df64caaca2a18a03a +F src/json.c 0915244c5eae7f80d4a7418ddac3186add8f7cc7185fd9fa7a64f5d9d3a5507b F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -640,7 +640,7 @@ F src/shell.c.in 09097e1b9df1f8092e85bf89979e12ca7b608d7efc84551b5d0c8de4dded779 F src/sqlite.h.in 4fff9c6cc5d4cbba9532a668112efb6dc469c425e1a2196664d7c07d508363ef F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h bf15f7db635d2e64a227bbf86845bc19755dbd932021a6461d6dd15b0da2cfd3 +F src/sqliteInt.h 6766b36c215e33d2cbfd48e632b7cc516092273d909672fdc095bcbd8c005ba1 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -1261,7 +1261,7 @@ F test/json102.test 13dc9e7b7f359ecb861e02f9bd7019f7342a63d1c354273b0a8f39040505 F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d -F test/json501.test 9b14d14f26659287277a1f6fcf2a932a0a2351e1aae522586b530498c977ce45 +F test/json501.test f71710f60fa45b19dc336fbaac9e8362f70f80cf81badefdb845ed3f7c7c2ccc F test/json502.test 66d150cc098674b8bf4354526a8dd411b926f43ca892306bcb3b6d3f93fef7be F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b @@ -2068,8 +2068,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 34c4e900a9cc51630eeaf01deef74bf5b18d66e0ab1dc61a2023ac8f837a5197 -R 6addb4498a8cf4456a809d957c22b7a6 +P fb551145e0d84213b3343dc1bc7db70c898b9dea24a72b968240617f4b52d821 +R b58bc780aaf2d8c5cd999008db262a63 U drh -Z 050299ed4706f61a98d79f7a5528f481 +Z 3139cc4432c28195fb82dc0d5445fb75 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index db3c346403..302a6bf45d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fb551145e0d84213b3343dc1bc7db70c898b9dea24a72b968240617f4b52d821 \ No newline at end of file +9be2c87518b33713210e3e2fa56924888e19415c71329141d18150b275f6f25e \ No newline at end of file diff --git a/src/global.c b/src/global.c index b018c50027..fcba7d7fa2 100644 --- a/src/global.c +++ b/src/global.c @@ -97,7 +97,7 @@ const unsigned char *sqlite3aGTb = &sqlite3UpperToLower[256+12-OP_Ne]; ** isalnum() 0x06 ** isxdigit() 0x08 ** toupper() 0x20 -** SQLite identifier character 0x40 +** SQLite identifier character 0x40 $, _, or non-ascii ** Quote character 0x80 ** ** Bit 0x20 is set if the mapped character requires translation to upper diff --git a/src/json.c b/src/json.c index c221193282..1c89cb43ce 100644 --- a/src/json.c +++ b/src/json.c @@ -552,7 +552,13 @@ static void jsonRenderNode( case JSON_STRING: { assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ - jsonAppendString(pOut, pNode->u.zJContent, pNode->n); + if( pNode->jnFlags & JNODE_LABEL ){ + jsonAppendChar(pOut, '"'); + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + jsonAppendChar(pOut, '"'); + }else{ + jsonAppendString(pOut, pNode->u.zJContent, pNode->n); + } }else if( pNode->jnFlags & JNODE_JSON5 ){ jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); }else{ @@ -1046,7 +1052,7 @@ static const struct NanInfName { { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" }, { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, -}; +}; /* ** Parse a single JSON value which begins at pParse->zJson[i]. Return the @@ -1088,9 +1094,15 @@ json_parse_restart: break; } j += json5Whitespace(&z[j]); - if( sqlite3Isalpha(z[j]) || z[j]=='_' || z[j]=='$' ){ - int k; - for(k=j+1; sqlite3Isalnum(z[k]) || z[k]=='_' || z[k]=='$'; k++){} + if( sqlite3JsonId1(z[j]) + || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2])) + ){ + int k = j+1; + while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) + || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2])) + ){ + k++; + } jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]); pParse->hasNonstd = 1; x = k; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4739951a5d..b06e192553 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4456,6 +4456,8 @@ int sqlite3CantopenError(int); # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) +# define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42) +# define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46) #else # define sqlite3Toupper(x) toupper((unsigned char)(x)) # define sqlite3Isspace(x) isspace((unsigned char)(x)) @@ -4465,6 +4467,8 @@ int sqlite3CantopenError(int); # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) # define sqlite3Tolower(x) tolower((unsigned char)(x)) # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') +# define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0') +# define sqlite3JsonId2(x) sqlite3IsIdChar(x) #endif int sqlite3IsIdChar(u8); diff --git a/test/json501.test b/test/json501.test index 4d6a058936..a37326973a 100644 --- a/test/json501.test +++ b/test/json501.test @@ -86,12 +86,9 @@ do_catchsql_test 1.10 { SELECT json('{ MNO_123/xyz : 789 }'); } {1 {malformed JSON}} -# Contra the JSON5 standard, SQLite does not allow non-ASCII characters in -# an unquoted object label. -# -do_catchsql_test 1.11 { - SELECT json('{ MNO_123æxyz : 789 }'); -} {1 {malformed JSON}} +do_execsql_test 1.11 { + SELECT '{ MNO_123æxyz : 789 }'->>'MNO_123æxyz'; +} {789} ############################################################################### # 2) Objects may have a single trailing comma. From 5196d9350556cbd301d7bd89a342a068207a4869 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 30 Apr 2023 23:52:55 +0000 Subject: [PATCH 36/37] Fix a problem with json_patch() when one side or the other is JSON5. dbsqlfuzz bc10593a4ba8e7a7862593532285be31f00f8e41 FossilOrigin-Name: e18c0899cc774f6f20d2a73381fa3ab3af9355cf4d108c7612db259eadbb8b96 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 15 +++++++++++---- test/json104.test | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 5f77a9a3c6..5733fbf0e5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Accept\sthe\sfull\sECMAScript\s5.1\sIdentifyName\ssyntax\sfor\skeys\sin\sobjects. -D 2023-04-30T20:37:49.462 +C Fix\sa\sproblem\swith\sjson_patch()\swhen\sone\sside\sor\sthe\sother\sis\sJSON5.\ndbsqlfuzz\sbc10593a4ba8e7a7862593532285be31f00f8e41 +D 2023-04-30T23:52:55.182 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -595,7 +595,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 0915244c5eae7f80d4a7418ddac3186add8f7cc7185fd9fa7a64f5d9d3a5507b +F src/json.c 359e99789cbc5db24e14b70e8f615155f5ae0138528eb863bdc114e6c6205c32 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -1259,7 +1259,7 @@ F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3 F test/json101.test 211d75638782370c07e10a847bd66e501ea1537f39c7da4447bfa055c0f261db F test/json102.test 13dc9e7b7f359ecb861e02f9bd7019f7342a63d1c354273b0a8f3904050560a8 F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe -F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 +F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d F test/json501.test f71710f60fa45b19dc336fbaac9e8362f70f80cf81badefdb845ed3f7c7c2ccc F test/json502.test 66d150cc098674b8bf4354526a8dd411b926f43ca892306bcb3b6d3f93fef7be @@ -2068,8 +2068,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P fb551145e0d84213b3343dc1bc7db70c898b9dea24a72b968240617f4b52d821 -R b58bc780aaf2d8c5cd999008db262a63 +P 9be2c87518b33713210e3e2fa56924888e19415c71329141d18150b275f6f25e +R 7a6b3c9a24ecf3fbc1a69d64509d805d U drh -Z 3139cc4432c28195fb82dc0d5445fb75 +Z 5033e057782cc2feec974bc4cc7a7826 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 302a6bf45d..3e1e31f59e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9be2c87518b33713210e3e2fa56924888e19415c71329141d18150b275f6f25e \ No newline at end of file +e18c0899cc774f6f20d2a73381fa3ab3af9355cf4d108c7612db259eadbb8b96 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1c89cb43ce..7e112c30dc 100644 --- a/src/json.c +++ b/src/json.c @@ -1675,7 +1675,7 @@ static JsonParse *jsonParseCached( ** Compare the OBJECT label at pNode against zKey,nKey. Return true on ** a match. */ -static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ +static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ if( pNode->n!=nKey ) return 0; @@ -1685,6 +1685,15 @@ static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; } } +static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ + if( p1->jnFlags & JNODE_RAW ){ + return jsonLabelCompare(p2, p1->u.zJContent, p1->n); + }else if( p2->jnFlags & JNODE_RAW ){ + return jsonLabelCompare(p1, p2->u.zJContent, p2->n); + }else{ + return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0; + } +} /* forward declaration */ static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); @@ -2246,12 +2255,10 @@ static JsonNode *jsonMergePatch( assert( pPatch[i].eU==1 ); nKey = pPatch[i].n; zKey = pPatch[i].u.zJContent; - assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); for(j=1; jn; j += jsonNodeSize(&pTarget[j+1])+1 ){ assert( pTarget[j].eType==JSON_STRING ); assert( pTarget[j].jnFlags & JNODE_LABEL ); - assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); - if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){ + if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){ if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; if( pPatch[i+1].eType==JSON_NULL ){ pTarget[j+1].jnFlags |= JNODE_REMOVE; diff --git a/test/json104.test b/test/json104.test index 56dd2738c3..c3c43d1e98 100644 --- a/test/json104.test +++ b/test/json104.test @@ -30,6 +30,48 @@ do_execsql_test json104-100 { } }'); } {{{"a":"z","c":{"d":"e"}}}} +do_execsql_test json104-101 { + SELECT json_patch('{ + "a": "b", + "c": { + "d": "e", + "f": "g" + } + }','{ + a:"z", + c: { + f: null + } + }'); +} {{{"a":"z","c":{"d":"e"}}}} +do_execsql_test json104-102 { + SELECT json_patch('{ + a: "b", + c: { + d: "e", + f: "g" + } + }','{ + "a":"z", + "c": { + "f": null + } + }'); +} {{{"a":"z","c":{"d":"e"}}}} +do_execsql_test json104-103 { + SELECT json_patch('{ + a: "b", + c: { + d: "e", + f: "g" + } + }','{ + a:"z", + c: { + f: null + } + }'); +} {{{"a":"z","c":{"d":"e"}}}} # This is the example from pages 4 and 5 of RFC-7396 From e210c9390321379f9a5c0b7a205e67128ab12192 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 1 May 2023 03:56:48 +0000 Subject: [PATCH 37/37] Fix abbreviated paths for objects such that they work even if the object key begins with '$'. FossilOrigin-Name: 1b991c78141a9915ae9350ecb347a758e50d7d25c8a0f4cc098ae10d47c27043 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 5733fbf0e5..28fca241a0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\sjson_patch()\swhen\sone\sside\sor\sthe\sother\sis\sJSON5.\ndbsqlfuzz\sbc10593a4ba8e7a7862593532285be31f00f8e41 -D 2023-04-30T23:52:55.182 +C Fix\sabbreviated\spaths\sfor\sobjects\ssuch\sthat\sthey\swork\seven\sif\sthe\sobject\nkey\sbegins\swith\s'$'. +D 2023-05-01T03:56:48.067 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -595,7 +595,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4 -F src/json.c 359e99789cbc5db24e14b70e8f615155f5ae0138528eb863bdc114e6c6205c32 +F src/json.c dd76caa9c9f1b417f7d7cdee06baabcbfc4c5ee18a578150d8f19c28260abeac F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d @@ -2068,8 +2068,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9be2c87518b33713210e3e2fa56924888e19415c71329141d18150b275f6f25e -R 7a6b3c9a24ecf3fbc1a69d64509d805d +P e18c0899cc774f6f20d2a73381fa3ab3af9355cf4d108c7612db259eadbb8b96 +R d7c6513749c47dbdaa7fb128b7fd565e U drh -Z 5033e057782cc2feec974bc4cc7a7826 +Z 1d9a6af1cfa9e670ad28e827814897c7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3e1e31f59e..4bf24a906a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e18c0899cc774f6f20d2a73381fa3ab3af9355cf4d108c7612db259eadbb8b96 \ No newline at end of file +1b991c78141a9915ae9350ecb347a758e50d7d25c8a0f4cc098ae10d47c27043 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 7e112c30dc..aae48c291a 100644 --- a/src/json.c +++ b/src/json.c @@ -2164,7 +2164,7 @@ static void jsonExtractFunc( zPath = (const char*)sqlite3_value_text(argv[1]); if( zPath==0 ) return; if( flags & JSON_ABPATH ){ - if( zPath[0]!='$' ){ + if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){ /* The -> and ->> operators accept abbreviated PATH arguments. This ** is mostly for compatibility with PostgreSQL, but also for ** convenience.