From 9dbf96bd708311ca99f73760ab20ee7fed7581c9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 6 Jan 2022 01:40:09 +0000 Subject: [PATCH 01/18] An attempt to integrate the JSON functions directly into the SQLite core, rather than holding them as an extension. FossilOrigin-Name: 583b47d865fb8d2c9ae4d3a4e70356a8a758978efb0a282f6b19775bf41fb748 --- Makefile.in | 10 +- Makefile.msc | 10 +- configure | 42 ++--- configure.ac | 25 +-- ext/rtree/geopoly.c | 11 +- main.mk | 7 +- manifest | 37 +++-- manifest.uuid | 2 +- src/ctime.c | 8 +- src/func.c | 1 + ext/misc/json1.c => src/json.c | 293 ++++++++++----------------------- src/main.c | 7 +- src/sqliteInt.h | 10 +- tool/mksqlite3c.tcl | 2 +- 14 files changed, 175 insertions(+), 290 deletions(-) rename ext/misc/json1.c => src/json.c (90%) diff --git a/Makefile.in b/Makefile.in index b3ea4628b3..9d37f883e9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -180,7 +180,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ fts5.lo \ func.lo global.lo hash.lo \ - icu.lo insert.lo json1.lo legacy.lo loadext.lo \ + icu.lo insert.lo json.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memdb.lo memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ @@ -234,6 +234,7 @@ SRC = \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ + $(TOP)/src/json.c \ $(TOP)/src/legacy.c \ $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ @@ -366,7 +367,6 @@ SRC += \ $(TOP)/ext/rbu/sqlite3rbu.h \ $(TOP)/ext/rbu/sqlite3rbu.c SRC += \ - $(TOP)/ext/misc/json1.c \ $(TOP)/ext/misc/stmt.c # Generated source code files @@ -873,6 +873,9 @@ hash.lo: $(TOP)/src/hash.c $(HDR) insert.lo: $(TOP)/src/insert.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/insert.c +json.lo: $(TOP)/src/json.c + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/json.c + legacy.lo: $(TOP)/src/legacy.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/legacy.c @@ -1176,9 +1179,6 @@ userauth.lo: $(TOP)/ext/userauth/userauth.c $(HDR) $(EXTHDR) sqlite3session.lo: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c -json1.lo: $(TOP)/ext/misc/json1.c - $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c - stmt.lo: $(TOP)/ext/misc/stmt.c $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c diff --git a/Makefile.msc b/Makefile.msc index 3cbf560d7c..50f63619b2 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1248,7 +1248,7 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \ fts3_tokenize_vtab.lo fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ fts5.lo \ func.lo global.lo hash.lo \ - icu.lo insert.lo json1.lo legacy.lo loadext.lo \ + icu.lo insert.lo json.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memdb.lo memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ @@ -1315,6 +1315,7 @@ SRC00 = \ $(TOP)\src\global.c \ $(TOP)\src\hash.c \ $(TOP)\src\insert.c \ + $(TOP)\src\json.c \ $(TOP)\src\legacy.c \ $(TOP)\src\loadext.c \ $(TOP)\src\main.c \ @@ -1445,7 +1446,6 @@ SRC07 = \ $(TOP)\ext\rtree\rtree.c \ $(TOP)\ext\session\sqlite3session.c \ $(TOP)\ext\rbu\sqlite3rbu.c \ - $(TOP)\ext\misc\json1.c \ $(TOP)\ext\misc\stmt.c # Extension header files, part 1. @@ -1990,6 +1990,9 @@ hash.lo: $(TOP)\src\hash.c $(HDR) insert.lo: $(TOP)\src\insert.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\insert.c +json.lo: $(TOP)\src\json.c $(HDR) + $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\json.c + legacy.lo: $(TOP)\src\legacy.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\legacy.c @@ -2298,9 +2301,6 @@ fts3_unicode2.lo: $(TOP)\ext\fts3\fts3_unicode2.c $(HDR) $(EXTHDR) fts3_write.lo: $(TOP)\ext\fts3\fts3_write.c $(HDR) $(EXTHDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_write.c -json1.lo: $(TOP)\ext\misc\json1.c $(HDR) $(EXTHDR) - $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\misc\json1.c - stmt.lo: $(TOP)\ext\misc\stmt.c $(HDR) $(EXTHDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\misc\stmt.c diff --git a/configure b/configure index 74dddfe76e..abb182d5eb 100755 --- a/configure +++ b/configure @@ -905,13 +905,13 @@ enable_debug enable_amalgamation enable_load_extension enable_math +enable_json enable_all enable_memsys5 enable_memsys3 enable_fts3 enable_fts4 enable_fts5 -enable_json1 enable_update_limit enable_geopoly enable_rtree @@ -1560,13 +1560,13 @@ Optional Features: --disable-load-extension Disable loading of external extensions --disable-math Disable math functions - --enable-all Enable FTS4, FTS5, Geopoly, JSON, RTree, Sessions + --disable-json Disable JSON functions + --enable-all Enable FTS4, FTS5, Geopoly, RTree, Sessions --enable-memsys5 Enable MEMSYS5 --enable-memsys3 Enable MEMSYS3 --enable-fts3 Enable the FTS3 extension --enable-fts4 Enable the FTS4 extension --enable-fts5 Enable the FTS5 extension - --enable-json1 Enable the JSON1 extension --enable-update-limit Enable the UPDATE/DELETE LIMIT clause --enable-geopoly Enable the GEOPOLY extension --enable-rtree Enable the RTREE extension @@ -11497,6 +11497,24 @@ fi fi +########## +# Do we want to support JSON functions +# +# Check whether --enable-json was given. +if test "${enable_json+set}" = set; then : + enableval=$enable_json; +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support JSON functions" >&5 +$as_echo_n "checking whether to support JSON functions... " >&6; } +if test "$enable_json" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_OMIT_JSON" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi ######## # The --enable-all argument is short-hand to enable @@ -11701,24 +11719,6 @@ else $as_echo "no" >&6; } fi -######### -# See whether we should enable JSON1 -# Check whether --enable-json1 was given. -if test "${enable_json1+set}" = set; then : - enableval=$enable_json1; -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support JSON" >&5 -$as_echo_n "checking whether to support JSON... " >&6; } -if test "${enable_json1}" = "yes" -o "${enable_all}" = "yes" ; then - OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - ######### # See whether we should enable the LIMIT clause on UPDATE and DELETE # statements. diff --git a/configure.ac b/configure.ac index 32fe0d229a..cc805a00d5 100644 --- a/configure.ac +++ b/configure.ac @@ -605,12 +605,24 @@ else AC_SEARCH_LIBS(ceil, m) fi +########## +# Do we want to support JSON functions +# +AC_ARG_ENABLE(json, +AC_HELP_STRING([--disable-json],[Disable JSON functions])) +AC_MSG_CHECKING([whether to support JSON functions]) +if test "$enable_json" = "no"; then + AC_MSG_RESULT([no]) + OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_OMIT_JSON" +else + AC_MSG_RESULT([yes]) +fi ######## # The --enable-all argument is short-hand to enable # multiple extensions. AC_ARG_ENABLE(all, AC_HELP_STRING([--enable-all], - [Enable FTS4, FTS5, Geopoly, JSON, RTree, Sessions])) + [Enable FTS4, FTS5, Geopoly, RTree, Sessions])) ########## # Do we want to support memsys3 and/or memsys5 @@ -666,17 +678,6 @@ else AC_MSG_RESULT([no]) fi -######### -# See whether we should enable JSON1 -AC_ARG_ENABLE(json1, AC_HELP_STRING([--enable-json1],[Enable the JSON1 extension])) -AC_MSG_CHECKING([whether to support JSON]) -if test "${enable_json1}" = "yes" -o "${enable_all}" = "yes" ; then - OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1" - AC_MSG_RESULT([yes]) -else - AC_MSG_RESULT([no]) -fi - ######### # See whether we should enable the LIMIT clause on UPDATE and DELETE # statements. diff --git a/ext/rtree/geopoly.c b/ext/rtree/geopoly.c index 98ea718f5a..68cde87344 100644 --- a/ext/rtree/geopoly.c +++ b/ext/rtree/geopoly.c @@ -26,11 +26,7 @@ # define GEODEBUG(X) #endif -#ifndef JSON_NULL /* The following stuff repeats things found in json1 */ -/* -** Versions of isspace(), isalnum() and isdigit() to which it is safe -** to pass signed char values. -*/ +/* Character class routines */ #ifdef sqlite3Isdigit /* Use the SQLite core versions if this routine is part of the ** SQLite amalgamation */ @@ -45,6 +41,7 @@ # define safe_isxdigit(x) isxdigit((unsigned char)(x)) #endif +#ifndef JSON_NULL /* The following stuff repeats things found in json1 */ /* ** Growing our own isspace() routine this way is twice as fast as ** the library isspace() function. @@ -67,7 +64,7 @@ static const char geopolyIsSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -#define safe_isspace(x) (geopolyIsSpace[(unsigned char)x]) +#define fast_isspace(x) (geopolyIsSpace[(unsigned char)x]) #endif /* JSON NULL - back to original code */ /* Compiler and version */ @@ -156,7 +153,7 @@ static void geopolySwab32(unsigned char *a){ /* Skip whitespace. Return the next non-whitespace character. */ static char geopolySkipSpace(GeoParse *p){ - while( safe_isspace(p->z[0]) ) p->z++; + while( fast_isspace(p->z[0]) ) p->z++; return p->z[0]; } diff --git a/main.mk b/main.mk index a3a4f33bd3..e09505aee2 100644 --- a/main.mk +++ b/main.mk @@ -64,7 +64,7 @@ LIBOBJ+= vdbe.o parse.o \ fts3_tokenize_vtab.o \ fts3_unicode.o fts3_unicode2.o \ fts3_write.o fts5.o func.o global.o hash.o \ - icu.o insert.o json1.o legacy.o loadext.o \ + icu.o insert.o json.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memdb.o memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ @@ -111,6 +111,7 @@ SRC = \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ + $(TOP)/src/json.c \ $(TOP)/src/legacy.c \ $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ @@ -244,7 +245,6 @@ SRC += \ $(TOP)/ext/rbu/sqlite3rbu.c \ $(TOP)/ext/rbu/sqlite3rbu.h SRC += \ - $(TOP)/ext/misc/json1.c \ $(TOP)/ext/misc/stmt.c @@ -828,9 +828,6 @@ fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR) fts5.o: fts5.c sqlite3ext.h sqlite3.h $(TCCX) -DSQLITE_CORE -c fts5.c -json1.o: $(TOP)/ext/misc/json1.c sqlite3ext.h sqlite3.h - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c - stmt.o: $(TOP)/ext/misc/stmt.c sqlite3ext.h sqlite3.h $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c diff --git a/manifest b/manifest index f0032d5289..c39afa35cd 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 An\sattempt\sto\sintegrate\sthe\sJSON\sfunctions\sdirectly\sinto\sthe\sSQLite\score,\nrather\sthan\sholding\sthem\sas\san\sextension. +D 2022-01-06T01:40:09.292 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 0e91c42a1dd13a569b1fa4f4dfb7d3632f3164a1c05c71341533d67db5b641dd +F Makefile.in fd537743957bfe87997dc5727783d8eec82098921e15eab984d6711cd46f001b F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 -F Makefile.msc 88f05063ee36a5fb77d69b877ef824d0743b5325c95bfbf74d6ff17bd1c9fb1f +F Makefile.msc 8ba680ec26e6d6f348954e0fee719607688075ef6c242e50e20d28e8ded7cc08 F README.md 2dd87a5c1d108b224921f3dd47dea567973f706e1f6959386282a626f459a70c F VERSION 392c2f83569705069415a5d98b1c138ec8fe8a56a663a0d94cea019e806537b2 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -34,8 +34,8 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63 F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6 F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559 -F configure 56f2a6637cdba53788673ccc229c4f95ab3ab6fe67036e0b1291dc615e531a58 x -F configure.ac c8ba54bac7e73e000acdfef5e394fe21a3876aa09d0f5c07131bf5ac5a525299 +F configure a2877fe63cc821af0df41abe70f1f7c4e97cb7e23a42e0a1402e8a2f55a88aa2 x +F configure.ac 3ef6eeff4387585bfcab76b0c3f6e15a0618587bb90245dd5d44e4378141bb35 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd F doc/lemon.html efc0cd2345d66905505d98f862e1c571512def0ceb5b016cb658fd4918eb76a3 @@ -306,7 +306,6 @@ 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/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -394,7 +393,7 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782 F ext/repair/test/checkindex01.test b530f141413b587c9eb78ff734de6bb79bc3515c335096108c12c01bddbadcec F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/geopoly.c a7021cb524621573ccda213a35b0339371849dd4acc4909f689786ee1f964b7f +F ext/rtree/geopoly.c cc3f89c11abcf114fa60d74709ae8b5bc1eae5a261b30bc1bb7085089c03bfab F ext/rtree/rtree.c d7b4b8b81d8d54376a7f81de5be85ec58b37c11604bcf42984a8418b34158d93 F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412 F ext/rtree/rtree1.test 35c3bc0def71317b7601ee0d1149e7df2cd8fc4f13ec89a64761ac3f46ca123f @@ -472,7 +471,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 24547d9000c965553e6f5c875d91935eac250ca19ae6d9afa57fe093e3edd257 +F main.mk 32e8c752386520016933873a23a9c649f1a9cfd5c75c218614e622f0510b8f42 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -497,7 +496,7 @@ F src/btreeInt.h ee9348c4cb9077243b049edc93a82c1f32ca48baeabf2140d41362b9f9139ff F src/build.c 6e16f7b539bfc55149a039bf0cda26b089640339df6147070b072df2d1c4f771 F src/callback.c 106b585da1edd57d75fa579d823a5218e0bf37f191dbf7417eeb4a8a9a267dbc F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/ctime.c 8159d5f706551861c18ec6c8f6bdf105e15ea00367f05d9ab65d31a1077facc1 +F src/ctime.c b09ce320b78718d5e0c4a7a59b8705abe8ee4683d9fa4b33768fe347e1b2a42a F src/date.c ab8e01d928f201f5dee0bc6d54d6702fdcec96dff4d58c387447671f6a46d191 F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d @@ -505,16 +504,17 @@ F src/delete.c 19814f621cde10b1771a0dea7fe25d3d7d39975b8d4be4888537d30860e7c08c F src/expr.c 827179c78d2ca7cc318392811de8151c60eacf7ce804b13e61bb7ef38f954846 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 5b73f7a7c00f06017531a5bd258cbc2c7a294e55a7f84a729fe27aa525242560 -F src/func.c 0f576a0c102485676266e63a796223e63c3cdb04baf3678ccc8bfeedba4a6fd4 +F src/func.c 67944b79d9c06fcb86a2944082ca1dd9d46eed7bd5c6721bcb19094dd7f81358 F src/global.c 1f56aead86e8a18c4415638f5e6c4d0a0550427f4b3f5d065ba5164cc09c22e8 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c e528416ff5d86fc5d656ea6a26f03fde39836b6175f93048c32a03cb2ee16743 +F src/json.c 973b36a733cc5d3c8347bbdb3479c1124c0916ee7416341d0126895d055d2447 w ext/misc/json1.c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 95db1fe62c5973f1c5d9c53f6083e21a73ece14cdd47eeca0639691332e85c4d -F src/main.c aa24539f6c26460543d51027ea14b79cad35e34bc9d4907bc349b52b71066644 +F src/main.c 2b6b0dbfeb14d4bb57e368604b0736b2aa42b51b00339d399b01d6b1fc9b4960 F src/malloc.c ef796bcc0e81d845d59a469f1cf235056caf9024172fd524e32136e65593647b F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -555,7 +555,7 @@ F src/shell.c.in f5111900d646a07da18e6438d57be20f112397daba6bfc85b117a0da586e55d F src/sqlite.h.in a5e0d6bd47e67aabf1475986d36bdcc7bfa9e06566790ebf8e3aa7fa551c9f99 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 01eb85e4f2759a5ee79c183f4b2877889d4ffdc49d27ae74529c9579e3c8c0ef -F src/sqliteInt.h 9782b7b22a9b10e43b7f375dcdfb93cd1ed7797d831122a07d065efacf9af668 +F src/sqliteInt.h 21a31abf60222f50c1d654cdc27ad9d4040249f0341129dd8286b8b5b32bcd30 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -1875,7 +1875,7 @@ F tool/mkshellc.tcl df5d249617f9cc94d5c48eb0401673eb3f31f383ecbc54e8a13ca3dd97e8 F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f -F tool/mksqlite3c.tcl bf9b40811aba68f73f2a8848fad9b1fb09fd54ab5b77e5227f18eea87ab60d92 +F tool/mksqlite3c.tcl 6f9e05facb51e906a1a7ef9f95274ef2ec91bf88b96732a9aed40647c605f419 F tool/mksqlite3h.tcl 1f5e4a1dbbbc43c83cc6e74fe32c6c620502240b66c7c0f33a51378e78fc4edf F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5 @@ -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 7ae596dd4a73a09585c5dc9f4faf75d126d0733fc2fb32c1de64126a1088d967 -R 7175e1e9a16516f1ab12321ca929e683 +P b6a82f3c3b9d89fdf628c7f117b6a4a64383a36c84fe84d47c80e845c9bd8a4f +R 512e6db3c5e4ab4d0c8ece9f791ebefa +T *branch * json-in-core +T *sym-json-in-core * +T -sym-trunk * U drh -Z 45714cdd1039a8cc38c8df2ff917256e +Z a41c104f7d21d7e5237fb8ae1a000e0b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3671abe1cd..8d5afef0f4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b6a82f3c3b9d89fdf628c7f117b6a4a64383a36c84fe84d47c80e845c9bd8a4f \ No newline at end of file +583b47d865fb8d2c9ae4d3a4e70356a8a758978efb0a282f6b19775bf41fb748 \ No newline at end of file diff --git a/src/ctime.c b/src/ctime.c index de68ea7f57..708d6ac2bf 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -179,6 +179,11 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_DISABLE_INTRINSIC "DISABLE_INTRINSIC", #endif +#ifdef SQLITE_DISABLE_JSON + "DISABLE_JSON", +#else + "ENABLE_JSON1", /* Legacy */ +#endif #ifdef SQLITE_DISABLE_LFS "DISABLE_LFS", #endif @@ -257,9 +262,6 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_ENABLE_IOTRACE "ENABLE_IOTRACE", #endif -#ifdef SQLITE_ENABLE_JSON1 - "ENABLE_JSON1", -#endif #ifdef SQLITE_ENABLE_LOAD_EXTENSION "ENABLE_LOAD_EXTENSION", #endif diff --git a/src/func.c b/src/func.c index eeab1d2647..360967a084 100644 --- a/src/func.c +++ b/src/func.c @@ -2347,6 +2347,7 @@ void sqlite3RegisterBuiltinFunctions(void){ #endif sqlite3WindowFunctions(); sqlite3RegisterDateTimeFunctions(); + sqlite3RegisterJsonFunctions(); sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc)); #if 0 /* Enable to print out how the built-in functions are hashed */ diff --git a/ext/misc/json1.c b/src/json.c similarity index 90% rename from ext/misc/json1.c rename to src/json.c index 787b46982e..5f39902c0e 100644 --- a/ext/misc/json1.c +++ b/src/json.c @@ -10,10 +10,10 @@ ** ****************************************************************************** ** -** This SQLite extension implements JSON functions. The interface is -** modeled after MySQL JSON functions: +** This SQLite JSON functions. ** -** https://dev.mysql.com/doc/refman/5.7/en/json.html +** This file began as an extension in ext/misc/json1.c in 2015. That +** extension proved so useful that it has now been moved into the core. ** ** For the time being, all JSON is stored as pure text. (We might add ** a JSONB type in the future which stores a binary encoding of JSON in @@ -21,57 +21,8 @@ ** This implementation parses JSON text at 250 MB/s, so it is hard to see ** how JSONB might improve on that.) */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) -#if !defined(SQLITEINT_H) -#include "sqlite3ext.h" -#endif -SQLITE_EXTENSION_INIT1 - -/* If compiling this extension separately (why would anybody do that when -** it is built into the amalgamation?) we must set NDEBUG if SQLITE_DEBUG -** is not defined *before* including , in order to disable asserts(). -*/ -#if !defined(SQLITE_AMALGAMATION) && !defined(SQLITE_DEBUG) -# define NDEBUG 1 -#endif - -#include -#include -#include -#include - -/* Mark a function parameter as unused, to suppress nuisance compiler -** warnings. */ -#ifndef UNUSED_PARAM -# define UNUSED_PARAM(X) (void)(X) -#endif - -#ifndef LARGEST_INT64 -# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) -# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) -#endif - -#ifndef deliberate_fall_through -# define deliberate_fall_through -#endif - -/* -** Versions of isspace(), isalnum() and isdigit() to which it is safe -** to pass signed char values. -*/ -#ifdef sqlite3Isdigit - /* Use the SQLite core versions if this routine is part of the - ** SQLite amalgamation */ -# define safe_isdigit(x) sqlite3Isdigit(x) -# define safe_isalnum(x) sqlite3Isalnum(x) -# define safe_isxdigit(x) sqlite3Isxdigit(x) -#else - /* Use the standard library for separate compilation */ -#include /* amalgamator: keep */ -# define safe_isdigit(x) isdigit((unsigned char)(x)) -# define safe_isalnum(x) isalnum((unsigned char)(x)) -# define safe_isxdigit(x) isxdigit((unsigned char)(x)) -#endif +#ifndef SQLITE_OMIT_JSON +#include "sqliteInt.h" /* ** Growing our own isspace() routine this way is twice as fast as @@ -96,44 +47,14 @@ static const char jsonIsSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -#define safe_isspace(x) (jsonIsSpace[(unsigned char)x]) +#define fast_isspace(x) (jsonIsSpace[(unsigned char)x]) -#ifndef SQLITE_AMALGAMATION - /* Unsigned integer types. These are already defined in the sqliteInt.h, - ** but the definitions need to be repeated for separate compilation. */ - typedef sqlite3_uint64 u64; - typedef unsigned int u32; - typedef unsigned short int u16; - typedef unsigned char u8; -# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) -# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 -# endif -# if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) -# define ALWAYS(X) (1) -# define NEVER(X) (0) -# elif !defined(NDEBUG) -# define ALWAYS(X) ((X)?1:(assert(0),0)) -# define NEVER(X) ((X)?(assert(0),1):0) -# else -# define ALWAYS(X) (X) -# define NEVER(X) (X) -# endif -# define testcase(X) -#endif #if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST) # define VVA(X) #else # define VVA(X) X #endif -/* -** Some of the testcase() macros in this file are problematic for gcov -** in that they generate false-miss errors randomly. This is a gcov problem, -** not a problem in this case. But to work around it, we disable the -** problematic test cases for production builds. -*/ -#define json_testcase(X) - /* Objects */ typedef struct JsonString JsonString; typedef struct JsonNode JsonNode; @@ -591,10 +512,10 @@ static u8 jsonHexToInt(int h){ */ static u32 jsonHexToInt4(const char *z){ u32 v; - assert( safe_isxdigit(z[0]) ); - assert( safe_isxdigit(z[1]) ); - assert( safe_isxdigit(z[2]) ); - assert( safe_isxdigit(z[3]) ); + assert( sqlite3Isxdigit(z[0]) ); + assert( sqlite3Isxdigit(z[1]) ); + assert( sqlite3Isxdigit(z[2]) ); + assert( sqlite3Isxdigit(z[3]) ); v = (jsonHexToInt(z[0])<<12) + (jsonHexToInt(z[1])<<8) + (jsonHexToInt(z[2])<<4) @@ -829,7 +750,7 @@ static int jsonParseAddNode( */ static int jsonIs4Hex(const char *z){ int i; - for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0; + for(i=0; i<4; i++) if( !sqlite3Isxdigit(z[i]) ) return 0; return 1; } @@ -848,13 +769,13 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ int x; JsonNode *pNode; const char *z = pParse->zJson; - while( safe_isspace(z[i]) ){ i++; } + while( fast_isspace(z[i]) ){ i++; } if( (c = z[i])=='{' ){ /* Parse object */ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; for(j=i+1;;j++){ - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); if( x<0 ){ @@ -867,14 +788,14 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ if( pNode->eType!=JSON_STRING ) return -1; pNode->jnFlags |= JNODE_LABEL; j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( z[j]!=':' ) return -1; j++; x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ) return -1; j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!='}' ) return -1; @@ -888,7 +809,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ if( iThis<0 ) return -1; memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); pParse->iDepth--; @@ -897,7 +818,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ return -1; } j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!=']' ) return -1; @@ -934,17 +855,17 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ return j+1; }else if( c=='n' && strncmp(z+i,"null",4)==0 - && !safe_isalnum(z[i+4]) ){ + && !sqlite3Isalnum(z[i+4]) ){ jsonParseAddNode(pParse, JSON_NULL, 0, 0); return i+4; }else if( c=='t' && strncmp(z+i,"true",4)==0 - && !safe_isalnum(z[i+4]) ){ + && !sqlite3Isalnum(z[i+4]) ){ jsonParseAddNode(pParse, JSON_TRUE, 0, 0); return i+4; }else if( c=='f' && strncmp(z+i,"false",5)==0 - && !safe_isalnum(z[i+5]) ){ + && !sqlite3Isalnum(z[i+5]) ){ jsonParseAddNode(pParse, JSON_FALSE, 0, 0); return i+5; }else if( c=='-' || (c>='0' && c<='9') ){ @@ -1015,7 +936,7 @@ static int jsonParse( if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); - while( safe_isspace(zJson[i]) ) i++; + while( fast_isspace(zJson[i]) ) i++; if( zJson[i] ) i = -1; } if( i<=0 ){ @@ -1243,7 +1164,7 @@ static JsonNode *jsonLookupStep( }else if( zPath[0]=='[' ){ i = 0; j = 1; - while( safe_isdigit(zPath[j]) ){ + while( sqlite3Isdigit(zPath[j]) ){ i = i*10 + zPath[j] - '0'; j++; } @@ -1264,13 +1185,13 @@ static JsonNode *jsonLookupStep( j = 1; } j = 2; - if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){ + if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ unsigned int x = 0; j = 3; do{ x = x*10 + zPath[j] - '0'; j++; - }while( safe_isdigit(zPath[j]) ); + }while( sqlite3Isdigit(zPath[j]) ); if( x>i ) return 0; i -= x; } @@ -1489,7 +1410,7 @@ static void jsonTest1Func( int argc, sqlite3_value **argv ){ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); } #endif /* SQLITE_DEBUG */ @@ -1510,7 +1431,7 @@ static void jsonQuoteFunc( sqlite3_value **argv ){ JsonString jx; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); jsonInit(&jx, ctx); jsonAppendValue(&jx, argv[0]); @@ -1721,7 +1642,7 @@ static void jsonPatchFunc( JsonParse y; /* The patch */ JsonNode *pResult; /* The result of the merge */ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){ jsonParseReset(&x); @@ -1842,7 +1763,7 @@ static void jsonReplaceFunc( if( x.nErr ) goto replace_err; if( pNode ){ assert( pNode->eU==0 || pNode->eU==1 || pNode->eU==4 ); - json_testcase( pNode->eU!=0 && pNode->eU!=1 ); + testcase( pNode->eU!=0 && pNode->eU!=1 ); pNode->jnFlags |= (u8)JNODE_REPLACE; VVA( pNode->eU = 4 ); pNode->u.iReplace = i + 1; @@ -1880,7 +1801,7 @@ static void jsonSetFunc( const char *zPath; u32 i; int bApnd; - int bIsSet = *(int*)sqlite3_user_data(ctx); + int bIsSet = sqlite3_user_data(ctx)!=0; if( argc<1 ) return; if( (argc&1)==0 ) { @@ -1899,7 +1820,7 @@ static void jsonSetFunc( }else if( x.nErr ){ goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ - json_testcase( pNode->eU!=0 && pNode->eU!=1 && pNode->eU!=4 ); + testcase( pNode->eU!=0 && pNode->eU!=1 && pNode->eU!=4 ); assert( pNode->eU!=3 || pNode->eU!=5 ); VVA( pNode->eU = 4 ); pNode->jnFlags |= (u8)JNODE_REPLACE; @@ -1957,7 +1878,7 @@ static void jsonValidFunc( sqlite3_value **argv ){ JsonParse *p; /* The parse */ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); sqlite3_result_int(ctx, p!=0); } @@ -1977,7 +1898,7 @@ static void jsonArrayStep( sqlite3_value **argv ){ JsonString *pStr; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -2037,8 +1958,8 @@ static void jsonGroupInverse( char *z; char c; JsonString *pStr; - UNUSED_PARAM(argc); - UNUSED_PARAM(argv); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); #ifdef NEVER /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will @@ -2082,7 +2003,7 @@ static void jsonObjectStep( JsonString *pStr; const char *z; u32 n; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -2173,10 +2094,10 @@ static int jsonEachConnect( #define JEACH_JSON 8 #define JEACH_ROOT 9 - UNUSED_PARAM(pzErr); - UNUSED_PARAM(argv); - UNUSED_PARAM(argc); - UNUSED_PARAM(pAux); + UNUSED_PARAMETER(pzErr); + UNUSED_PARAMETER(argv); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(pAux); rc = sqlite3_declare_vtab(db, "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path," "json HIDDEN,root HIDDEN)"); @@ -2199,7 +2120,7 @@ static int jsonEachDisconnect(sqlite3_vtab *pVtab){ static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ JsonEachCursor *pCur; - UNUSED_PARAM(p); + UNUSED_PARAMETER(p); pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); @@ -2259,7 +2180,7 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ p->eType = pUp->eType; if( pUp->eType==JSON_ARRAY ){ assert( pUp->eU==0 || pUp->eU==3 ); - json_testcase( pUp->eU==3 ); + testcase( pUp->eU==3 ); VVA( pUp->eU = 3 ); if( iUp==p->i-1 ){ pUp->u.iKey = 0; @@ -2446,7 +2367,7 @@ static int jsonEachBestIndex( /* This implementation assumes that JSON and ROOT are the last two ** columns in the table */ assert( JEACH_ROOT == JEACH_JSON+1 ); - UNUSED_PARAM(tab); + UNUSED_PARAMETER(tab); aIdx[0] = aIdx[1] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; inConstraint; i++, pConstraint++){ @@ -2502,8 +2423,8 @@ static int jsonEachFilter( const char *zRoot = 0; sqlite3_int64 n; - UNUSED_PARAM(idxStr); - UNUSED_PARAM(argc); + UNUSED_PARAMETER(idxStr); + UNUSED_PARAMETER(argc); jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; z = (const char*)sqlite3_value_text(argv[0]); @@ -2628,57 +2549,50 @@ static sqlite3_module jsonTreeModule = { 0 /* xShadowName */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#endif /* !defined(SQLITE_OMIT_JSON) */ -/**************************************************************************** -** The following routines are the only publically visible identifiers in this -** file. Call the following routines in order to register the various SQL -** functions and the virtual table implemented by this file. -****************************************************************************/ - -int sqlite3Json1Init(sqlite3 *db){ - int rc = SQLITE_OK; - unsigned int i; - static const struct { - const char *zName; - 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_insert", -1, 0, jsonSetFunc }, - { "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 }, - +/* +** Register JSON functions. +*/ +void sqlite3RegisterJsonFunctions(void){ +#ifndef SQLITE_OMIT_JSON + static FuncDef aJsonFunc[] = { + JFUNCTION(json, 1, 0, jsonRemoveFunc), + JFUNCTION(json_array, -1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), + JFUNCTION(json_extract, -1, 0, jsonExtractFunc), + JFUNCTION(json_insert, -1, 0, jsonSetFunc), + JFUNCTION(json_object, -1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 0, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), + JFUNCTION(json_set, -1, 1, jsonSetFunc), + JFUNCTION(json_type, 1, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 0, jsonValidFunc), #if SQLITE_DEBUG - /* DEBUG and TESTING functions */ - { "json_parse", 1, 0, jsonParseFunc }, - { "json_test1", 1, 0, jsonTest1Func }, + JFUNCTION(json_parse, 1, 0, jsonParseFunc), + JFUNCTION(json_test1, 1, 0, jsonTest1Func), #endif + WAGGREGATE(json_group_array, 1, 0, 0, + jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS), + WAGGREGATE(json_group_object, 2, 0, 0, + jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS) }; - static const struct { - const char *zName; - int nArg; - void (*xStep)(sqlite3_context*,int,sqlite3_value**); - void (*xFinal)(sqlite3_context*); - void (*xValue)(sqlite3_context*); - } aAgg[] = { - { "json_group_array", 1, - jsonArrayStep, jsonArrayFinal, jsonArrayValue }, - { "json_group_object", 2, - jsonObjectStep, jsonObjectFinal, jsonObjectValue }, - }; -#ifndef SQLITE_OMIT_VIRTUALTABLE + sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); +#endif +} + +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) +/* +** Register the JSON table-valued functions +*/ +int sqlite3JsonTableFunctions(sqlite3 *db){ + int rc = SQLITE_OK; static const struct { const char *zName; sqlite3_module *pModule; @@ -2686,45 +2600,10 @@ int sqlite3Json1Init(sqlite3 *db){ { "json_each", &jsonEachModule }, { "json_tree", &jsonTreeModule }, }; -#endif - static const int enc = - SQLITE_UTF8 | - SQLITE_DETERMINISTIC | - SQLITE_INNOCUOUS; - for(i=0; i Date: Fri, 7 Jan 2022 14:58:47 +0000 Subject: [PATCH 02/18] 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 03/18] 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 04/18] 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 05/18] 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 06/18] 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 07/18] 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 08/18] 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 09/18] 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 From 9d44f18b3ccf2ca757ee27ae294b859cb32ec127 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 9 Jan 2022 16:54:02 +0000 Subject: [PATCH 10/18] Add a new built-in subtype() function. FossilOrigin-Name: a25f4ce255c034fc694c33728aedb98289ebccda9c48920829ef780b92b8faee --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/func.c | 12 ++++++++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index b3ac61a7f5..d04f8b0c46 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\sJSON\sfunction\senhancements\sfrom\sthe\sjson-enhancements\sbranch\sinto\njson-in-core. -D 2022-01-08T15:37:13.201 +C Add\sa\snew\sbuilt-in\ssubtype()\sfunction. +D 2022-01-09T16:54:02.203 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -505,7 +505,7 @@ F src/delete.c 19814f621cde10b1771a0dea7fe25d3d7d39975b8d4be4888537d30860e7c08c F src/expr.c 827179c78d2ca7cc318392811de8151c60eacf7ce804b13e61bb7ef38f954846 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 5b73f7a7c00f06017531a5bd258cbc2c7a294e55a7f84a729fe27aa525242560 -F src/func.c 67944b79d9c06fcb86a2944082ca1dd9d46eed7bd5c6721bcb19094dd7f81358 +F src/func.c 76a9ac772f67be5af9476e02ae0866f4919d1981f97ff16dedfe37a1c62cbf8e F src/global.c 1f56aead86e8a18c4415638f5e6c4d0a0550427f4b3f5d065ba5164cc09c22e8 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -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 583b47d865fb8d2c9ae4d3a4e70356a8a758978efb0a282f6b19775bf41fb748 4d81425e1bf2cff6fa961d0a7936b5f62d3f8ffe9bffea89c1e8b8ddf8fad6f4 -R 9de2cc0789b4af5e569fa6f6937afbf5 +P e116501c2f0e594eb7a3dd804daa943cc508f32ded3078aed21b695ec83bcd4c +R 28e65f1e2d788c0c1b31d4dbdc4f0a91 U drh -Z 5d4090391a2a7b92e27e5b55336ae1bf +Z fde45050a7eb088af98952ef4d939a27 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3d3e816e87..7d48848f7b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e116501c2f0e594eb7a3dd804daa943cc508f32ded3078aed21b695ec83bcd4c \ No newline at end of file +a25f4ce255c034fc694c33728aedb98289ebccda9c48920829ef780b92b8faee \ No newline at end of file diff --git a/src/func.c b/src/func.c index 360967a084..596a1bb3ab 100644 --- a/src/func.c +++ b/src/func.c @@ -97,6 +97,17 @@ static void typeofFunc( sqlite3_result_text(context, azType[i], -1, SQLITE_STATIC); } +/* subtype(X) +** +** Return the subtype of X +*/ +static void subtypeFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3_result_int(context, sqlite3_value_subtype(argv[0])); +} /* ** Implementation of the length() function @@ -2246,6 +2257,7 @@ void sqlite3RegisterBuiltinFunctions(void){ WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), + FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF), FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(printf, -1, 0, 0, printfFunc ), From d3110034dc12341a30fd1e2291088035c237182f Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 9 Jan 2022 19:44:36 +0000 Subject: [PATCH 11/18] Do not enclude ENABLE_JSON1 in the compile-time options. FossilOrigin-Name: 8bf41bc5cb19fcde569ed2c788553a848ebd9c79065bd3d2aa99e5a6bfed9696 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/ctime.c | 2 -- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index e36c760a79..48688d7bc6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\senhancements\sinto\sthe\sjson-in-core\sbranch. -D 2022-01-09T19:36:39.850 +C Do\snot\senclude\sENABLE_JSON1\sin\sthe\scompile-time\soptions. +D 2022-01-09T19:44:36.695 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -497,7 +497,7 @@ F src/btreeInt.h ee9348c4cb9077243b049edc93a82c1f32ca48baeabf2140d41362b9f9139ff F src/build.c c72407a27a28982a384cd453a3a6b6992a1ceae8bd8d95e96d7fb9c0d645a32f F src/callback.c 4c19af69835787bfe790ac560f3071a824eb629f34e41f97b52ce5235c77de1c F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/ctime.c b09ce320b78718d5e0c4a7a59b8705abe8ee4683d9fa4b33768fe347e1b2a42a +F src/ctime.c 638e75c9a02b276c782cb27f59f3abf2ab94a33514107ffd165b2f69d1bc2aaa F src/date.c ab8e01d928f201f5dee0bc6d54d6702fdcec96dff4d58c387447671f6a46d191 F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d @@ -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 a25f4ce255c034fc694c33728aedb98289ebccda9c48920829ef780b92b8faee 71272caff5874137ad0b1ddfc22ced4bb66e6c97f7868fdae0347a186f589b38 -R 49fcbfca4503349519b1d6e2c92f7acf +P ea755771699dcbffe0ddfd204d42fae9b6e79e107485c725b8eb6caab92aacb8 +R 70859f0c399b3e8f43d2059a10792076 U drh -Z 4bb2ffcd67d474872b040ea5d2948b38 +Z f2d44a3082cc3593041829311c626937 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f20238fd7c..f5e15eac91 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ea755771699dcbffe0ddfd204d42fae9b6e79e107485c725b8eb6caab92aacb8 \ No newline at end of file +8bf41bc5cb19fcde569ed2c788553a848ebd9c79065bd3d2aa99e5a6bfed9696 \ No newline at end of file diff --git a/src/ctime.c b/src/ctime.c index 708d6ac2bf..4377528006 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -181,8 +181,6 @@ static const char * const sqlite3azCompileOpt[] = { #endif #ifdef SQLITE_DISABLE_JSON "DISABLE_JSON", -#else - "ENABLE_JSON1", /* Legacy */ #endif #ifdef SQLITE_DISABLE_LFS "DISABLE_LFS", From f2afe4e888b786fc4d8b8e78bed13ea774cb0b09 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 9 Jan 2022 20:42:55 +0000 Subject: [PATCH 12/18] Update the doc/json-enhancements.md document to better explain the features of this branch. FossilOrigin-Name: b8ac938f41eff8e5a23037c0e8b035a65e1b9505eca2a601221c195225595788 --- doc/json-enhancements.md | 206 +++++++++++++++++++-------------------- manifest | 12 +-- manifest.uuid | 2 +- 3 files changed, 110 insertions(+), 110 deletions(-) diff --git a/doc/json-enhancements.md b/doc/json-enhancements.md index 4c226a2c96..fababa8be2 100644 --- a/doc/json-enhancements.md +++ b/doc/json-enhancements.md @@ -1,123 +1,123 @@ -# Proposed Enhancements To JSON Functions +# JSON Functions Enhancements (2022) -## 1.0 New function json_nextract() +This document summaries enhancements to the SQLite JSON support added in +early 2022. -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.0 New feature summary: - 1. **json_extract()** → raises an error and aborts the query. + 1. New **->** and **->>** operators that work like MySQL and PostgreSQL (PG). + 2. New functions: **json_nextract()** and **json_ntype()**. + 3. JSON functions are built-in rather than being an extension. They + are included by default, but can be omitted using the + -DSQLITE_OMIT_JSON compile-time option. - 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. +## 2.0 The **json_nextract()** function. - 3. **json_nextract()** otherwise → return NULL. +The new **json_nextract()** function works like **json_extract()** with +one exception: if the input text in first argument is not well-formed +JSON, then json_nextract() returns NULL whereas json_extract() raises +an error. The extra "n" in the name of json_nextract() can be throught +of as meaning "null-if-error". -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: +A call to json_nextract($JSON,$PATH) is logically equivalent to: > ~~~ -UPDATE data SET jsonData = json_nextract(jsonData,'$'); +CASE WHEN json_valid($JSON) THEN json_extract($JSON,$PATH) END ~~~ -In the query above, well-formed JSON would be unchanged, and mis-formatted -JSON would be converted into a well-formatted JSON string. +The json_nextract() function is intended for use in tables where a +column might hold a mixture of datatypes - some rows holding JSON and other +rows holding primitive SQL datatypes such as INT, REAL, and TEXT. The +json_nextract() function makes it easier to write robust queries +against such tables. -## 2.0 Add the `->` and '->>` operators as aliases for json_extract(). +## 3.0 New operators **->** and **->>** -Two new binary operators "`->`" and "`->>`" operators are the same -as json_nextract() and json_extract(), respectively. +The SQLite language adds two new binary operators **->** and **->>**. +The -> operator works like the two-argument version of json_nextract() +and the ->> operator works like the two-argument version of json_extract(). +The left-hand operand of -> and ->> is JSON. The right-hand operand +is a JSON path expression. These operators extract and return a value +from the left-hand JSON that is specified by right-hand path expression. + +The operators work exactly the same if the left-hand side is well-formed +JSON. The only difference is that if the left-hand side is not well-formed +JSON, the ->> raises an error whereas the -> operator simply returns NULL. + +### 3.1 Compatibility with MySQL + +The ->> operator should be compatible with MySQL in the sense that +a ->> operator that works in MySQL should work the same way in SQLite. +But (see below) the SQLite ->> operator is also extended to support PG +syntax so not every use of ->> that wworks in SQLite will work for MySQL. + +The -> operator is *mostly* compatible with MySQL. Key differences +between the SQLite -> operator and the MySQL -> operator are: + + * The SQLite -> operator returns NULL if the left-hand side is + not well-formed JSON whereas MySQL will raise an error. + + * When the JSON path expression on the right-hand side selects a + text value from the JSON, the -> operator in MySQL returns the + string quoted as if for JSON, whereas the SQLite -> operator + returns an unquoted SQL text value. + +This second difference - the handling of text values extracted from JSON - +is also a difference in the json_extract() function between SQLite and +MySQL. Because json_extract() has been in active use for 6 years, and +because the SQLite semantics seem to be more useful, there +are no plans to change json_extract() to make it compatible with MySQL. + +### 3.2 Compatibility with PostgreSQL (PG) + +The ->> operator in PG does not accept a JSON path expression as its +right-hand operand. Instead, PG looks for either a text string X +(which is then interpreted as the path "$.X") or an integer N (which +is then interpreted as "$[N]"). In order to make the SQLite ->> operator +compatible with the PG ->> operator, the SQLite ->> operator has been +extended so that its right-hand operand can be either a text label or +a integer array index, as it is in PG. The SQLite ->> operator also +accepts full JSON path expressions as well. + +The enhancement of accepting JSON path expression that consist of just +a bare object label or array index is unique to the -> and ->> operators. +All other places in the SQLite JSON interface that require JSON path +expressions continue to require well-formed JSON path expressions. +Only -> and ->> accept the PG-compatible abbreviated path expressions. + +The -> operator in SQLite is *mostly* compatible with the -> operator +in PG. The differences are the same as for MySQL. + +## 4.0 The **json_ntype()** function. + +The **json_ntype()** function works like **json_type()** except that when +the argument is not well-formed JSON, the json_ntype() function returns +NULL whereas json_type() raises an error. The extra "n" in the name can +be understood as standing for "null-if-error". + +The json_ntype($JSON) function is logically equivalent to: > ~~~ -SELECT '{"a":5,"b":17}' -> '$.a', '[4,1,-6]' ->> '$[0]'; +CASE WHEN json_valid($JSON) THEN json_type($JSON) END ~~~ -Is equivalent to (and generates the same bytecode as): +The json_ntype() function can be seen as an enhanced version of +the json_valid() function, that in addition to indicating whether or +not the string is well-formed JSON, also indicates the top-level type +of that JSON. -> ~~~ -SELECT json_nextract('{"a":5,"b":17}','$.a'), json_extract('[4,1,-6]','$[0]'); -~~~ +## 5.0 JSON moved into the core -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 JSON interface is now moved into the SQLite core. -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. +When originally written in 2015, the JSON functions were an extension +that could be optionally included at compile-time, or loaded at run-time. +The implementation was in a source file named ext/misc/json1.c in the +source tree. JSON functions were only compiled in if the +-DSQLITE_ENABLE_JSON1 compile-time option was used. -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'; -~~~ +After these enhancements, the JSON functions are now built-ins. +The source file that implements the JSON functions is moved to src/json.c. +No special compile-time options are needed to load JSON into the build. +Instead, there is a new -DSQLITE_OMIT_JSON compile-time option to leave +them out. diff --git a/manifest b/manifest index 48688d7bc6..b1040bffa8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\senclude\sENABLE_JSON1\sin\sthe\scompile-time\soptions. -D 2022-01-09T19:44:36.695 +C Update\sthe\sdoc/json-enhancements.md\sdocument\sto\sbetter\sexplain\sthe\sfeatures\nof\sthis\sbranch. +D 2022-01-09T20:42:55.486 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -38,7 +38,7 @@ F configure a2877fe63cc821af0df41abe70f1f7c4e97cb7e23a42e0a1402e8a2f55a88aa2 x F configure.ac 3ef6eeff4387585bfcab76b0c3f6e15a0618587bb90245dd5d44e4378141bb35 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd -F doc/json-enhancements.md b026346e18a18a90d84dbda457e3127282468ad26baaefc005a1656429fa4232 +F doc/json-enhancements.md 07822849342a976455b8d9c7d564d347b97e13ca8f7444388312a8f7a0d5e846 F doc/lemon.html efc0cd2345d66905505d98f862e1c571512def0ceb5b016cb658fd4918eb76a3 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a @@ -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 ea755771699dcbffe0ddfd204d42fae9b6e79e107485c725b8eb6caab92aacb8 -R 70859f0c399b3e8f43d2059a10792076 +P 8bf41bc5cb19fcde569ed2c788553a848ebd9c79065bd3d2aa99e5a6bfed9696 +R 83ea098ded25ea5f04c457b4cfe21d79 U drh -Z f2d44a3082cc3593041829311c626937 +Z b469d7ebc54649e5bb73aa5daf50477b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f5e15eac91..13ef2c36e9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8bf41bc5cb19fcde569ed2c788553a848ebd9c79065bd3d2aa99e5a6bfed9696 \ No newline at end of file +b8ac938f41eff8e5a23037c0e8b035a65e1b9505eca2a601221c195225595788 \ No newline at end of file From 18ccc5c5ab3762b7fd1478525707dba4c15c9d43 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 9 Jan 2022 20:51:59 +0000 Subject: [PATCH 13/18] Typo fix in doc/json-enhancements.md. FossilOrigin-Name: c3b01d496479b3250a8895c245f79ab43ac469148d163593fea00894db195a37 --- doc/json-enhancements.md | 2 +- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/json-enhancements.md b/doc/json-enhancements.md index fababa8be2..8d83554ad2 100644 --- a/doc/json-enhancements.md +++ b/doc/json-enhancements.md @@ -14,7 +14,7 @@ early 2022. ## 2.0 The **json_nextract()** function. The new **json_nextract()** function works like **json_extract()** with -one exception: if the input text in first argument is not well-formed +one exception: if the input text in the first argument is not well-formed JSON, then json_nextract() returns NULL whereas json_extract() raises an error. The extra "n" in the name of json_nextract() can be throught of as meaning "null-if-error". diff --git a/manifest b/manifest index b1040bffa8..2111bfa40b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\sdoc/json-enhancements.md\sdocument\sto\sbetter\sexplain\sthe\sfeatures\nof\sthis\sbranch. -D 2022-01-09T20:42:55.486 +C Typo\sfix\sin\sdoc/json-enhancements.md. +D 2022-01-09T20:51:59.795 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -38,7 +38,7 @@ F configure a2877fe63cc821af0df41abe70f1f7c4e97cb7e23a42e0a1402e8a2f55a88aa2 x F configure.ac 3ef6eeff4387585bfcab76b0c3f6e15a0618587bb90245dd5d44e4378141bb35 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd -F doc/json-enhancements.md 07822849342a976455b8d9c7d564d347b97e13ca8f7444388312a8f7a0d5e846 +F doc/json-enhancements.md b1731f584a8594660dcee86555e77559dbc447a88c010f010a7f5274bb050459 F doc/lemon.html efc0cd2345d66905505d98f862e1c571512def0ceb5b016cb658fd4918eb76a3 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a @@ -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 8bf41bc5cb19fcde569ed2c788553a848ebd9c79065bd3d2aa99e5a6bfed9696 -R 83ea098ded25ea5f04c457b4cfe21d79 +P b8ac938f41eff8e5a23037c0e8b035a65e1b9505eca2a601221c195225595788 +R bb97ccd30a4bea2e78fa0684b28c1c7a U drh -Z b469d7ebc54649e5bb73aa5daf50477b +Z 59da2a31f3cfc387aa42335fd8788184 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 13ef2c36e9..173df7a789 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b8ac938f41eff8e5a23037c0e8b035a65e1b9505eca2a601221c195225595788 \ No newline at end of file +c3b01d496479b3250a8895c245f79ab43ac469148d163593fea00894db195a37 \ No newline at end of file From d3c6c3459b5aff110877428770e52df354c63360 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 10 Jan 2022 13:55:08 +0000 Subject: [PATCH 14/18] New proposal for -> and ->> operators. FossilOrigin-Name: 1108e12a2244edc6247050a0e9ad25b912bba6c57c71c51c2bc55167a6955175 --- doc/json-enhancements.md | 179 ++++++++++++++++++++++----------------- manifest | 12 +-- manifest.uuid | 2 +- 3 files changed, 107 insertions(+), 86 deletions(-) diff --git a/doc/json-enhancements.md b/doc/json-enhancements.md index 8d83554ad2..d3ab6e0a43 100644 --- a/doc/json-enhancements.md +++ b/doc/json-enhancements.md @@ -3,110 +3,131 @@ This document summaries enhancements to the SQLite JSON support added in early 2022. -## 1.0 New feature summary: +## 1.0 Change summary: 1. New **->** and **->>** operators that work like MySQL and PostgreSQL (PG). - 2. New functions: **json_nextract()** and **json_ntype()**. - 3. JSON functions are built-in rather than being an extension. They + 2. JSON functions are built-in rather than being an extension. They are included by default, but can be omitted using the -DSQLITE_OMIT_JSON compile-time option. -## 2.0 The **json_nextract()** function. -The new **json_nextract()** function works like **json_extract()** with -one exception: if the input text in the first argument is not well-formed -JSON, then json_nextract() returns NULL whereas json_extract() raises -an error. The extra "n" in the name of json_nextract() can be throught -of as meaning "null-if-error". - -A call to json_nextract($JSON,$PATH) is logically equivalent to: - -> ~~~ -CASE WHEN json_valid($JSON) THEN json_extract($JSON,$PATH) END -~~~ - -The json_nextract() function is intended for use in tables where a -column might hold a mixture of datatypes - some rows holding JSON and other -rows holding primitive SQL datatypes such as INT, REAL, and TEXT. The -json_nextract() function makes it easier to write robust queries -against such tables. - -## 3.0 New operators **->** and **->>** +## 2.0 New operators **->** and **->>** The SQLite language adds two new binary operators **->** and **->>**. -The -> operator works like the two-argument version of json_nextract() -and the ->> operator works like the two-argument version of json_extract(). -The left-hand operand of -> and ->> is JSON. The right-hand operand -is a JSON path expression. These operators extract and return a value -from the left-hand JSON that is specified by right-hand path expression. +Both operators are similar to json_extract(). The left operand is +JSON and the right operand is a JSON path expression (possibly abbreviated +for compatibility with PG - see below). So they are similar to a +two-argument call to json_extract(). -The operators work exactly the same if the left-hand side is well-formed -JSON. The only difference is that if the left-hand side is not well-formed -JSON, the ->> raises an error whereas the -> operator simply returns NULL. +The difference between -> and ->> (and json_extract()) is as follows: -### 3.1 Compatibility with MySQL + * The -> operator always returns JSON. -The ->> operator should be compatible with MySQL in the sense that -a ->> operator that works in MySQL should work the same way in SQLite. -But (see below) the SQLite ->> operator is also extended to support PG -syntax so not every use of ->> that wworks in SQLite will work for MySQL. + * The ->> operator converts the answer into a primitive SQL datatype + such as TEXT, INTEGER, REAL, or NULL. If a JSON object or array + is selected, that object or array is rendered as text. If a JSON + value is selected, that value is converted into its corresponding + SQL type -The -> operator is *mostly* compatible with MySQL. Key differences -between the SQLite -> operator and the MySQL -> operator are: + * The json_extract() interface returns JSON when a JSON object or + array is selected, or a primitive SQL datatype when a JSON value + is selected. This is different from MySQL, in which json_extract() + always returns JSON, but the difference is retained because it has + worked that way for 6 years and changing it now would likely break + a lot of legacy code. - * The SQLite -> operator returns NULL if the left-hand side is - not well-formed JSON whereas MySQL will raise an error. +In MySQL and PG, the ->> operator always returns TEXT (or NULL) and never +INTEGER or REAL. This is due to limitations in the type handling capabilities +of those systems. In MySQL and PG, the result type a function or operator +may only depend on the type of its arguments, never the value of its arguments. +But the underlying JSON type depends on the value of the JSON path +expression, not the type of the JSON path expression (which is always TEXT). +Hence, the result type of ->> in MySQL and PG is unable to vary according +to the type of the JSON value being extracted. - * When the JSON path expression on the right-hand side selects a - text value from the JSON, the -> operator in MySQL returns the - string quoted as if for JSON, whereas the SQLite -> operator - returns an unquoted SQL text value. +The type system in SQLite is more general. Functions in SQLite are able +to return different datatypes depending on the value of their arguments. +So the ->> operator in SQLite is able to return TEXT, INTEGER, REAL, or NULL +depending on the JSON type of the value being extracted. This means that +the behavior of the ->> is slightly different in SQLite versus MySQL and PG +in that it will sometimes return INTEGER and REAL values, depending on its +inputs. It is possible to implement the ->> operator in SQLite so that it +always operates exactly like MySQL and PG and always returns TEXT or NULL, +but I have been unable to think of any situations where returning the +actual JSON value this would cause problems, so I'm including the enhanced +functionality in SQLite. -This second difference - the handling of text values extracted from JSON - -is also a difference in the json_extract() function between SQLite and -MySQL. Because json_extract() has been in active use for 6 years, and -because the SQLite semantics seem to be more useful, there -are no plans to change json_extract() to make it compatible with MySQL. +The table below attempts to summarize the differences between the +-> and ->> operators and the json_extract() function, for SQLite, MySQL, +and PG. JSON values are shown using their SQL text representation but +in a bold font. -### 3.2 Compatibility with PostgreSQL (PG) -The ->> operator in PG does not accept a JSON path expression as its -right-hand operand. Instead, PG looks for either a text string X -(which is then interpreted as the path "$.X") or an integer N (which -is then interpreted as "$[N]"). In order to make the SQLite ->> operator -compatible with the PG ->> operator, the SQLite ->> operator has been -extended so that its right-hand operand can be either a text label or -a integer array index, as it is in PG. The SQLite ->> operator also -accepts full JSON path expressions as well. + +
JSONPATH-> operator
(all)
->> operator
(MySQL/PG) +
->> operator
(SQLite)
json_extract()
(SQLite) +
**'{"a":123}'** '$.a' **'123'** '123' 123 123 +
**'{"a":4.5}'** '$.a' **'4.5'** '4.5' 4.5 4.5 +
**'{"a":"xyz"}'** '$.a' **'"xyz"'** 'xyz' 'xyz' 'xyz' +
**'{"a":null}'** '$.a' **'null'** NULL NULL NULL +
**'{"a":[6,7,8]}'** '$.a' **'[6,7,8]'** '[6,7,8]' '[6,7,8]' **'[6,7,9]'** +
**'{"a":{"x":9}}'** '$.a' **'{"x":9}'** '{"x":9}' '{"x":9}' **'{"x":9}'** +
**'{"b":999}'** '$.a' NULL NULL NULL NULL +
-The enhancement of accepting JSON path expression that consist of just -a bare object label or array index is unique to the -> and ->> operators. -All other places in the SQLite JSON interface that require JSON path -expressions continue to require well-formed JSON path expressions. -Only -> and ->> accept the PG-compatible abbreviated path expressions. +Important points about the table above: -The -> operator in SQLite is *mostly* compatible with the -> operator -in PG. The differences are the same as for MySQL. + * The -> operator always returns either JSON or NULL. -## 4.0 The **json_ntype()** function. + * The ->> operator never returns JSON. It always returns TEXT or NULL, or in the + case of SQLite, INTEGER or REAL. -The **json_ntype()** function works like **json_type()** except that when -the argument is not well-formed JSON, the json_ntype() function returns -NULL whereas json_type() raises an error. The extra "n" in the name can -be understood as standing for "null-if-error". + * The MySQL json_extract() function works exactly the same + as the MySQL -> operator. -The json_ntype($JSON) function is logically equivalent to: + * The SQLite json_extract() operator works like -> for JSON objects and + arrays, and like ->> for JSON values. -> ~~~ -CASE WHEN json_valid($JSON) THEN json_type($JSON) END -~~~ + * The -> operator works the same for all systems. -The json_ntype() function can be seen as an enhanced version of -the json_valid() function, that in addition to indicating whether or -not the string is well-formed JSON, also indicates the top-level type -of that JSON. + * The only difference in ->> between SQLite and other systems is that + when the JSON value is numeric, SQLite returns a numeric SQL value, + whereas the other systems return a text representation of the numeric + value. -## 5.0 JSON moved into the core +### 2.1 Abbreviated JSON path expressions for PG compatibility + +The table above always shows the full JSON path expression: '$.a'. But +PG does not accept this syntax. PG only allows a single JSON object label +name or a single integer array index. In order to provide compatibility +with PG, The -> and ->> operators in SQLite are extended to also support +a JSON object label or an integer array index for the right-hand side +operand, in addition to a full JSON path expression. + +Thus, a -> or ->> operator that works on MySQL will work in +SQLite. And a -> or ->> operator that works in PG will work in SQLite. +But because SQLite supports the union of the disjoint capabilities of +MySQL and PG, there will always be -> and ->> operators that work in +SQLite that do not work in one of MySQL and PG. This is an unavoidable +consequence of the different syntax for -> and ->> in MySQL and PG. + +In the following table, assume that "value1" is a JSON object and +"value2" is a JSON array. + + +
SQL expression Works in MySQL?Works in PG?Works in SQLite +
value1->'$.a' yes no yes +
value1->'a' no yes yes +
value2->'$[2]' yes no yes +
value2->2 no yes yes +
+ +The abbreviated JSON path expressions only work for the -> and ->> operators +in SQLite. The json_extract() function, and all other built-in SQLite +JSON functions, continue to require complete JSON path expressions for their +PATH arguments. + +## 3.0 JSON moved into the core The JSON interface is now moved into the SQLite core. diff --git a/manifest b/manifest index 2111bfa40b..2019f703d9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Typo\sfix\sin\sdoc/json-enhancements.md. -D 2022-01-09T20:51:59.795 +C New\sproposal\sfor\s->\sand\s->>\soperators. +D 2022-01-10T13:55:08.463 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -38,7 +38,7 @@ F configure a2877fe63cc821af0df41abe70f1f7c4e97cb7e23a42e0a1402e8a2f55a88aa2 x F configure.ac 3ef6eeff4387585bfcab76b0c3f6e15a0618587bb90245dd5d44e4378141bb35 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd -F doc/json-enhancements.md b1731f584a8594660dcee86555e77559dbc447a88c010f010a7f5274bb050459 +F doc/json-enhancements.md 7f67a2e75de23958b9e767a15f6fb6abf918ef53f45f7dbb6d70ec7b55d71810 F doc/lemon.html efc0cd2345d66905505d98f862e1c571512def0ceb5b016cb658fd4918eb76a3 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a @@ -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 b8ac938f41eff8e5a23037c0e8b035a65e1b9505eca2a601221c195225595788 -R bb97ccd30a4bea2e78fa0684b28c1c7a +P c3b01d496479b3250a8895c245f79ab43ac469148d163593fea00894db195a37 +R 992262dfca636fa297052ec77a843cce U drh -Z 59da2a31f3cfc387aa42335fd8788184 +Z 83712db3104322990e05c7028e4a9b88 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 173df7a789..d1f9996545 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c3b01d496479b3250a8895c245f79ab43ac469148d163593fea00894db195a37 \ No newline at end of file +1108e12a2244edc6247050a0e9ad25b912bba6c57c71c51c2bc55167a6955175 \ No newline at end of file From d83c90bd63aaf20808355aa815c3d4be1424d5f8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 10 Jan 2022 15:43:13 +0000 Subject: [PATCH 15/18] Implement the new PG-compliant versions of the -> and ->> operators. FossilOrigin-Name: 39eff3b9bf1f318d3606d8e5361a2adb2ba8bc1ca2a436ce4f9a204aa4cf20dd --- manifest | 14 +++--- manifest.uuid | 2 +- src/json.c | 77 +++++++++++++------------------ test/json102.test | 114 +++++++++++++++++++++++----------------------- 4 files changed, 96 insertions(+), 111 deletions(-) diff --git a/manifest b/manifest index 2019f703d9..990e4b9844 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\sproposal\sfor\s->\sand\s->>\soperators. -D 2022-01-10T13:55:08.463 +C Implement\sthe\snew\sPG-compliant\sversions\sof\sthe\s->\sand\s->>\soperators. +D 2022-01-10T15:43:13.387 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -512,7 +512,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c e528416ff5d86fc5d656ea6a26f03fde39836b6175f93048c32a03cb2ee16743 -F src/json.c 726f1901ecc66bc359d785c268e70c2f10fcec22b212613b00d7da5aa561d462 +F src/json.c d3134d392d88cb21ab352dd877b901ee5d46e6404fc1321f77cf67bb4d4dc9d3 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 95db1fe62c5973f1c5d9c53f6083e21a73ece14cdd47eeca0639691332e85c4d F src/main.c 2b6b0dbfeb14d4bb57e368604b0736b2aa42b51b00339d399b01d6b1fc9b4960 @@ -1148,7 +1148,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 a44a20b7dfa446e67f47d0c02927fe93f80d6ebf3080a5d827757e8d4921c081 +F test/json102.test 86edc7d283085addff8593b178997e75875530d1385f5926717543d3475e6b01 F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144 F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173 @@ -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 c3b01d496479b3250a8895c245f79ab43ac469148d163593fea00894db195a37 -R 992262dfca636fa297052ec77a843cce +P 1108e12a2244edc6247050a0e9ad25b912bba6c57c71c51c2bc55167a6955175 +R 7d182b8fa0a61a014b08b226b8a1030b U drh -Z 83712db3104322990e05c7028e4a9b88 +Z b4595bb8abe65d415fc97060e1191b94 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d1f9996545..2dee764db7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1108e12a2244edc6247050a0e9ad25b912bba6c57c71c51c2bc55167a6955175 \ No newline at end of file +39eff3b9bf1f318d3606d8e5361a2adb2ba8bc1ca2a436ce4f9a204aa4cf20dd \ No newline at end of file diff --git a/src/json.c b/src/json.c index aad4507767..e2dbc7e307 100644 --- a/src/json.c +++ b/src/json.c @@ -1506,40 +1506,30 @@ static void jsonArrayLengthFunc( ** 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_JSON 0x01 /* Result is always JSON */ +#define JSON_SQL 0x02 /* Result is always SQL */ +#define JSON_ABPATH 0x03 /* 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 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. +** is not found. ** -** If there are multiple PATHs, then the value returned is a JSON array -** with one entry in the array for each PATH term. +** If JSON_JSON is set or if more that one PATH argument is supplied then +** always return a JSON representation of the result. If JSON_SQL is set, +** then always return an SQL representation of the result. If neither flag +** is present and argc==2, then return JSON for objects and arrays and SQL +** for all other values. ** -** Throw an error if any PATH is malformed. +** When multiple PATH arguments are supplied, the result is a JSON array +** containing the result of each PATH. ** -** 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 "$". +** Abbreviated JSON path expressions are allows if JSON_ABPATH, for +** compatibility with PG. */ static void jsonExtractFunc( sqlite3_context *ctx, @@ -1553,23 +1543,13 @@ static void jsonExtractFunc( JsonString jx; if( argc<2 ) return; - 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 & JSON_NULLERR)!=0 - && argc==2 - && (zPath = (const char*)sqlite3_value_text(argv[1]))!=0 - && zPath[0]=='$' && zPath[1]==0 - ){ - jsonQuoteFunc(ctx, argc, argv); - } - return; - } + p = jsonParseCached(ctx, argv, ctx); + if( p==0 ) return; if( argc==2 ){ - /* With a single PATH argument, the return is the unquoted SQL value */ + /* With a single PATH argument */ zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath && zPath[0]!='$' && zPath[0]!=0 && (flags & JSON_ABPATH)!=0 ){ + if( zPath==0 ) return; + if( zPath[0]!='$' && (flags & JSON_ABPATH)!=0 ){ /* The -> and ->> operators accept abbreviated PATH arguments. This ** is mostly for compatibility with PostgreSQL, but also for convenience. ** @@ -1587,13 +1567,22 @@ static void jsonExtractFunc( jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); jsonAppendChar(&jx, 0); } - pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); + if( jx.bErr==0 ){ + pNode = jsonLookup(p, jx.zBuf, 0, ctx); + if( pNode==0 ){ + /* No-op. jsonLookup will have left an error for us */ + }else if( flags & JSON_JSON ){ + jsonReturnJson(pNode, ctx, 0); + }else{ + jsonReturn(pNode, ctx, 0); + sqlite3_result_subtype(ctx, 0); + } + } jsonReset(&jx); }else{ pNode = jsonLookup(p, zPath, 0, ctx); + if( p->nErr==0 && pNode ) jsonReturn(pNode, ctx, 0); } - 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 */ @@ -2637,11 +2626,9 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), JFUNCTION(json_extract, -1, 0, jsonExtractFunc), - JFUNCTION(json_nextract, -1, JSON_NULLERR, jsonExtractFunc), - JFUNCTION(->, 2, JSON_NULLERR|JSON_ABPATH, jsonExtractFunc), - JFUNCTION(->>, 2, JSON_ABPATH, jsonExtractFunc), + JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), JFUNCTION(json_insert, -1, 0, jsonSetFunc), - JFUNCTION(json_ntype, 1, JSON_NULLERR, jsonTypeFunc), JFUNCTION(json_object, -1, 0, jsonObjectFunc), JFUNCTION(json_patch, 2, 0, jsonPatchFunc), JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), diff --git a/test/json102.test b/test/json102.test index 7ae99353bf..9f8f482b85 100644 --- a/test/json102.test +++ b/test/json102.test @@ -71,70 +71,21 @@ 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 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 { 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_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-269a { - SELECT '[1,2,3' ->> '$'; -} {1 {malformed JSON}} -do_catchsql_test json102-269b { - 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}}} -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]}} @@ -198,9 +149,6 @@ 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}} @@ -234,12 +182,6 @@ 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 { @@ -395,4 +337,60 @@ do_execsql_test json102-1501 { SELECT sum(json_valid(json_quote('a'||char(x)||'z'))) FROM c ORDER BY x; } {31} +# 2022-01-10 tests for -> and ->> operators +# +reset_db +do_execsql_test json102-1600 { + CREATE TABLE t1(id INTEGER PRIMARY KEY, x JSON); + INSERT INTO t1(id,x) VALUES + (1, '{"a":null}'), + (2, '{"a":123}'), + (3, '{"a":4.5}'), + (4, '{"a":"six"}'), + (5, '{"a":[7,8]}'), + (6, '{"a":{"b":9}}'), + (7, '{"b":999}'); + SELECT + id, + x->'a' AS '->', + CASE WHEN subtype(x->'a') THEN 'json' ELSE typeof(x->'a') END AS 'type', + x->>'a' AS '->>', + CASE WHEN subtype(x->>'a') THEN 'json' ELSE typeof(x->>'a') END AS 'type', + json_extract(x,'$.a') AS 'json_extract', + CASE WHEN subtype(json_extract(x,'$.a')) + THEN 'json' ELSE typeof(json_extract(x,'$.a')) END AS 'type' + FROM t1 ORDER BY id; +} [list \ + 1 null json {} null {} null \ + 2 123 json 123 integer 123 integer \ + 3 4.5 json 4.5 real 4.5 real \ + 4 {"six"} json six text six text \ + 5 {[7,8]} json {[7,8]} text {[7,8]} json \ + 6 {{"b":9}} json {{"b":9}} text {{"b":9}} json \ + 7 {} null {} null {} null +] +do_execsql_test json102-1610 { + DELETE FROM t1; + INSERT INTO t1(x) VALUES('[null,123,4.5,"six",[7,8],{"b":9}]'); + WITH c(y) AS (VALUES(0),(1),(2),(3),(4),(5),(6)) + SELECT + y, + x->y AS '->', + CASE WHEN subtype(x->y) THEN 'json' ELSE typeof(x->y) END AS 'type', + x->>y AS '->>', + CASE WHEN subtype(x->>y) THEN 'json' ELSE typeof(x->>y) END AS 'type', + json_extract(x,format('$[%d]',y)) AS 'json_extract', + CASE WHEN subtype(json_extract(x,format('$[%d]',y))) + THEN 'json' ELSE typeof(json_extract(x,format('$[%d]',y))) END AS 'type' + FROM c, t1 ORDER BY y; +} [list \ + 0 null json {} null {} null \ + 1 123 json 123 integer 123 integer \ + 2 4.5 json 4.5 real 4.5 real \ + 3 {"six"} json six text six text \ + 4 {[7,8]} json {[7,8]} text {[7,8]} json \ + 5 {{"b":9}} json {{"b":9}} text {{"b":9}} json \ + 6 {} null {} null {} null +] + finish_test From 875912c246f38376a102521e48e3efe1c647d26d Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 10 Jan 2022 17:43:54 +0000 Subject: [PATCH 16/18] Fix typo in the json-enhancements.md document. FossilOrigin-Name: feba24ef774d80ebbaf87a93fc106cb7e482edcc3f237edd4c9d4e918ffb3131 --- doc/json-enhancements.md | 2 +- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/json-enhancements.md b/doc/json-enhancements.md index d3ab6e0a43..bc03e8978c 100644 --- a/doc/json-enhancements.md +++ b/doc/json-enhancements.md @@ -70,7 +70,7 @@ in a bold font. **'{"a":4.5}'** '$.a' **'4.5'** '4.5' 4.5 4.5 **'{"a":"xyz"}'** '$.a' **'"xyz"'** 'xyz' 'xyz' 'xyz' **'{"a":null}'** '$.a' **'null'** NULL NULL NULL - **'{"a":[6,7,8]}'** '$.a' **'[6,7,8]'** '[6,7,8]' '[6,7,8]' **'[6,7,9]'** + **'{"a":[6,7,8]}'** '$.a' **'[6,7,8]'** '[6,7,8]' '[6,7,8]' **'[6,7,8]'** **'{"a":{"x":9}}'** '$.a' **'{"x":9}'** '{"x":9}' '{"x":9}' **'{"x":9}'** **'{"b":999}'** '$.a' NULL NULL NULL NULL diff --git a/manifest b/manifest index 990e4b9844..4fe6fba3c0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Implement\sthe\snew\sPG-compliant\sversions\sof\sthe\s->\sand\s->>\soperators. -D 2022-01-10T15:43:13.387 +C Fix\stypo\sin\sthe\sjson-enhancements.md\sdocument. +D 2022-01-10T17:43:54.652 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -38,7 +38,7 @@ F configure a2877fe63cc821af0df41abe70f1f7c4e97cb7e23a42e0a1402e8a2f55a88aa2 x F configure.ac 3ef6eeff4387585bfcab76b0c3f6e15a0618587bb90245dd5d44e4378141bb35 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd -F doc/json-enhancements.md 7f67a2e75de23958b9e767a15f6fb6abf918ef53f45f7dbb6d70ec7b55d71810 +F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f F doc/lemon.html efc0cd2345d66905505d98f862e1c571512def0ceb5b016cb658fd4918eb76a3 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a @@ -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 1108e12a2244edc6247050a0e9ad25b912bba6c57c71c51c2bc55167a6955175 -R 7d182b8fa0a61a014b08b226b8a1030b +P 39eff3b9bf1f318d3606d8e5361a2adb2ba8bc1ca2a436ce4f9a204aa4cf20dd +R 8ac564d1981cf04622f1f15a60ba0e5b U drh -Z b4595bb8abe65d415fc97060e1191b94 +Z c237913f633491314765f45ba87ab8df # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2dee764db7..af50a8820d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -39eff3b9bf1f318d3606d8e5361a2adb2ba8bc1ca2a436ce4f9a204aa4cf20dd \ No newline at end of file +feba24ef774d80ebbaf87a93fc106cb7e482edcc3f237edd4c9d4e918ffb3131 \ No newline at end of file From aa97b57b111b20c0d1fa4e5d0d7576506a81142e Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 11 Jan 2022 18:01:17 +0000 Subject: [PATCH 17/18] Fix the PG-compatible -> and ->> path parsing. FossilOrigin-Name: 22d5138315fb77eeea1c7e66ccc4190bcae18d058a99aa37ddd119e54b52f723 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 47 +++++++++++++++++++++++++---------------------- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/manifest b/manifest index 4fe6fba3c0..1af7555428 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\stypo\sin\sthe\sjson-enhancements.md\sdocument. -D 2022-01-10T17:43:54.652 +C Fix\sthe\sPG-compatible\s->\sand\s->>\spath\sparsing. +D 2022-01-11T18:01:17.466 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -512,7 +512,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c e528416ff5d86fc5d656ea6a26f03fde39836b6175f93048c32a03cb2ee16743 -F src/json.c d3134d392d88cb21ab352dd877b901ee5d46e6404fc1321f77cf67bb4d4dc9d3 +F src/json.c 5dee750a85262600817f20e3ced050f9c2a47d8c3db77f31f4ee379c0f843a8a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 95db1fe62c5973f1c5d9c53f6083e21a73ece14cdd47eeca0639691332e85c4d F src/main.c 2b6b0dbfeb14d4bb57e368604b0736b2aa42b51b00339d399b01d6b1fc9b4960 @@ -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 39eff3b9bf1f318d3606d8e5361a2adb2ba8bc1ca2a436ce4f9a204aa4cf20dd -R 8ac564d1981cf04622f1f15a60ba0e5b +P feba24ef774d80ebbaf87a93fc106cb7e482edcc3f237edd4c9d4e918ffb3131 +R 0007d377d0ed1c0dc1f81b209b625ab0 U drh -Z c237913f633491314765f45ba87ab8df +Z e45b9675c3fefc0ecfe81398e8f3bbb1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index af50a8820d..d47038dcfc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -feba24ef774d80ebbaf87a93fc106cb7e482edcc3f237edd4c9d4e918ffb3131 \ No newline at end of file +22d5138315fb77eeea1c7e66ccc4190bcae18d058a99aa37ddd119e54b52f723 \ No newline at end of file diff --git a/src/json.c b/src/json.c index e2dbc7e307..6fe5fa411c 100644 --- a/src/json.c +++ b/src/json.c @@ -1549,36 +1549,39 @@ static void jsonExtractFunc( /* With a single PATH argument */ zPath = (const char*)sqlite3_value_text(argv[1]); if( zPath==0 ) return; - if( zPath[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( sqlite3Isdigit(zPath[0]) ){ - jsonAppendRaw(&jx, "$[", 2); - jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendRaw(&jx, "]", 2); + if( flags & JSON_ABPATH ){ + if( zPath[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( sqlite3Isdigit(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 = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); + jsonReset(&jx); }else{ - jsonAppendRaw(&jx, "$.", 1 + (zPath[0]!='[')); - jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendChar(&jx, 0); + pNode = jsonLookup(p, zPath, 0, ctx); } - if( jx.bErr==0 ){ - pNode = jsonLookup(p, jx.zBuf, 0, ctx); - if( pNode==0 ){ - /* No-op. jsonLookup will have left an error for us */ - }else if( flags & JSON_JSON ){ + if( pNode ){ + if( flags & JSON_JSON ){ jsonReturnJson(pNode, ctx, 0); }else{ jsonReturn(pNode, ctx, 0); sqlite3_result_subtype(ctx, 0); } } - jsonReset(&jx); }else{ pNode = jsonLookup(p, zPath, 0, ctx); if( p->nErr==0 && pNode ) jsonReturn(pNode, ctx, 0); From a6c596b1941296377f22cc4173991c963d14a97e Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 11 Jan 2022 22:06:25 +0000 Subject: [PATCH 18/18] Remove vestigial traces of json_ntype(). FossilOrigin-Name: 8da07c8b09ff83436a34e539e4cce8ba9524390017ca14f4af8344a12e351550 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 7 ++----- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 1af7555428..c17e656e6b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sPG-compatible\s->\sand\s->>\spath\sparsing. -D 2022-01-11T18:01:17.466 +C Remove\svestigial\straces\sof\sjson_ntype(). +D 2022-01-11T22:06:25.550 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -512,7 +512,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c e528416ff5d86fc5d656ea6a26f03fde39836b6175f93048c32a03cb2ee16743 -F src/json.c 5dee750a85262600817f20e3ced050f9c2a47d8c3db77f31f4ee379c0f843a8a +F src/json.c f3bbaf6a4df6f5ada5b71e01666f5cd67642c616cd8be47dbfa5b5768dfa71d4 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 95db1fe62c5973f1c5d9c53f6083e21a73ece14cdd47eeca0639691332e85c4d F src/main.c 2b6b0dbfeb14d4bb57e368604b0736b2aa42b51b00339d399b01d6b1fc9b4960 @@ -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 feba24ef774d80ebbaf87a93fc106cb7e482edcc3f237edd4c9d4e918ffb3131 -R 0007d377d0ed1c0dc1f81b209b625ab0 +P 22d5138315fb77eeea1c7e66ccc4190bcae18d058a99aa37ddd119e54b52f723 +R 4fe2152bcfc77f35d559181df42337b8 U drh -Z e45b9675c3fefc0ecfe81398e8f3bbb1 +Z 2256adb4b917a0fe76784837651ce52b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d47038dcfc..f7e85f0d18 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -22d5138315fb77eeea1c7e66ccc4190bcae18d058a99aa37ddd119e54b52f723 \ No newline at end of file +8da07c8b09ff83436a34e539e4cce8ba9524390017ca14f4af8344a12e351550 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6fe5fa411c..dbc3faaef3 100644 --- a/src/json.c +++ b/src/json.c @@ -1903,13 +1903,10 @@ jsonSetDone: /* ** json_type(JSON) -** json_ntype(JSON) ** json_type(JSON, PATH) ** ** 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. +** error if either the JSON or PATH inputs are not well-formed. */ static void jsonTypeFunc( sqlite3_context *ctx, @@ -1920,7 +1917,7 @@ static void jsonTypeFunc( const char *zPath; JsonNode *pNode; - p = jsonParseCached(ctx, argv, sqlite3_user_data(ctx)!=0 ? 0 : ctx); + p = jsonParseCached(ctx, argv, ctx); if( p==0 ) return; if( argc==2 ){ zPath = (const char*)sqlite3_value_text(argv[1]);