From 3c648882532dcf174145d6d76e92da041fad0662 Mon Sep 17 00:00:00 2001 From: mistachkin Date: Thu, 6 Jan 2022 17:13:56 +0000 Subject: [PATCH 1/9] Add the '-guard:cf' compiler option for Windows 10, per [forum:/forumpost/8d3b4ad694|forum post 8d3b4ad694]. FossilOrigin-Name: 2d6a16caa7d28ad5c766036b2eb6c2020683fcc9389b3c7df2013739929dd36f --- Makefile.msc | 12 ++++++------ autoconf/Makefile.msc | 8 ++++---- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Makefile.msc b/Makefile.msc index 3cbf560d7c..42f4648875 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -580,17 +580,17 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS) # !IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0 !IF "$(PLATFORM)"=="x86" -CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall # <> -TEST_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl +TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl # <> !ELSE !IFNDEF PLATFORM -CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall # <> -TEST_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl +TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl # <> !ELSE CORE_CCONV_OPTS = diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc index 40d0e8113d..524aeb6f41 100644 --- a/autoconf/Makefile.msc +++ b/autoconf/Makefile.msc @@ -502,12 +502,12 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS) # !IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0 !IF "$(PLATFORM)"=="x86" -CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall !ELSE !IFNDEF PLATFORM -CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall !ELSE CORE_CCONV_OPTS = SHELL_CCONV_OPTS = diff --git a/manifest b/manifest index f0032d5289..66cd8be755 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ -C Remove\stwo\sNEVER()\smacros\sthat\scan\ssometimes\sbe\strue\sif\sthe\sdatabase\sis\ncorrupt.\s\sdbsqlfuzz\s0414d2c18290fc80fd5fb540def7d3e46c1ae9c6. -D 2022-01-05T21:01:26.927 +C Add\sthe\s'-guard:cf'\scompiler\soption\sfor\sWindows\s10,\sper\s[forum:/forumpost/8d3b4ad694|forum\spost\s8d3b4ad694]. +D 2022-01-06T17:13:56.835 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F Makefile.in 0e91c42a1dd13a569b1fa4f4dfb7d3632f3164a1c05c71341533d67db5b641dd F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 -F Makefile.msc 88f05063ee36a5fb77d69b877ef824d0743b5325c95bfbf74d6ff17bd1c9fb1f +F Makefile.msc 517f22463b99b56f6c4c81afcf09c7bf3a0289d08f2dadcc0d5322819dbd6017 F README.md 2dd87a5c1d108b224921f3dd47dea567973f706e1f6959386282a626f459a70c F VERSION 392c2f83569705069415a5d98b1c138ec8fe8a56a663a0d94cea019e806537b2 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -15,7 +15,7 @@ F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903 F autoconf/Makefile.am a8d1d24affe52ebf8d7ddcf91aa973fa0316618ab95bb68c87cabf8faf527dc8 F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac -F autoconf/Makefile.msc d146a08ebbdf7f881ba600a49cd8dce40c4c807addcdb4b9b6a507e4b40ce837 +F autoconf/Makefile.msc 262dad10223b39a6853c4faefadb0f71931ca55c742fa285f8ee8d56b7543ddc F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7 F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1 F autoconf/configure.ac a8ba2a9e61216f5093d44f3b7d2cb8fe1890d6b7dc330a02f802d8efaa1fdc79 @@ -1937,8 +1937,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 7ae596dd4a73a09585c5dc9f4faf75d126d0733fc2fb32c1de64126a1088d967 -R 7175e1e9a16516f1ab12321ca929e683 -U drh -Z 45714cdd1039a8cc38c8df2ff917256e +P b6a82f3c3b9d89fdf628c7f117b6a4a64383a36c84fe84d47c80e845c9bd8a4f +R 77a4280a2931dd249bcf6514bda5d68b +U mistachkin +Z 912599d00221acb9d08acd46c2ac9173 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3671abe1cd..ba34d0d3e9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b6a82f3c3b9d89fdf628c7f117b6a4a64383a36c84fe84d47c80e845c9bd8a4f \ No newline at end of file +2d6a16caa7d28ad5c766036b2eb6c2020683fcc9389b3c7df2013739929dd36f \ No newline at end of file From d5326c333f2189542d844350b3268804028d501a Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 7 Jan 2022 14:58:47 +0000 Subject: [PATCH 2/9] Add new binary operators "->" and "->>" to the parser that evaluate to 2-argument SQL functions by the same name. Add new "->" and "->>" functions to the JSON extension that are aliases for json_extract(). FossilOrigin-Name: c4e4e3a3fc5da0381ccb7930706e57d7831d87f9c63bafe49ae64117701e1cfe --- ext/misc/json1.c | 2 ++ manifest | 23 +++++++++++++---------- manifest.uuid | 2 +- src/parse.y | 10 ++++++++-- src/tokenize.c | 3 +++ test/func.test | 15 +++++++++++++++ 6 files changed, 42 insertions(+), 13 deletions(-) diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 787b46982e..5addd7dbcc 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -2649,6 +2649,8 @@ int sqlite3Json1Init(sqlite3 *db){ { "json_array_length", 1, 0, jsonArrayLengthFunc }, { "json_array_length", 2, 0, jsonArrayLengthFunc }, { "json_extract", -1, 0, jsonExtractFunc }, + { "->", 2, 1, jsonExtractFunc }, + { "->>", 2, 1, jsonExtractFunc }, { "json_insert", -1, 0, jsonSetFunc }, { "json_object", -1, 0, jsonObjectFunc }, { "json_patch", 2, 0, jsonPatchFunc }, diff --git a/manifest b/manifest index 66cd8be755..b962a3d961 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\s'-guard:cf'\scompiler\soption\sfor\sWindows\s10,\sper\s[forum:/forumpost/8d3b4ad694|forum\spost\s8d3b4ad694]. -D 2022-01-06T17:13:56.835 +C Add\snew\sbinary\soperators\s"->"\sand\s"->>"\sto\sthe\sparser\sthat\sevaluate\sto\n2-argument\sSQL\sfunctions\sby\sthe\ssame\sname.\s\sAdd\snew\s"->"\sand\s"->>"\sfunctions\nto\sthe\sJSON\sextension\sthat\sare\saliases\sfor\sjson_extract(). +D 2022-01-07T14:58:47.701 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -306,7 +306,7 @@ F ext/misc/fileio.c 4e7f7cd30de8df4820c552f14af3c9ca451c5ffe1f2e7bef34d598a12ebf F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511 -F ext/misc/json1.c a4ea54d9b228901695a7f51df338dddb29a2a574b870d0c0cb58539cc90e9a31 +F ext/misc/json1.c ee5d8250f78d598e0c8b81fc37e8fa1b52098e5f7bb71f0db3cd39c1acc4ca78 F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -539,7 +539,7 @@ F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 55a9a8c745c47a49e6541b1d634f6152e3f72f76c6dbb04fe24413986a928091 F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f -F src/parse.y 761b5d30a7ea9bd2db3b3571438cfcceb5f7dbf4fcad6881c8de65bdda07135a +F src/parse.y 04f61db1cdd7036c6d74baad1c342d3e3110cb0765c48fcfd3bdf4e974a1e5bb F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65 @@ -614,7 +614,7 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9 F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c -F src/tokenize.c dc5367c6d42336b35d55f96d2f04cd6256e92bc6ecf74ed5d855d24e43343aff +F src/tokenize.c b74d878aa7c82ec8460779468061a96185e22257f68ab785b69abce354b70446 F src/treeview.c 9dfdb7ff7f6645d0a6458dbdf4ffac041c071c4533a6db8bb6e502b979ac67bc F src/trigger.c 40e7c3dcff57a770d5fa38ba21ed4725572fd2e224c58af61eb980598b60f9c8 F src/update.c d6f5c7b9e072660757ac7d58175aca11c07cb95ebbb297ae7f38853700f52328 @@ -1045,7 +1045,7 @@ F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f75 F test/fts4unicode.test 82a9c16b68ba2f358a856226bb2ee02f81583797bc4744061c54401bf1a0f4c9 F test/fts4upfrom.test f25835162c989dffd5e2ef91ec24c4848cc9973093e2d492d1c7b32afac1b49d F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d -F test/func.test 3a65ddb6c1998f71aa86492501a6be87904197e62bfb5b70b2493552b558abd1 +F test/func.test 0a305e88a4fbdce6be8c6f4863d6dab9ecb2975b95e94734621e2c900779b712 F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func3.test 600a632c305a88f3946d38f9a51efe145c989b2e13bd2b2a488db47fe76bab6a F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31 @@ -1937,8 +1937,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 b6a82f3c3b9d89fdf628c7f117b6a4a64383a36c84fe84d47c80e845c9bd8a4f -R 77a4280a2931dd249bcf6514bda5d68b -U mistachkin -Z 912599d00221acb9d08acd46c2ac9173 +P 2d6a16caa7d28ad5c766036b2eb6c2020683fcc9389b3c7df2013739929dd36f +R a976b35db7b5d2f5710ec976fab3e1b7 +T *branch * json-enhancements +T *sym-json-enhancements * +T -sym-trunk * +U drh +Z e4a5223ea04d8f66a6958c2ae1a2641b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ba34d0d3e9..20f17305ae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2d6a16caa7d28ad5c766036b2eb6c2020683fcc9389b3c7df2013739929dd36f \ No newline at end of file +c4e4e3a3fc5da0381ccb7930706e57d7831d87f9c63bafe49ae64117701e1cfe \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index 559057f9ae..5b83b16b60 100644 --- a/src/parse.y +++ b/src/parse.y @@ -236,7 +236,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);} // %token ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST. %token CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL. -%token OR AND NOT MATCH LIKE_KW BETWEEN IS IN ISNULL NOTNULL NE EQ. +%token OR AND NOT IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ. %token GT LE LT GE ESCAPE. // The following directive causes tokens ABORT, AFTER, ASC, etc. to @@ -286,7 +286,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);} %left BITAND BITOR LSHIFT RSHIFT. %left PLUS MINUS. %left STAR SLASH REM. -%left CONCAT. +%left CONCAT PTR. %left COLLATE. %right BITNOT. %nonassoc ON. @@ -1235,6 +1235,12 @@ expr(A) ::= PLUS|MINUS(B) expr(X). [BITNOT] { /*A-overwrites-B*/ } +expr(A) ::= expr(B) PTR(C) expr(D). { + ExprList *pList = sqlite3ExprListAppend(pParse, 0, B); + pList = sqlite3ExprListAppend(pParse, pList, D); + A = sqlite3ExprFunction(pParse, pList, &C, 0); +} + %type between_op {int} between_op(A) ::= BETWEEN. {A = 0;} between_op(A) ::= NOT BETWEEN. {A = 1;} diff --git a/src/tokenize.c b/src/tokenize.c index 5f41e5665e..347fd57323 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -290,6 +290,9 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; + }else if( z[1]=='>' ){ + *tokenType = TK_PTR; + return 2 + (z[2]=='>'); } *tokenType = TK_MINUS; return 1; diff --git a/test/func.test b/test/func.test index ca1027f508..008819baa5 100644 --- a/test/func.test +++ b/test/func.test @@ -1507,4 +1507,19 @@ do_execsql_test func-35.200 { PRAGMA integrity_check; } {ok} +# 2021-01-07: The -> and ->> operators. +# +proc ptr1 {a b} { return "$a->$b" } +db func -> ptr1 +proc ptr2 {a b} { return "$a->>$b" } +db func ->> ptr2 +do_execsql_test func-36.100 { + SELECT 123 -> 456 +} {123->456} +do_execsql_test func-36.110 { + SELECT 123 ->> 456 +} {123->>456} + + + finish_test From 12b9fa979cfd1e4c20423c80d4931591269da27b Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 7 Jan 2022 15:47:12 +0000 Subject: [PATCH 3/9] Accept abbreviated JSON Paths on the right-hand side of the -> and ->> operators. FossilOrigin-Name: d15410900dccbb7159683c29f640fa321b1e019718827334f5fefe89da623008 --- ext/misc/json1.c | 60 ++++++++++++++++++++++++++++++++++------------- manifest | 17 ++++++-------- manifest.uuid | 2 +- test/json102.test | 27 +++++++++++++++++++++ 4 files changed, 79 insertions(+), 27 deletions(-) diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 5addd7dbcc..6f66d4e70c 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -1598,34 +1598,62 @@ static void jsonExtractFunc( JsonNode *pNode; const char *zPath; JsonString jx; - int i; if( argc<2 ) return; p = jsonParseCached(ctx, argv, ctx); if( p==0 ) return; - jsonInit(&jx, ctx); - jsonAppendChar(&jx, '['); - for(i=1; inErr ) break; - if( argc>2 ){ + if( argc==2 ){ + /* With a single PATH argument, the return is the unquoted SQL value */ + zPath = (const char*)sqlite3_value_text(argv[1]); + if( zPath && zPath[0]!='$' && zPath[0]!=0 + && *(int*)sqlite3_user_data(ctx) + ){ + /* The -> and ->> operators accept abbreviated PATH arguments: + ** NUMBER ==> $[NUMBER] + ** LABEL ==> $.LABEL + ** [NUMBER] ==> $[NUMBER] + */ + jsonInit(&jx, ctx); + if( safe_isdigit(zPath[0]) ){ + jsonAppendRaw(&jx, "$[", 2); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendRaw(&jx, "]", 2); + }else{ + jsonAppendRaw(&jx, "$.", 1 + (zPath[0]!='[')); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendChar(&jx, 0); + } + pNode = jsonLookup(p, jx.zBuf, 0, ctx); + jsonReset(&jx); + }else{ + pNode = jsonLookup(p, zPath, 0, ctx); + } + if( p->nErr ) return; + if( pNode ) jsonReturn(pNode, ctx, 0); + }else{ + /* Two or more PATH arguments results in a JSON array with each + ** element of the array being the value selected by one of the PATHs */ + int i; + jsonInit(&jx, ctx); + jsonAppendChar(&jx, '['); + for(i=1; inErr ) break; jsonAppendSeparator(&jx); if( pNode ){ jsonRenderNode(pNode, &jx, 0); }else{ jsonAppendRaw(&jx, "null", 4); } - }else if( pNode ){ - jsonReturn(pNode, ctx, 0); } + if( i==argc ){ + jsonAppendChar(&jx, ']'); + jsonResult(&jx); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } + jsonReset(&jx); } - if( argc>2 && i==argc ){ - jsonAppendChar(&jx, ']'); - jsonResult(&jx); - sqlite3_result_subtype(ctx, JSON_SUBTYPE); - } - jsonReset(&jx); } /* This is the RFC 7396 MergePatch algorithm. diff --git a/manifest b/manifest index b962a3d961..3fa4543e06 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snew\sbinary\soperators\s"->"\sand\s"->>"\sto\sthe\sparser\sthat\sevaluate\sto\n2-argument\sSQL\sfunctions\sby\sthe\ssame\sname.\s\sAdd\snew\s"->"\sand\s"->>"\sfunctions\nto\sthe\sJSON\sextension\sthat\sare\saliases\sfor\sjson_extract(). -D 2022-01-07T14:58:47.701 +C Accept\sabbreviated\sJSON\sPaths\son\sthe\sright-hand\sside\sof\sthe\s->\sand\s->>\soperators. +D 2022-01-07T15:47:12.461 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -306,7 +306,7 @@ F ext/misc/fileio.c 4e7f7cd30de8df4820c552f14af3c9ca451c5ffe1f2e7bef34d598a12ebf F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511 -F ext/misc/json1.c ee5d8250f78d598e0c8b81fc37e8fa1b52098e5f7bb71f0db3cd39c1acc4ca78 +F ext/misc/json1.c 81a11151fbeefd8870c31a9e77576daeacc9ef1c66153950ea2eb084193235c6 F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -1147,7 +1147,7 @@ F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json101.test bb71538005f2d9e18620bdd3b76839a93ca0be61903eb8d751a64e78cf99b8fb -F test/json102.test eeb54efa221e50b74a2d6fb9259963b48d7414dca3ce2fdfdeed45cb28487bc1 +F test/json102.test 8ee925a282f03f47526ca4e260b8e4af9f4e5b562f38647aa4f32dc84c19a0a3 F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144 F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173 @@ -1937,11 +1937,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 2d6a16caa7d28ad5c766036b2eb6c2020683fcc9389b3c7df2013739929dd36f -R a976b35db7b5d2f5710ec976fab3e1b7 -T *branch * json-enhancements -T *sym-json-enhancements * -T -sym-trunk * +P c4e4e3a3fc5da0381ccb7930706e57d7831d87f9c63bafe49ae64117701e1cfe +R dd2f603ec9049bd111aea3757f3d8f8e U drh -Z e4a5223ea04d8f66a6958c2ae1a2641b +Z 9891da1f488bb126e807d6977835eae4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 20f17305ae..6b7aeda36e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c4e4e3a3fc5da0381ccb7930706e57d7831d87f9c63bafe49ae64117701e1cfe \ No newline at end of file +d15410900dccbb7159683c29f640fa321b1e019718827334f5fefe89da623008 \ No newline at end of file diff --git a/test/json102.test b/test/json102.test index 18a6bbf5cc..6afe2307b3 100644 --- a/test/json102.test +++ b/test/json102.test @@ -71,21 +71,48 @@ do_execsql_test json102-240 { do_execsql_test json102-250 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$'); } {{{"a":2,"c":[4,5,{"f":7}]}}} +do_execsql_test json102-251 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> '$'; +} {{{"a":2,"c":[4,5,{"f":7}]}}} do_execsql_test json102-260 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c'); } {{[4,5,{"f":7}]}} +do_execsql_test json102-261 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> '$.c'; +} {{[4,5,{"f":7}]}} +do_execsql_test json102-262 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> 'c'; +} {{[4,5,{"f":7}]}} do_execsql_test json102-270 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2]'); } {{{"f":7}}} +do_execsql_test json102-271 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> '$.c[2]'; +} {{{"f":7}}} +do_execsql_test json102-272 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> 'c' -> 2; +} {{{"f":7}}} do_execsql_test json102-280 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2].f'); } {{7}} +do_execsql_test json102-281 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> 'c' -> 2 -> 'f'; +} {{7}} +do_execsql_test json102-282 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> 'c' -> '[2]' -> 'f'; +} {{7}} do_execsql_test json102-290 { SELECT json_extract('{"a":2,"c":[4,5],"f":7}','$.c','$.a'); } {{[[4,5],2]}} do_execsql_test json102-300 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x'); } {{}} +do_execsql_test json102-301 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> 'x'; +} {{}} +do_execsql_test json102-302 { + SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> NULL; +} {{}} do_execsql_test json102-310 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x', '$.a'); } {{[null,2]}} From a4e4e18494f745b50f69e87de1275bbed5dd75e4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 7 Jan 2022 16:03:00 +0000 Subject: [PATCH 4/9] Add the json_ntype() SQL function. Works like the 1-argument json_type() except that it returns NULL if the argument is not well-formed JSON, rather than raising an error. FossilOrigin-Name: ed9956f5ddca68141eded81d5362847db603257329801622c4eb3b0732112f14 --- ext/misc/json1.c | 10 +++++++--- manifest | 14 +++++++------- manifest.uuid | 2 +- test/json102.test | 9 +++++++++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 6f66d4e70c..926f0a8b68 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -1946,10 +1946,13 @@ jsonSetDone: /* ** json_type(JSON) +** json_ntype(JSON) ** json_type(JSON, PATH) ** -** Return the top-level "type" of a JSON string. Throw an error if -** either the JSON or PATH inputs are not well-formed. +** Return the top-level "type" of a JSON string. json_type() raises an +** error if either the JSON or PATH inputs are not well-formed. json_ntype() +** works like the one-argument version of json_type() except that it +** returns NULL if the JSON argument is not well-formed. */ static void jsonTypeFunc( sqlite3_context *ctx, @@ -1960,7 +1963,7 @@ static void jsonTypeFunc( const char *zPath; JsonNode *pNode; - p = jsonParseCached(ctx, argv, ctx); + p = jsonParseCached(ctx, argv, *(int*)sqlite3_user_data(ctx) ? 0 : ctx); if( p==0 ) return; if( argc==2 ){ zPath = (const char*)sqlite3_value_text(argv[1]); @@ -2680,6 +2683,7 @@ int sqlite3Json1Init(sqlite3 *db){ { "->", 2, 1, jsonExtractFunc }, { "->>", 2, 1, jsonExtractFunc }, { "json_insert", -1, 0, jsonSetFunc }, + { "json_ntype", 1, 1, jsonTypeFunc }, { "json_object", -1, 0, jsonObjectFunc }, { "json_patch", 2, 0, jsonPatchFunc }, { "json_quote", 1, 0, jsonQuoteFunc }, diff --git a/manifest b/manifest index 3fa4543e06..7dff65acd8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Accept\sabbreviated\sJSON\sPaths\son\sthe\sright-hand\sside\sof\sthe\s->\sand\s->>\soperators. -D 2022-01-07T15:47:12.461 +C Add\sthe\sjson_ntype()\sSQL\sfunction.\s\sWorks\slike\sthe\s1-argument\sjson_type()\nexcept\sthat\sit\sreturns\sNULL\sif\sthe\sargument\sis\snot\swell-formed\sJSON,\srather\nthan\sraising\san\serror. +D 2022-01-07T16:03:00.989 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -306,7 +306,7 @@ F ext/misc/fileio.c 4e7f7cd30de8df4820c552f14af3c9ca451c5ffe1f2e7bef34d598a12ebf F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511 -F ext/misc/json1.c 81a11151fbeefd8870c31a9e77576daeacc9ef1c66153950ea2eb084193235c6 +F ext/misc/json1.c 5416dd330d6d4cfade184dd3d123ab720ca99ac72b21bd419b76cdbe69ac68d9 F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -1147,7 +1147,7 @@ F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json101.test bb71538005f2d9e18620bdd3b76839a93ca0be61903eb8d751a64e78cf99b8fb -F test/json102.test 8ee925a282f03f47526ca4e260b8e4af9f4e5b562f38647aa4f32dc84c19a0a3 +F test/json102.test c0c45152f516036dd444b8650dd55a06c455792e35ab1c3180537b77d00f7f10 F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144 F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173 @@ -1937,8 +1937,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 c4e4e3a3fc5da0381ccb7930706e57d7831d87f9c63bafe49ae64117701e1cfe -R dd2f603ec9049bd111aea3757f3d8f8e +P d15410900dccbb7159683c29f640fa321b1e019718827334f5fefe89da623008 +R 33189226ed8931e2d3369f37d0903d14 U drh -Z 9891da1f488bb126e807d6977835eae4 +Z aa887589cd33ea857ca5f9e682543a02 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6b7aeda36e..8efe23711e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d15410900dccbb7159683c29f640fa321b1e019718827334f5fefe89da623008 \ No newline at end of file +ed9956f5ddca68141eded81d5362847db603257329801622c4eb3b0732112f14 \ No newline at end of file diff --git a/test/json102.test b/test/json102.test index 6afe2307b3..ce1da6993a 100644 --- a/test/json102.test +++ b/test/json102.test @@ -176,6 +176,9 @@ do_execsql_test json102-500 { do_execsql_test json102-510 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}'); } {{object}} +do_execsql_test json102-511 { + SELECT json_ntype('{"a":[2,3.5,true,false,null,"x"]}'); +} {{object}} do_execsql_test json102-520 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$'); } {{object}} @@ -209,6 +212,12 @@ do_execsql_test json102-610 { do_execsql_test json102-620 { SELECT json_valid(char(123)||'"x":35'); } {{0}} +do_catchsql_test json102-630 { + SELECT json_type('["a",'); +} {1 {malformed JSON}} +do_catchsql_test json102-631 { + SELECT json_ntype('["a",'); +} {0 {{}}} ifcapable vtab { do_execsql_test json102-1000 { From 338b1fde623427ee8bbdc7921cff8d8714518fda Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 7 Jan 2022 17:08:48 +0000 Subject: [PATCH 5/9] New json_nextract() function that works like json_extract() except that it returns NULL instead of raising an error if the first argument is not well-formed JSON. Or if the first argument is not well-formed JSON and the second argument is '$', then return the first argument quoted. The "->" and "->>" operators are converted to use json_nextract(). FossilOrigin-Name: dc00f5286d26524b149de071490320afaa203fec5777b3eb813f07963614861a --- ext/misc/json1.c | 25 ++++++++++++++++++------- manifest | 14 +++++++------- manifest.uuid | 2 +- test/json102.test | 19 +++++++++++++++++++ 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 926f0a8b68..2cb9d700b0 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -1597,17 +1597,27 @@ static void jsonExtractFunc( JsonParse *p; /* The parse */ JsonNode *pNode; const char *zPath; + int flags = *(int*)sqlite3_user_data(ctx); JsonString jx; if( argc<2 ) return; - p = jsonParseCached(ctx, argv, ctx); - if( p==0 ) return; + p = jsonParseCached(ctx, argv, (flags & 1)!=0 ? 0 : ctx); + if( p==0 ){ + /* If the form is "json_nextract(IN,'$')" and IN is not well-formed JSON, + ** then return IN as a quoted JSON string. */ + if( (flags & 1)!=0 + && argc==2 + && (zPath = (const char*)sqlite3_value_text(argv[1]))!=0 + && zPath[0]=='$' && zPath[1]==0 + ){ + jsonQuoteFunc(ctx, argc, argv); + } + return; + } if( argc==2 ){ /* With a single PATH argument, the return is the unquoted SQL value */ zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath && zPath[0]!='$' && zPath[0]!=0 - && *(int*)sqlite3_user_data(ctx) - ){ + if( zPath && zPath[0]!='$' && zPath[0]!=0 && (flags & 2)!=0 ){ /* The -> and ->> operators accept abbreviated PATH arguments: ** NUMBER ==> $[NUMBER] ** LABEL ==> $.LABEL @@ -2680,8 +2690,9 @@ int sqlite3Json1Init(sqlite3 *db){ { "json_array_length", 1, 0, jsonArrayLengthFunc }, { "json_array_length", 2, 0, jsonArrayLengthFunc }, { "json_extract", -1, 0, jsonExtractFunc }, - { "->", 2, 1, jsonExtractFunc }, - { "->>", 2, 1, jsonExtractFunc }, + { "json_nextract", -1, 1, jsonExtractFunc }, + { "->", 2, 3, jsonExtractFunc }, + { "->>", 2, 3, jsonExtractFunc }, { "json_insert", -1, 0, jsonSetFunc }, { "json_ntype", 1, 1, jsonTypeFunc }, { "json_object", -1, 0, jsonObjectFunc }, diff --git a/manifest b/manifest index 7dff65acd8..a1aa869c97 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sjson_ntype()\sSQL\sfunction.\s\sWorks\slike\sthe\s1-argument\sjson_type()\nexcept\sthat\sit\sreturns\sNULL\sif\sthe\sargument\sis\snot\swell-formed\sJSON,\srather\nthan\sraising\san\serror. -D 2022-01-07T16:03:00.989 +C New\sjson_nextract()\sfunction\sthat\sworks\slike\sjson_extract()\sexcept\sthat\sit\nreturns\sNULL\sinstead\sof\sraising\san\serror\sif\sthe\sfirst\sargument\sis\snot\nwell-formed\sJSON.\s\sOr\sif\sthe\sfirst\sargument\sis\snot\swell-formed\sJSON\sand\nthe\ssecond\sargument\sis\s'$',\sthen\sreturn\sthe\sfirst\sargument\squoted.\s\sThe\n"->"\sand\s"->>"\soperators\sare\sconverted\sto\suse\sjson_nextract(). +D 2022-01-07T17:08:48.125 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -306,7 +306,7 @@ F ext/misc/fileio.c 4e7f7cd30de8df4820c552f14af3c9ca451c5ffe1f2e7bef34d598a12ebf F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511 -F ext/misc/json1.c 5416dd330d6d4cfade184dd3d123ab720ca99ac72b21bd419b76cdbe69ac68d9 +F ext/misc/json1.c f2dc0952d66ebb6c9913e41170f574a4fb4a46d80d4c30ec2b7bc7635d565aae F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -1147,7 +1147,7 @@ F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json101.test bb71538005f2d9e18620bdd3b76839a93ca0be61903eb8d751a64e78cf99b8fb -F test/json102.test c0c45152f516036dd444b8650dd55a06c455792e35ab1c3180537b77d00f7f10 +F test/json102.test 40a408ffbb9294bffc9a62ea32f3ecdc2b9b40df16c69b73dc18f1684540512c F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144 F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173 @@ -1937,8 +1937,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 d15410900dccbb7159683c29f640fa321b1e019718827334f5fefe89da623008 -R 33189226ed8931e2d3369f37d0903d14 +P ed9956f5ddca68141eded81d5362847db603257329801622c4eb3b0732112f14 +R f26fe71ed976f3035850cd240318b4a2 U drh -Z aa887589cd33ea857ca5f9e682543a02 +Z 13ae4c74ef71f6a4a05f3faff51e35b4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8efe23711e..d63d456dde 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ed9956f5ddca68141eded81d5362847db603257329801622c4eb3b0732112f14 \ No newline at end of file +dc00f5286d26524b149de071490320afaa203fec5777b3eb813f07963614861a \ No newline at end of file diff --git a/test/json102.test b/test/json102.test index ce1da6993a..fbd9711c5d 100644 --- a/test/json102.test +++ b/test/json102.test @@ -72,6 +72,9 @@ do_execsql_test json102-250 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$'); } {{{"a":2,"c":[4,5,{"f":7}]}}} do_execsql_test json102-251 { + SELECT json_nextract('{"a":2,"c":[4,5,{"f":7}]}', '$'); +} {{{"a":2,"c":[4,5,{"f":7}]}}} +do_execsql_test json102-252 { SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> '$'; } {{{"a":2,"c":[4,5,{"f":7}]}}} do_execsql_test json102-260 { @@ -83,6 +86,22 @@ do_execsql_test json102-261 { do_execsql_test json102-262 { SELECT '{"a":2,"c":[4,5,{"f":7}]}' -> 'c'; } {{[4,5,{"f":7}]}} +do_catchsql_test json102-265 { + SELECT json_extract('[1,2,3', '$[2]'); +} {1 {malformed JSON}} +do_catchsql_test json102-266 { + SELECT json_nextract('[1,2,3', '$[2]'); +} {0 {{}}} +do_catchsql_test json102-267 { + SELECT json_extract('[1,2,3', '$'); +} {1 {malformed JSON}} +do_catchsql_test json102-268 { + SELECT json_nextract('[1,2,3', '$'); +} {0 {{"[1,2,3"}}} +do_catchsql_test json102-269 { + SELECT '[1,2,3' ->> '$'; +} {0 {{"[1,2,3"}}} + do_execsql_test json102-270 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2]'); } {{{"f":7}}} From 9956c18a814de7928821fb446249c692c9e64df4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 7 Jan 2022 17:14:35 +0000 Subject: [PATCH 6/9] Change the -> operator to use json_extract(). The ->> operator continues to use json_nextract(). FossilOrigin-Name: b4c8a62381755b9f1447e10ab95df7209eebda91f9a4583ef1c093a13f6b4725 --- ext/misc/json1.c | 2 +- manifest | 14 +++++++------- manifest.uuid | 2 +- test/json102.test | 5 ++++- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 2cb9d700b0..e35d4ed576 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -2691,7 +2691,7 @@ int sqlite3Json1Init(sqlite3 *db){ { "json_array_length", 2, 0, jsonArrayLengthFunc }, { "json_extract", -1, 0, jsonExtractFunc }, { "json_nextract", -1, 1, jsonExtractFunc }, - { "->", 2, 3, jsonExtractFunc }, + { "->", 2, 2, jsonExtractFunc }, { "->>", 2, 3, jsonExtractFunc }, { "json_insert", -1, 0, jsonSetFunc }, { "json_ntype", 1, 1, jsonTypeFunc }, diff --git a/manifest b/manifest index a1aa869c97..647b8c22a9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\sjson_nextract()\sfunction\sthat\sworks\slike\sjson_extract()\sexcept\sthat\sit\nreturns\sNULL\sinstead\sof\sraising\san\serror\sif\sthe\sfirst\sargument\sis\snot\nwell-formed\sJSON.\s\sOr\sif\sthe\sfirst\sargument\sis\snot\swell-formed\sJSON\sand\nthe\ssecond\sargument\sis\s'$',\sthen\sreturn\sthe\sfirst\sargument\squoted.\s\sThe\n"->"\sand\s"->>"\soperators\sare\sconverted\sto\suse\sjson_nextract(). -D 2022-01-07T17:08:48.125 +C Change\sthe\s->\soperator\sto\suse\sjson_extract().\s\sThe\s->>\soperator\scontinues\nto\suse\sjson_nextract(). +D 2022-01-07T17:14:35.839 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -306,7 +306,7 @@ F ext/misc/fileio.c 4e7f7cd30de8df4820c552f14af3c9ca451c5ffe1f2e7bef34d598a12ebf F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511 -F ext/misc/json1.c f2dc0952d66ebb6c9913e41170f574a4fb4a46d80d4c30ec2b7bc7635d565aae +F ext/misc/json1.c bec23aa378f56320246ce0bed2d841bef9d73a467f0f7a188eed1f753c61d113 F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -1147,7 +1147,7 @@ F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json101.test bb71538005f2d9e18620bdd3b76839a93ca0be61903eb8d751a64e78cf99b8fb -F test/json102.test 40a408ffbb9294bffc9a62ea32f3ecdc2b9b40df16c69b73dc18f1684540512c +F test/json102.test 94a91c6ba5df84397d887af8e7278d3e8344486e9fe6fba2916473d2b89819e1 F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144 F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173 @@ -1937,8 +1937,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 ed9956f5ddca68141eded81d5362847db603257329801622c4eb3b0732112f14 -R f26fe71ed976f3035850cd240318b4a2 +P dc00f5286d26524b149de071490320afaa203fec5777b3eb813f07963614861a +R c2278f0d888348e91e31b99a70b490f1 U drh -Z 13ae4c74ef71f6a4a05f3faff51e35b4 +Z e2f5d3c5e50a6c66708d727e107dbfb6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d63d456dde..cb3b4bb5de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dc00f5286d26524b149de071490320afaa203fec5777b3eb813f07963614861a \ No newline at end of file +b4c8a62381755b9f1447e10ab95df7209eebda91f9a4583ef1c093a13f6b4725 \ No newline at end of file diff --git a/test/json102.test b/test/json102.test index fbd9711c5d..521373a833 100644 --- a/test/json102.test +++ b/test/json102.test @@ -98,7 +98,10 @@ do_catchsql_test json102-267 { do_catchsql_test json102-268 { SELECT json_nextract('[1,2,3', '$'); } {0 {{"[1,2,3"}}} -do_catchsql_test json102-269 { +do_catchsql_test json102-269a { + SELECT '[1,2,3' -> '$'; +} {1 {malformed JSON}} +do_catchsql_test json102-269b { SELECT '[1,2,3' ->> '$'; } {0 {{"[1,2,3"}}} From a3f51d7b3e7cd531e33779bcb7fec8f259c99cc6 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 7 Jan 2022 17:26:40 +0000 Subject: [PATCH 7/9] Reverse the meaningn of -> and ->>. ->> raises an error on invalid JSON but -> does not. This allows ->> to behave the same as PG and MySQL. FossilOrigin-Name: 85f8170555ee0d4d28cb7e120a7062e9f64c331a936fdfa29fc0e67224eea7c6 --- ext/misc/json1.c | 4 ++-- manifest | 14 +++++++------- manifest.uuid | 2 +- test/json102.test | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ext/misc/json1.c b/ext/misc/json1.c index e35d4ed576..86a486c21c 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -2691,8 +2691,8 @@ int sqlite3Json1Init(sqlite3 *db){ { "json_array_length", 2, 0, jsonArrayLengthFunc }, { "json_extract", -1, 0, jsonExtractFunc }, { "json_nextract", -1, 1, jsonExtractFunc }, - { "->", 2, 2, jsonExtractFunc }, - { "->>", 2, 3, jsonExtractFunc }, + { "->", 2, 3, jsonExtractFunc }, + { "->>", 2, 2, jsonExtractFunc }, { "json_insert", -1, 0, jsonSetFunc }, { "json_ntype", 1, 1, jsonTypeFunc }, { "json_object", -1, 0, jsonObjectFunc }, diff --git a/manifest b/manifest index 647b8c22a9..3227004e9a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\s->\soperator\sto\suse\sjson_extract().\s\sThe\s->>\soperator\scontinues\nto\suse\sjson_nextract(). -D 2022-01-07T17:14:35.839 +C Reverse\sthe\smeaningn\sof\s->\sand\s->>.\s\s->>\sraises\san\serror\son\sinvalid\sJSON\nbut\s->\sdoes\snot.\s\sThis\sallows\s->>\sto\sbehave\sthe\ssame\sas\sPG\sand\sMySQL. +D 2022-01-07T17:26:40.622 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -306,7 +306,7 @@ F ext/misc/fileio.c 4e7f7cd30de8df4820c552f14af3c9ca451c5ffe1f2e7bef34d598a12ebf F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511 -F ext/misc/json1.c bec23aa378f56320246ce0bed2d841bef9d73a467f0f7a188eed1f753c61d113 +F ext/misc/json1.c bfee5581c6f8443d2b4d73939c1aaad887111b7bb5f3a8c5dbd1572f627de83c F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -1147,7 +1147,7 @@ F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json101.test bb71538005f2d9e18620bdd3b76839a93ca0be61903eb8d751a64e78cf99b8fb -F test/json102.test 94a91c6ba5df84397d887af8e7278d3e8344486e9fe6fba2916473d2b89819e1 +F test/json102.test a44a20b7dfa446e67f47d0c02927fe93f80d6ebf3080a5d827757e8d4921c081 F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144 F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173 @@ -1937,8 +1937,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 dc00f5286d26524b149de071490320afaa203fec5777b3eb813f07963614861a -R c2278f0d888348e91e31b99a70b490f1 +P b4c8a62381755b9f1447e10ab95df7209eebda91f9a4583ef1c093a13f6b4725 +R 5e00922e334b2eca45d33167fc947357 U drh -Z e2f5d3c5e50a6c66708d727e107dbfb6 +Z ca8d93554520c6d97db646c26d2dff94 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cb3b4bb5de..89e07a6e91 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b4c8a62381755b9f1447e10ab95df7209eebda91f9a4583ef1c093a13f6b4725 \ No newline at end of file +85f8170555ee0d4d28cb7e120a7062e9f64c331a936fdfa29fc0e67224eea7c6 \ No newline at end of file diff --git a/test/json102.test b/test/json102.test index 521373a833..7ae99353bf 100644 --- a/test/json102.test +++ b/test/json102.test @@ -99,10 +99,10 @@ do_catchsql_test json102-268 { SELECT json_nextract('[1,2,3', '$'); } {0 {{"[1,2,3"}}} do_catchsql_test json102-269a { - SELECT '[1,2,3' -> '$'; + SELECT '[1,2,3' ->> '$'; } {1 {malformed JSON}} do_catchsql_test json102-269b { - SELECT '[1,2,3' ->> '$'; + SELECT '[1,2,3' -> '$'; } {0 {{"[1,2,3"}}} do_execsql_test json102-270 { From 4f3557e4c95b78bc15229afc06799b2bf1ef99dd Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 7 Jan 2022 18:09:56 +0000 Subject: [PATCH 8/9] Notes on the JSON enhancement proposals. FossilOrigin-Name: 18160985ea6b2bbf27de25e0f4f3a1ebcdb079a36af039fc06e37a834e49e772 --- doc/json-enhancements.md | 123 +++++++++++++++++++++++++++++++++++++++ manifest | 11 ++-- manifest.uuid | 2 +- 3 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 doc/json-enhancements.md diff --git a/doc/json-enhancements.md b/doc/json-enhancements.md new file mode 100644 index 0000000000..4c226a2c96 --- /dev/null +++ b/doc/json-enhancements.md @@ -0,0 +1,123 @@ +# Proposed Enhancements To JSON Functions + +## 1.0 New function json_nextract() + +The new function json_nextract() works the same as json_extract() except +when the input JSON is not well-formed. This is what the routines do +when the input JSON in the first argument is not well-formed: + + 1. **json_extract()** → raises an error and aborts the query. + + 2. **json_nextract()** with 2 arguments the second argument is + exactly `'$'` → work like json_quote() and return the first + argument as a JSON quoted string. + + 3. **json_nextract()** otherwise → return NULL. + +If the input is known to be JSON, then json_extract() should work just +fine for all your needs. But sometimes a table might have a column that +sometimes holds JSON and sometimes holds some other content. Suppose, +for example, an application started out holding a single phone number for +each user, but later was enhanced so that the same database file could +hold a JSON array of phone numbers. The USER table might have some entries +that are JSON arrays and some entries which are just text strings containing +phone numbers. The application can use json_nextract() to be robust in +extracting values from that column. + +The feature (2) above is envisioned to be useful for sanitizing table +content. Suppose a table is populated from dirty CSV, and some of the +JSON is mis-formatted. You could convert all entries in a table to use +well-formed JSON using something like this: + +> ~~~ +UPDATE data SET jsonData = json_nextract(jsonData,'$'); +~~~ + +In the query above, well-formed JSON would be unchanged, and mis-formatted +JSON would be converted into a well-formatted JSON string. + +## 2.0 Add the `->` and '->>` operators as aliases for json_extract(). + +Two new binary operators "`->`" and "`->>`" operators are the same +as json_nextract() and json_extract(), respectively. + +> ~~~ +SELECT '{"a":5,"b":17}' -> '$.a', '[4,1,-6]' ->> '$[0]'; +~~~ + +Is equivalent to (and generates the same bytecode as): + +> ~~~ +SELECT json_nextract('{"a":5,"b":17}','$.a'), json_extract('[4,1,-6]','$[0]'); +~~~ + +The ->> operator works the same as the ->> operator in MySQL +and mostly compatible with PostgreSQL (hereafter "PG"). Addition enhancements +in section 3.0 below are required to bring ->> into compatibility with PG. + +The -> operator is mostly compatible with MySQL and PG too. The main +difference is that in MySQL and PG, the result from -> is not a primitive +SQL datatype type but rather more JSON. It is unclear how this would ever +be useful for anything, and so I am unsure why they do this. But that is +the way it is done in those system. + +SQLite strives to be compatible with MySQL and PG with the ->> operator, +but not with the -> operator. + +## 3.0 Abbreviated JSON path specifications for use with -> and ->> + +The "->" and "->>" and operators allow abbreviated +forms of JSON path specs that omit unnecessary $-prefix text. For +example, the following queries are equivalent: + +> ~~~ +SELECT '{"a":17, "b":4.5}' ->> '$.a'; +SELECT '{"a":17, "b":4.5}' ->> 'a'; +~~~ + +Similarly, these queries mean exactly the same thing: + +> ~~~ +SELECT '[17,4.5,"hello",0]' ->> '$[1]'; +SELECT '[17,4.5,"hello",0]' ->> 1; +~~~ + +The abbreviated JSON path specs are allowed with the -> and ->> operators +only. The json_extract() and json_nextract() functions, and all the other +JSON functions, still use the full path spec and will raise an error if +the full path spec is not provided. + +This enhancement provides compatibility with PG. +PG does not support JSON path specs on its ->> operator. With PG, the +right-hand side of ->> must be either an integer (if the left-hand side +is a JSON array) or a text string which is interpreted as a field name +(if the left-hand side is a JSON object). So the ->> operator in PG is +rather limited. With this enhancement, the ->> operator in SQLite +covers all the functionality of PG, plus a lot more. + +MySQL also supports the ->> operator, but it requires a full JSON path +spec on the right-hand side. SQLite also supports this, so SQLite is +compatibility with MySQL as well. Note, however, that MySQL and PG +are incompatible with each other. You can (in theory) write SQL that +uses the ->> operator that is compatible between SQLite and MySQL, +or that is compatible between SQLite and PG, but not that is compatible +with all three. + +## 4.0 New json_ntype() SQL function + +A new function "json_ntype(JSON)" works like the existing one-argument +version of the "json_type(JSON)" function, except that json_ntype(JSON) +returns NULL if the argument is not well-formed JSON, whereas the +existing json_type() function raises an error in that case. + +In other words, "`json_ntype($json)`" is equivalent to +"`CASE WHEN json_valid($json) THEN json_type($json) END`". + +This function is seen as useful for figuring out which rows of a table +have a JSON type in a column and which do not. For example, to find +all rows in a table in which the value of the the "phonenumber" column +contains a JSON array, you could write: + +> ~~~ +SELECT * FROM users WHERE json_ntype(phonenumber) IS 'array'; +~~~ diff --git a/manifest b/manifest index 3227004e9a..0c197632b6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Reverse\sthe\smeaningn\sof\s->\sand\s->>.\s\s->>\sraises\san\serror\son\sinvalid\sJSON\nbut\s->\sdoes\snot.\s\sThis\sallows\s->>\sto\sbehave\sthe\ssame\sas\sPG\sand\sMySQL. -D 2022-01-07T17:26:40.622 +C Notes\son\sthe\sJSON\senhancement\sproposals. +D 2022-01-07T18:09:56.414 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -38,6 +38,7 @@ F configure 56f2a6637cdba53788673ccc229c4f95ab3ab6fe67036e0b1291dc615e531a58 x F configure.ac c8ba54bac7e73e000acdfef5e394fe21a3876aa09d0f5c07131bf5ac5a525299 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd +F doc/json-enhancements.md b026346e18a18a90d84dbda457e3127282468ad26baaefc005a1656429fa4232 F doc/lemon.html efc0cd2345d66905505d98f862e1c571512def0ceb5b016cb658fd4918eb76a3 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a @@ -1937,8 +1938,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 b4c8a62381755b9f1447e10ab95df7209eebda91f9a4583ef1c093a13f6b4725 -R 5e00922e334b2eca45d33167fc947357 +P 85f8170555ee0d4d28cb7e120a7062e9f64c331a936fdfa29fc0e67224eea7c6 +R f0bbc4fbd1752207d287e4842cece932 U drh -Z ca8d93554520c6d97db646c26d2dff94 +Z d04dbd17b8376af71e0674b428869308 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 89e07a6e91..3de7da8f44 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -85f8170555ee0d4d28cb7e120a7062e9f64c331a936fdfa29fc0e67224eea7c6 \ No newline at end of file +18160985ea6b2bbf27de25e0f4f3a1ebcdb079a36af039fc06e37a834e49e772 \ No newline at end of file From dc60c68cc02aa94d1fbfeeff999d42e1b5f99a4b Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 8 Jan 2022 15:05:53 +0000 Subject: [PATCH 9/9] Improved commenting of changes in the json1.c extension. FossilOrigin-Name: 4d81425e1bf2cff6fa961d0a7936b5f62d3f8ffe9bffea89c1e8b8ddf8fad6f4 --- ext/misc/json1.c | 102 +++++++++++++++++++++++++++++++---------------- manifest | 12 +++--- manifest.uuid | 2 +- 3 files changed, 75 insertions(+), 41 deletions(-) diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 86a486c21c..26f08e9168 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -1581,13 +1581,44 @@ static void jsonArrayLengthFunc( sqlite3_result_int64(ctx, n); } +/* +** Bit values for the flags passed into jsonExtractFunc() or +** jsonSetFunc() via the user-data value. +*/ +#define JSON_NULLERR 0x01 /* Return NULL if input is not JSON */ +#define JSON_ABPATH 0x02 /* Allow abbreviated JSON path specs */ +#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ + /* ** json_extract(JSON, PATH, ...) +** json_nextract(JSON, PATH, ...) +** "->"(JSON,PATH) +** "->>"(JSON,PATH) ** -** Return the element described by PATH. Return NULL if there is no -** PATH element. If there are multiple PATHs, then return a JSON array -** with the result from each path. Throw an error if the JSON or any PATH -** is malformed. +** Return the element described by PATH. Return NULL if that PATH element +** is not found. For leaf nodes of the JSON, the value returned is a pure +** SQL value. In other words, quotes have been removed from strings. +** +** If there are multiple PATHs, then the value returned is a JSON array +** with one entry in the array for each PATH term. +** +** Throw an error if any PATH is malformed. +** +** If JSON is not well-formed JSON then: +** +** (1) raise an error if the JSON_NULLERR flag is not set. +** +** (2) Otherwise (if the JSON_NULLERR flags is set and) if there +** is a single PATH argument with the value '$', simply quote +** the JSON input as if by json_quote(). In other words, treat +** the JSON input as a string and convert it into a valid JSON +** string. +** +** (3) Otherwise (if JSON_NULLERR is set and the PATH is not '$') +** return NULL +** +** If the JSON_ABPATH flag is set and there is only a single PATH, then +** allow abbreviated PATH specs that omit the leading "$". */ static void jsonExtractFunc( sqlite3_context *ctx, @@ -1601,11 +1632,11 @@ static void jsonExtractFunc( JsonString jx; if( argc<2 ) return; - p = jsonParseCached(ctx, argv, (flags & 1)!=0 ? 0 : ctx); + p = jsonParseCached(ctx, argv, (flags & JSON_NULLERR)!=0 ? 0 : ctx); if( p==0 ){ /* If the form is "json_nextract(IN,'$')" and IN is not well-formed JSON, ** then return IN as a quoted JSON string. */ - if( (flags & 1)!=0 + if( (flags & JSON_NULLERR)!=0 && argc==2 && (zPath = (const char*)sqlite3_value_text(argv[1]))!=0 && zPath[0]=='$' && zPath[1]==0 @@ -1617,11 +1648,13 @@ static void jsonExtractFunc( if( argc==2 ){ /* With a single PATH argument, the return is the unquoted SQL value */ zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath && zPath[0]!='$' && zPath[0]!=0 && (flags & 2)!=0 ){ - /* The -> and ->> operators accept abbreviated PATH arguments: - ** NUMBER ==> $[NUMBER] - ** LABEL ==> $.LABEL - ** [NUMBER] ==> $[NUMBER] + if( zPath && zPath[0]!='$' && zPath[0]!=0 && (flags & JSON_ABPATH)!=0 ){ + /* The -> and ->> operators accept abbreviated PATH arguments. This + ** is mostly for compatibility with PostgreSQL, but also for convenience. + ** + ** NUMBER ==> $[NUMBER] // PG compatible + ** LABEL ==> $.LABEL // PG compatible + ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience */ jsonInit(&jx, ctx); if( safe_isdigit(zPath[0]) ){ @@ -1633,7 +1666,7 @@ static void jsonExtractFunc( jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); jsonAppendChar(&jx, 0); } - pNode = jsonLookup(p, jx.zBuf, 0, ctx); + pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); jsonReset(&jx); }else{ pNode = jsonLookup(p, zPath, 0, ctx); @@ -1896,6 +1929,7 @@ replace_err: jsonParseReset(&x); } + /* ** json_set(JSON, PATH, VALUE, ...) ** @@ -2681,34 +2715,34 @@ int sqlite3Json1Init(sqlite3 *db){ unsigned int i; static const struct { const char *zName; + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); int nArg; int flag; - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); } aFunc[] = { - { "json", 1, 0, jsonRemoveFunc }, - { "json_array", -1, 0, jsonArrayFunc }, - { "json_array_length", 1, 0, jsonArrayLengthFunc }, - { "json_array_length", 2, 0, jsonArrayLengthFunc }, - { "json_extract", -1, 0, jsonExtractFunc }, - { "json_nextract", -1, 1, jsonExtractFunc }, - { "->", 2, 3, jsonExtractFunc }, - { "->>", 2, 2, jsonExtractFunc }, - { "json_insert", -1, 0, jsonSetFunc }, - { "json_ntype", 1, 1, jsonTypeFunc }, - { "json_object", -1, 0, jsonObjectFunc }, - { "json_patch", 2, 0, jsonPatchFunc }, - { "json_quote", 1, 0, jsonQuoteFunc }, - { "json_remove", -1, 0, jsonRemoveFunc }, - { "json_replace", -1, 0, jsonReplaceFunc }, - { "json_set", -1, 1, jsonSetFunc }, - { "json_type", 1, 0, jsonTypeFunc }, - { "json_type", 2, 0, jsonTypeFunc }, - { "json_valid", 1, 0, jsonValidFunc }, + { "json", jsonRemoveFunc, 1, 0 }, + { "json_array", jsonArrayFunc, -1, 0 }, + { "json_array_length", jsonArrayLengthFunc, 1, 0 }, + { "json_array_length", jsonArrayLengthFunc, 2, 0 }, + { "json_extract", jsonExtractFunc, -1, 0 }, + { "json_nextract", jsonExtractFunc, -1, JSON_NULLERR }, + { "->", jsonExtractFunc, 2, JSON_NULLERR|JSON_ABPATH }, + { "->>", jsonExtractFunc, 2, JSON_ABPATH }, + { "json_insert", jsonSetFunc, -1, 0 }, + { "json_object", jsonObjectFunc, -1, 0 }, + { "json_patch", jsonPatchFunc, 2, 0 }, + { "json_quote", jsonQuoteFunc, 1, 0 }, + { "json_remove", jsonRemoveFunc, -1, 0 }, + { "json_replace", jsonReplaceFunc, -1, 0 }, + { "json_set", jsonSetFunc, -1, JSON_ISSET }, + { "json_type", jsonTypeFunc, 1, 0 }, + { "json_ntype", jsonTypeFunc, 1, JSON_NULLERR }, + { "json_type", jsonTypeFunc, 2, 0 }, + { "json_valid", jsonValidFunc, 1, 0 }, #if SQLITE_DEBUG /* DEBUG and TESTING functions */ - { "json_parse", 1, 0, jsonParseFunc }, - { "json_test1", 1, 0, jsonTest1Func }, + { "json_parse", jsonParseFunc, 1, 0 }, + { "json_test1", jsonTest1Func, 1, 0 }, #endif }; static const struct { diff --git a/manifest b/manifest index 0c197632b6..38810b62bc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Notes\son\sthe\sJSON\senhancement\sproposals. -D 2022-01-07T18:09:56.414 +C Improved\scommenting\sof\schanges\sin\sthe\sjson1.c\sextension. +D 2022-01-08T15:05:53.556 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -307,7 +307,7 @@ F ext/misc/fileio.c 4e7f7cd30de8df4820c552f14af3c9ca451c5ffe1f2e7bef34d598a12ebf F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511 -F ext/misc/json1.c bfee5581c6f8443d2b4d73939c1aaad887111b7bb5f3a8c5dbd1572f627de83c +F ext/misc/json1.c a35ebe08f25515e4ab03c4ac7e06dd8a837c461f96df040ef7566a6ee4edbb84 F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -1938,8 +1938,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 85f8170555ee0d4d28cb7e120a7062e9f64c331a936fdfa29fc0e67224eea7c6 -R f0bbc4fbd1752207d287e4842cece932 +P 18160985ea6b2bbf27de25e0f4f3a1ebcdb079a36af039fc06e37a834e49e772 +R ea25b8b6843a230f65190e794ee89370 U drh -Z d04dbd17b8376af71e0674b428869308 +Z 72ca0ba8118c37c05f41d020549c1f4e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3de7da8f44..dc1aa1ea0e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -18160985ea6b2bbf27de25e0f4f3a1ebcdb079a36af039fc06e37a834e49e772 \ No newline at end of file +4d81425e1bf2cff6fa961d0a7936b5f62d3f8ffe9bffea89c1e8b8ddf8fad6f4 \ No newline at end of file