summaryrefslogtreecommitdiffstats
path: root/test/json
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:07:11 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:07:11 +0000
commit63847496f14c813a5d80efd5b7de0f1294ffe1e3 (patch)
tree01c7571c7c762ceee70638549a99834fdd7c411b /test/json
parentInitial commit. (diff)
downloadsqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.tar.xz
sqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.zip
Adding upstream version 3.45.1.upstream/3.45.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--test/json/README.md66
-rw-r--r--test/json/json-generator.tcl401
-rw-r--r--test/json/json-q1.txt24
-rwxr-xr-xtest/json/json-speed-check.sh87
-rw-r--r--test/json/jsonb-q1.txt24
-rw-r--r--test/json101.test1167
-rw-r--r--test/json102.test767
-rw-r--r--test/json103.test93
-rw-r--r--test/json104.test195
-rw-r--r--test/json105.test113
-rw-r--r--test/json106.test73
-rw-r--r--test/json107.test86
-rw-r--r--test/json501.test309
-rw-r--r--test/json502.test67
-rw-r--r--test/jsonb01.test53
15 files changed, 3525 insertions, 0 deletions
diff --git a/test/json/README.md b/test/json/README.md
new file mode 100644
index 0000000..4ebbda6
--- /dev/null
+++ b/test/json/README.md
@@ -0,0 +1,66 @@
+The files in this subdirectory are used to help measure the performance
+of the SQLite JSON functions, especially in relation to handling large
+JSON inputs.
+
+# 1.0 Prerequisites
+
+ * Standard SQLite build environment (SQLite source tree, compiler, make, etc.)
+
+ * Valgrind
+
+ * Fossil (only the "fossil xdiff" command is used by this procedure)
+
+ * tclsh
+
+# 2.0 Setup
+
+ * Run: "`tclsh json-generator.tcl | sqlite3 json100mb.db`" to create
+ the 100 megabyte test database. Do this so that the "json100mb.db"
+ file lands in the directory from which you will run tests, not in
+ the test/json subdirectory of the source tree.
+
+ * Make a copy of "json100mb.db" into "jsonb100mb.db" - change the prefix
+ from "json" to "jsonb".
+
+ * Bring up jsonb100mb.db in the sqlite3 command-line shell.
+ Convert all of the content into JSONB using a commands like this:
+
+> UPDATE data1 SET x=jsonb(x);
+> VACUUM;
+
+ * Build the baseline sqlite3.c file with sqlite3.h and shell.c.
+
+> make clean sqlite3.c
+
+ * Run "`sh json-speed-check.sh trunk`". This creates the baseline
+ profile in "jout-trunk.txt" for the preformance test using text JSON.
+
+ * Run "`sh json-speed-check.sh trunk --jsonb`". This creates the
+ baseline profile in "joutb-trunk.txt" for the performance test
+ for processing JSONB
+
+ * (Optional) Verify that the json100mb.db database really does contain
+ approximately 100MB of JSON content by running:
+
+> SELECT sum(length(x)) FROM data1;
+> SELECT * FROM data1 WHERE NOT json_valid(x);
+
+# 3.0 Testing
+
+ * Build the sqlite3.c (with sqlite3.h and shell.c) to be tested.
+
+ * Run "`sh json-speed-check.sh x1`". The profile output will appear
+ in jout-x1.txt. Substitute any label you want in place of "x1".
+
+ * Run "`sh json-speed-check.sh x1 --jsonb`". The profile output will appear
+ in joutb-x1.txt. Substitute any label you want in place of "x1".
+
+ * Run the script shown below in the CLI.
+ Divide 2500 by the real elapse time from this test
+ to get an estimate for number of MB/s that the JSON parser is
+ able to process.
+
+> .open json100mb.db
+> .timer on
+> WITH RECURSIVE c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<25)
+> SELECT sum(json_valid(x)) FROM c, data1;
diff --git a/test/json/json-generator.tcl b/test/json/json-generator.tcl
new file mode 100644
index 0000000..d499bc7
--- /dev/null
+++ b/test/json/json-generator.tcl
@@ -0,0 +1,401 @@
+#!/usr/bin/tclsh
+#
+# Generate SQL that will populate an SQLite database with about 100 megabytes
+# of pseudo-random JSON text.
+#
+# tclsh json-generator.tcl | sqlite3 json110mb.db
+#
+# srand() is used to initialize the random seed so that the same JSON
+# is generated for every run.
+#
+expr srand(12345678)
+set wordlist {
+ ability able abroad access account act
+ action active actor add address adept
+ adroit advance advice affect age ageless
+ agency agent agile agree air airfare
+ airline airport alert almond alpha always
+ amend amount amplify analyst anchor angel
+ angelic angle ankle annual answer antique
+ anybody anyhow appeal apple apricot apt
+ area argon arm army arrival arsenic
+ art artful article arugula aside ask
+ aspect assist assume atom atone attempt
+ author autumn average avocado award awl
+ azure back bacon bag bagel bake
+ baker balance ball balloon bamboo banana
+ band banjo bank barium base basil
+ basin basis basket bass bat bath
+ battery beach beak bean bear bearcub
+ beauty beef beet beige being bell
+ belly belt bench bend benefit best
+ beta better beyond bicycle bid big
+ bike bill bird biscuit bismuth bisque
+ bit black blank blest blind bliss
+ block bloom blue board boat body
+ bokchoy bone bonus book bookish boot
+ border boron boss bossy bottle bottom
+ bow bowl bowtie box brain brainy
+ branch brave bravely bread break breath
+ breezy brick bridge brie brief briefly
+ bright broad broil bromine bronze brother
+ brow brown brush buddy budget buffalo
+ bug bugle bull bunch burger burly
+ burrito bus busy butter button buy
+ buyer byte cab cabbage cabinet cable
+ cadet cadmium caesium cake calcium caliper
+ call caller calm calmly camera camp
+ can canary cancel candle candy cap
+ capable caper capital captain car carbon
+ card care career careful carp carpet
+ carrot carry case cash cassava casual
+ cat catch catfish catsear catsup cause
+ cave celery cell century chain chair
+ chalk chance change channel chapter chard
+ charge charity chart check cheddar cheery
+ cheese chicken chicory chiffon child chin
+ chip chives choice chowder chum church
+ circle city claim clam class classic
+ classy clay clean cleaner clear clearly
+ clerk click client climate clock clorine
+ closet clothes cloud clown club clue
+ cluster coach coast coat cobbler cobolt
+ cod code coffee colby cold collar
+ college comb combine comet comfort command
+ comment common company complex concept concern
+ concert conduit consist contact contest context
+ control convert cook cookie copilot copper
+ copy coral cordial corn corner corny
+ correct cost count counter country county
+ couple courage course court cover cow
+ cowbird crab crack craft crash crazy
+ cream credit creek cress crevice crew
+ crimson croaker crop cross crowd cube
+ cuckoo cuisine culture cup current curve
+ cut cyan cycle dagger daily dance
+ dare darter data date day daylily
+ deal dear dearly debate debit decade
+ decimal deep deft deftly degree delay
+ deluxe deposit depth design desk detail
+ device dew diamond diet dig dill
+ dinner dip direct dirt dish disk
+ display diver divide divine doctor dodger
+ donut door dot double dough draft
+ drag dragon drama draw drawer drawing
+ dream drill drink drive driver drop
+ drum dry dryer drywall duck due
+ dump dusk dust duty dye eagle
+ ear earring earth ease east easy
+ eat economy edge editor eel effect
+ effort egg eight elbow elegant element
+ elf elk email emerald employ end
+ endive endless energy engine enjoy enter
+ entry equal equip error escape essay
+ eternal evening event exam example excuse
+ exit expert extent extreme eye face
+ fact factor factual fail failure fair
+ fajita fall family fan fang farm
+ farmer fat fault feature feed feel
+ feeling fench fennel festive few fiber
+ field fig figure file fill film
+ filter final finance finding finger finish
+ fire fish fishing fit fitting five
+ fix flier flight floor floral florine
+ flour flow flower fly flying focus
+ fold folding food foot force forest
+ forever forgive form formal format fortune
+ forum frame free freedom freely fresh
+ friend frog front fruit fuchsia fuel
+ fun funny future gain galaxy gallium
+ game gamma gap garage garden garlic
+ gas gate gather gauge gear gem
+ gene general gentle gently gherkin ghost
+ gift give glad glass gleeful glossy
+ glove glue goal goat goby gold
+ goldeye golf good gouda goulash gourd
+ grab grace grade gram grand grape
+ grapes grass gravy gray great green
+ grits grocery ground group grouper grout
+ growth guard guave guess guest guide
+ guitar gumbo guppy habit hacksaw haddock
+ hafnium hagfish hair half halibut hall
+ hammer hand handle handy hanger happy
+ hat havarti hay haybale head health
+ healthy hearing heart hearty heat heavy
+ heel height helium hello help helpful
+ herald herring hide high highly highway
+ hill hip hipster hire history hit
+ hoki hold hole holiday holly home
+ honest honey hook hope hopeful horizon
+ horn horse host hotel hour house
+ housing human humane humor hunt hurry
+ ice icecube icefish icy idea ideal
+ image impact impress inch income indigo
+ initial inkpen insect inside intense invite
+ iodine iridium iron island issue item
+ ivory jacket jargon javelin jello jelly
+ jewel job jocund join joint joke
+ jovial joy joyful joyous judge juice
+ jump junior jury just justice kale
+ keel keep kelp ketchup key keyhole
+ keyway khaki kick kid kidney kiloohm
+ kind kindly king kitchen kite kiwi
+ knee knife krill krypton kumquat lab
+ lace lack ladder lake lamp lamprey
+ land laser laugh law lawn lawyer
+ layer lead leader leading leaf leafy
+ league leather leave lecture leek leg
+ lemon length lentil lesson let letter
+ lettuce level library life lift light
+ lily lime limit line linen link
+ lip list listen lithium lively living
+ lizard load loan lobster local lock
+ log long longfin look lotus love
+ lovely loving low lucid luck luffa
+ lunch lung machine magenta magnet mail
+ main major make mall manager mango
+ manner many map march market maroon
+ martian master match math matter maximum
+ maybe meal meaning meat media medium
+ meet meeting melody melon member memory
+ mention menu mercury merry mess message
+ messy metal meter method micron middle
+ might mile milk mind mine minimum
+ minnow minor mint minute mirror miss
+ mission misty mix mixer mixture mobile
+ mode model moment monitor monk month
+ moon moray morning most motor mouse
+ mouth move mover movie much mud
+ mudfish muffin mullet munster muon muscle
+ music mustard nail name nation native
+ natural nature navy neat neatly nebula
+ neck needle neon nerve net network
+ neutron news nibble nice nickel night
+ niobium nobody noise noodle normal north
+ nose note nothing notice nova novel
+ number nurse nursery oar object offer
+ office officer oil okay okra old
+ olive one onion open opening opinion
+ option orange orbit orchid order oregano
+ other ounce outcome outside oven owner
+ oxygen oyster pace pack package page
+ pager paint pair pale pan pancake
+ papaya paper pardon parent park parking
+ parsley parsnip part partner party pass
+ passage past pasta path patient pattern
+ pause pay pea peace peach peacock
+ peahen peak peanut pear pearl pen
+ penalty pencil pension people pepper perch
+ perfect period permit person phase phone
+ photo phrase physics piano pick picture
+ pie piece pigeon pike pilot pin
+ pink pinkie pious pipe pitch pizza
+ place plan plane planet plant planter
+ plastic plate play player playful plenty
+ pliers plum pod poem poet poetry
+ point police policy pollock pony pool
+ pop popover poptart pork port portal
+ post pot potato pound powder power
+ present press price pride primary print
+ prior private prize problem process produce
+ product profile profit program project promise
+ prompt proof proper protein proton public
+ puff puffer pull pumpkin pup pupfish
+ pure purple purpose push put quality
+ quark quarter quiet quill quit quote
+ rabbit raccoon race radiant radio radish
+ radium radon rain rainbow raise ramp
+ ranch range rasp rate ratio ray
+ razor reach read reading real reality
+ reason recipe record recover red redeem
+ reed reef refuse region regret regular
+ relaxed release relief relish remote remove
+ rent repair repeat reply report request
+ reserve resist resolve resort rest result
+ return reveal review reward ribbon rice
+ rich ride ridge right ring rise
+ risk river rivet road roast rock
+ rocket role roll roof room rope
+ rose rough roughy round row royal
+ rub ruby rudder ruin rule run
+ runner rush rust sacred saddle safe
+ safety sail salad salami sale salmon
+ salt sample sand sander sandy sauce
+ save saving saw scale scampi scene
+ scheme school score screen script sea
+ search season seat second secret sector
+ seemly self sell senate senior sense
+ series serve set shake shape share
+ shark shell shift shine shiny ship
+ shock shoe shoot shop shovel show
+ side sign signal silk silly silver
+ simple sing singer single sink site
+ size skill skin sky slate sleep
+ sleepy slice slide slip smart smell
+ smelt smile smoke smooth snap snipe
+ snow snowy sock socket sodium soft
+ softly soil sole solid song sorrel
+ sort soul sound soup source south
+ space spare speech speed spell spend
+ sphere spice spider spirit spite split
+ spoon sport spot spray spread spring
+ squab square squash stable staff stage
+ stand staple star start state status
+ stay steak steel step stern stew
+ stick still stock stone stop store
+ storm story strain street stress strike
+ string stroke strong studio study stuff
+ style sugar suit sulfur summer sun
+ sunny sunset super superb surf survey
+ sweet swim swing switch symbol system
+ table tackle tail tale talk tan
+ tank tap tape target task taste
+ tau tea teach teal team tear
+ tell ten tender tennis tent term
+ test tetra text thanks theme theory
+ thing think thread throat thumb ticket
+ tidy tie tiger till time timely
+ tin tip title toast today toe
+ tomato tone tongue tool tooth top
+ topic total touch tough tour towel
+ tower town track trade train trash
+ travel tray treat tree trick trip
+ trout trowel truck trupet trust truth
+ try tube tuna tune turf turkey
+ turn turnip tutor tux tweet twist
+ two type union unique unit upbeat
+ upper use useful user usual valley
+ value van vase vast veil vein
+ velvet verse very vessel vest video
+ view violet visit visual vivid voice
+ volume vowel voyage waffle wait wake
+ walk wall warm warmth wasabi wash
+ watch water wave wax way wealth
+ wear web wedge week weekly weight
+ west whale what wheat wheel when
+ where while who whole why will
+ win wind window wing winner winter
+ wire wish witty wolf wonder wood
+ wool woolly word work worker world
+ worry worth worthy wrap wrench wrist
+ writer xenon yak yam yard yarrow
+ year yearly yellow yew yogurt young
+ youth zebra zephyr zinc zone zoo
+}
+set nwordlist [llength $wordlist]
+
+proc random_char {} {
+ return [string index \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ [expr {int(rand()*52)}]]
+}
+proc random_label {} {
+ set label [random_char]
+ while {rand()>0.8} {
+ append label [random_char]
+ }
+ if {rand()>0.9} {append label -}
+ append label [format %d [expr {int(rand()*100)}]]
+ return $label
+}
+proc random_numeric {} {
+ set n [expr {(rand()*2-1.0)*1e6}]
+ switch [expr {int(rand()*6)}] {
+ 0 {set format %.3f}
+ 1 {set format %.6E}
+ 2 {set format %.4e}
+ default {set format %g}
+ }
+ return [format $format $n]
+}
+
+
+proc random_json {limit indent} {
+ global nwordlist wordlist
+ set res {}
+ if {$indent==0 || ($limit>0 && rand()>0.5)} {
+ incr limit -1
+ incr indent 2
+ set n [expr {int(rand()*5)+1}]
+ if {$n==5} {incr n [expr {int(rand()*10)}]}
+ if {rand()>0.5} {
+ set res \173\n
+ for {set i 0} {$i<$n} {incr i} {
+ append res [string repeat { } $indent]
+ if {rand()>0.8} {
+ if {rand()>0.5} {
+ set sep ":\n [string repeat { } $indent]"
+ } else {
+ set sep " : "
+ }
+ } else {
+ set sep :
+ }
+ append res \"[random_label]\"$sep[random_json $limit $indent]
+ if {$i<$n-1} {append res ,}
+ append res \n
+ }
+ incr indent -2
+ append res [string repeat { } $indent]
+ append res \175
+ return $res
+ } else {
+ set res \[\n
+ for {set i 0} {$i<$n} {incr i} {
+ append res [string repeat { } $indent]
+ append res [random_json $limit $indent]
+ if {$i<$n-1} {append res ,}
+ append res \n
+ }
+ incr indent -2
+ append res [string repeat { } $indent]
+ append res \]
+ return $res
+ }
+ } elseif {rand()>0.9} {
+ if {rand()>0.7} {return "true"}
+ if {rand()>0.5} {return "false"}
+ return "null"
+ } elseif {rand()>0.5} {
+ return [random_numeric]
+ } else {
+ set res \"
+ set n [expr {int(rand()*4)+1}]
+ if {$n>=4} {set n [expr {$n+int(rand()*6)}]}
+ for {set i 0} {$i<$n} {incr i} {
+ if {rand()<0.05} {
+ set w [random_numeric]
+ } else {
+ set k [expr {int(rand()*$nwordlist)}]
+ set w [lindex $wordlist $k]
+ }
+ if {rand()<0.07} {
+ set w \\\"$w\\\"
+ }
+ if {$i<$n-1} {
+ switch [expr {int(rand()*9)}] {
+ 0 {set sp {, }}
+ 1 {set sp "\\n "}
+ 2 {set sp "-"}
+ default {set sp { }}
+ }
+ append res $w$sp
+ } else {
+ append res $w
+ if {rand()<0.2} {append res .}
+ }
+ }
+ return $res\"
+ }
+}
+
+puts "CREATE TABLE IF NOT EXISTS data1(x JSON);"
+puts "BEGIN;"
+set sz 0
+for {set i 0} {$sz<100000000} {incr i} {
+ set j [random_json 7 0]
+ incr sz [string length $j]
+ puts "INSERT INTO data1(x) VALUES('$j');"
+}
+puts "COMMIT;"
+puts "SELECT sum(length(x)) FROM data1;"
diff --git a/test/json/json-q1.txt b/test/json/json-q1.txt
new file mode 100644
index 0000000..d122a2d
--- /dev/null
+++ b/test/json/json-q1.txt
@@ -0,0 +1,24 @@
+.mode qbox
+.timer on
+.param set $label 'q87'
+SELECT rowid, x->>$label FROM data1 WHERE x->>$label IS NOT NULL;
+
+CREATE TEMP TABLE t2(x JSON TEXT);
+WITH RECURSIVE
+ c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<25000),
+ array1(y) AS (
+ SELECT json_group_array(
+ json_object('x',x,'y',random(),'z',hex(randomblob(50)))
+ )
+ FROM c
+ ),
+ c2(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c2 WHERE n<5)
+INSERT INTO t2(x)
+ SELECT json_object('a',n,'b',n*2,'c',y,'d',3,'e',5,'f',6) FROM array1, c2;
+CREATE INDEX t2x1 ON t2(x->>'a');
+CREATE INDEX t2x2 ON t2(x->>'b');
+CREATE INDEX t2x3 ON t2(x->>'e');
+CREATE INDEX t2x4 ON t2(x->>'f');
+UPDATE t2 SET x=json_replace(x,'$.f',(x->>'f')+1);
+UPDATE t2 SET x=json_set(x,'$.e',(x->>'f')-1);
+UPDATE t2 SET x=json_remove(x,'$.d');
diff --git a/test/json/json-speed-check.sh b/test/json/json-speed-check.sh
new file mode 100755
index 0000000..682a7ae
--- /dev/null
+++ b/test/json/json-speed-check.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+#
+# This is a template for a script used for day-to-day size and
+# performance monitoring of SQLite. Typical usage:
+#
+# sh speed-check.sh trunk # Baseline measurement of trunk
+# sh speed-check.sh x1 # Measure some experimental change
+# fossil xdiff --tk jout-trunk.txt jout-x1.txt # View chanages
+#
+# There are multiple output files, all with a base name given by
+# the first argument:
+#
+# summary-$BASE.txt # Copy of standard output
+# jout-$BASE.txt # cachegrind output
+# explain-$BASE.txt # EXPLAIN listings (only with --explain)
+#
+if test "$1" = ""
+then
+ echo "Usage: $0 OUTPUTFILE [OPTIONS]"
+ exit
+fi
+NAME=$1
+shift
+#CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5"
+CC_OPTS="-DSQLITE_ENABLE_MEMSYS5"
+CC=gcc
+LEAN_OPTS="-DSQLITE_THREADSAFE=0"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_MEMSTATUS=0"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_LIKE_DOESNT_MATCH_BLOBS"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_MAX_EXPR_DEPTH=0"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DECLTYPE"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DEPRECATED"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_PROGRESS_CALLBACK"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_SHARED_CACHE"
+LEAN_OPTS="$LEAN_OPTS -DSQLITE_USE_ALLOCA"
+BASELINE="trunk"
+TYPE="json"
+doExplain=0
+doCachegrind=1
+doVdbeProfile=0
+doWal=1
+doDiff=1
+doJsonB=0
+while test "$1" != ""; do
+ case $1 in
+ --nodiff)
+ doDiff=0
+ ;;
+ --lean)
+ CC_OPTS="$CC_OPTS $LEAN_OPTS"
+ ;;
+ --clang)
+ CC=clang
+ ;;
+ --gcc7)
+ CC=gcc-7
+ ;;
+ --jsonb)
+ doJsonB=1
+ TYPE="jsonb"
+ ;;
+ -*)
+ CC_OPTS="$CC_OPTS $1"
+ ;;
+ *)
+ BASELINE=$1
+ ;;
+ esac
+ shift
+done
+echo "NAME = $NAME" | tee summary-$NAME.txt
+echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt
+rm -f cachegrind.out.* jsonshell
+$CC -g -Os -Wall -I. $CC_OPTS ./shell.c ./sqlite3.c -o jsonshell -ldl -lpthread
+ls -l jsonshell | tee -a summary-$NAME.txt
+home=`echo $0 | sed -e 's,/[^/]*$,,'`
+DB=$TYPE''100mb.db
+echo ./jsonshell $DB "<$home/$TYPE-q1.txt"
+valgrind --tool=cachegrind ./jsonshell json100mb_b.db <$home/$TYPE-q1.txt \
+ 2>&1 | tee -a summary-$NAME.txt
+cg_anno.tcl cachegrind.out.* >$TYPE-$NAME.txt
+echo '*****************************************************' >>$TYPE-$NAME.txt
+sed 's/^[0-9=-]\{9\}/==00000==/' summary-$NAME.txt >>$TYPE-$NAME.txt
+if test "$NAME" != "$BASELINE" -a $doDiff -ne 0; then
+ fossil xdiff --tk -c 20 $TYPE-$BASELINE.txt $TYPE-$NAME.txt
+fi
diff --git a/test/json/jsonb-q1.txt b/test/json/jsonb-q1.txt
new file mode 100644
index 0000000..e78c636
--- /dev/null
+++ b/test/json/jsonb-q1.txt
@@ -0,0 +1,24 @@
+.mode qbox
+.timer on
+.param set $label 'q87'
+SELECT rowid, x->>$label FROM data1 WHERE x->>$label IS NOT NULL;
+
+CREATE TEMP TABLE t2(x JSON TEXT);
+WITH RECURSIVE
+ c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<25000),
+ array1(y) AS (
+ SELECT json_group_array(
+ json_object('x',x,'y',random(),'z',hex(randomblob(50)))
+ )
+ FROM c
+ ),
+ c2(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c2 WHERE n<5)
+INSERT INTO t2(x)
+ SELECT jsonb_object('a',n,'b',n*2,'c',y,'d',3,'e',5,'f',6) FROM array1, c2;
+CREATE INDEX t2x1 ON t2(x->>'a');
+CREATE INDEX t2x2 ON t2(x->>'b');
+CREATE INDEX t2x3 ON t2(x->>'e');
+CREATE INDEX t2x4 ON t2(x->>'f');
+UPDATE t2 SET x=jsonb_replace(x,'$.f',(x->>'f')+1);
+UPDATE t2 SET x=jsonb_set(x,'$.e',(x->>'f')-1);
+UPDATE t2 SET x=jsonb_remove(x,'$.d');
diff --git a/test/json101.test b/test/json101.test
new file mode 100644
index 0000000..bae68e7
--- /dev/null
+++ b/test/json101.test
@@ -0,0 +1,1167 @@
+# 2015-08-12
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for JSON SQL functions extension to the
+# SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test json101-1.1.00 {
+ SELECT json_array(1,2.5,null,'hello');
+} {[1,2.5,null,"hello"]}
+do_execsql_test json101-1.1.01 {
+ SELECT json_array(1,'{"abc":2.5,"def":null,"ghi":hello}',99);
+ -- the second term goes in as a string:
+} {[1,"{\\"abc\\":2.5,\\"def\\":null,\\"ghi\\":hello}",99]}
+do_execsql_test json101-1.1.02 {
+ SELECT json_array(1,json('{"abc":2.5,"def":null,"ghi":"hello"}'),99);
+ -- the second term goes in as JSON
+} {[1,{"abc":2.5,"def":null,"ghi":"hello"},99]}
+do_execsql_test json101-1.1.03 {
+ SELECT json_array(1,json_object('abc',2.5,'def',null,'ghi','hello'),99);
+ -- the second term goes in as JSON
+} {[1,{"abc":2.5,"def":null,"ghi":"hello"},99]}
+do_execsql_test json101-1.2 {
+ SELECT hex(json_array('String "\ Test'));
+} {5B22537472696E67205C225C5C2054657374225D}
+do_catchsql_test json101-1.3 {
+ SELECT json_array(1,printf('%.1000c','x'),x'abcd',3);
+} {1 {JSON cannot hold BLOB values}}
+do_catchsql_test json101-1.3b {
+ SELECT jsonb_array(1,printf('%.1000c','x'),x'abcd',3);
+} {1 {JSON cannot hold BLOB values}}
+do_execsql_test json101-1.4 {
+ SELECT json_array(-9223372036854775808,9223372036854775807,0,1,-1,
+ 0.0, 1.0, -1.0, -1e99, +2e100,
+ 'one','two','three',
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, NULL, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 99);
+} {[-9223372036854775808,9223372036854775807,0,1,-1,0.0,1.0,-1.0,-1.0e+99,2.0e+100,"one","two","three",4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,21,22,23,24,25,26,27,28,29,30,31,"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ",99]}
+do_execsql_test json101-1.4b {
+ SELECT json(jsonb_array(-9223372036854775808,9223372036854775807,0,1,-1,
+ 0.0, 1.0, -1.0, -1e99, +2e100,
+ 'one','two','three',
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, NULL, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 99));
+} {[-9223372036854775808,9223372036854775807,0,1,-1,0.0,1.0,-1.0,-1.0e+99,2.0e+100,"one","two","three",4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,21,22,23,24,25,26,27,28,29,30,31,"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ",99]}
+
+do_execsql_test json101-2.1 {
+ SELECT json_object('a',1,'b',2.5,'c',null,'d','String Test');
+} {{{"a":1,"b":2.5,"c":null,"d":"String Test"}}}
+do_execsql_test json101-2.1b {
+ SELECT json(jsonb_object('a',1,'b',2.5,'c',null,'d','String Test'));
+} {{{"a":1,"b":2.5,"c":null,"d":"String Test"}}}
+do_catchsql_test json101-2.2 {
+ SELECT json_object('a',printf('%.1000c','x'),2,2.5);
+} {1 {json_object() labels must be TEXT}}
+do_catchsql_test json101-2.2b {
+ SELECT jsonb_object('a',printf('%.1000c','x'),2,2.5);
+} {1 {json_object() labels must be TEXT}}
+do_execsql_test json101-2.2.2 {
+ SELECT json_object('a',json_array('xyx',77,4.5),'x',2.5);
+} {{{"a":["xyx",77,4.5],"x":2.5}}}
+do_execsql_test json101-2.2.2b {
+ SELECT json(jsonb_object('a',json_array('xyx',77,4.5),'x',2.5));
+} {{{"a":["xyx",77,4.5],"x":2.5}}}
+do_execsql_test json101-2.2.3 {
+ SELECT json_object('a',jsonb_array('xyx',77,4.5),'x',2.5);
+} {{{"a":["xyx",77,4.5],"x":2.5}}}
+do_execsql_test json101-2.2.3b {
+ SELECT json(jsonb_object('a',jsonb_array('xyx',77,4.5),'x',2.5));
+} {{{"a":["xyx",77,4.5],"x":2.5}}}
+do_catchsql_test json101-2.3 {
+ SELECT json_object('a',1,'b');
+} {1 {json_object() requires an even number of arguments}}
+do_catchsql_test json101-2.4 {
+ SELECT json_object('a',printf('%.1000c','x'),'b',x'abcd');
+} {1 {JSON cannot hold BLOB values}}
+do_execsql_test json101-2.5 {
+ SELECT json_object('a',printf('%.10c','x'),'b',jsonb_array(1,2,3));
+} {{{"a":"xxxxxxxxxx","b":[1,2,3]}}}
+
+do_execsql_test json101-3.1 {
+ SELECT json_replace('{"a":1,"b":2}','$.a','[3,4,5]');
+} {{{"a":"[3,4,5]","b":2}}}
+do_execsql_test json101-3.1b {
+ SELECT json(jsonb_replace('{"a":1,"b":2}','$.a','[3,4,5]'));
+} {{{"a":"[3,4,5]","b":2}}}
+do_execsql_test json101-3.2 {
+ SELECT json_replace('{"a":1,"b":2}','$.a',json('[3,4,5]'));
+} {{{"a":[3,4,5],"b":2}}}
+do_execsql_test json101-3.2b {
+ SELECT json_replace('{"a":1,"b":2}','$.a',jsonb('[3,4,5]'));
+} {{{"a":[3,4,5],"b":2}}}
+do_execsql_test json101-3.3 {
+ SELECT json_type(json_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b');
+} {text}
+do_execsql_test json101-3.3b {
+ SELECT json_type(jsonb_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b');
+} {text}
+do_execsql_test json101-3.4 {
+ SELECT json_type(json_set('{"a":1,"b":2}','$.b',json('{"x":3,"y":4}')),'$.b');
+} {object}
+do_execsql_test json101-3.4b {
+ SELECT json_type(jsonb_set('{"a":1,"b":2}','$.b',jsonb('{"x":3,"y":4}')),'$.b');
+} {object}
+ifcapable vtab {
+ do_execsql_test json101-3.5 {
+ SELECT fullkey, atom, '|' FROM json_tree(json_set('{}','$.x',123,'$.x',456));
+ } {{$} {} | {$.x} 456 |}
+ do_execsql_test json101-3.5b {
+ SELECT fullkey, atom, '|' FROM json_tree(jsonb_set('{}','$.x',123,'$.x',456));
+ } {{$} {} | {$.x} 456 |}
+}
+
+# Per rfc7159, any JSON value is allowed at the top level, and whitespace
+# is permitting before and/or after that value.
+#
+do_execsql_test json101-4.1 {
+ CREATE TABLE j1(x);
+ INSERT INTO j1(x)
+ VALUES('true'),('false'),('null'),('123'),('-234'),('34.5e+6'),
+ ('""'),('"\""'),('"\\"'),('"abcdefghijlmnopqrstuvwxyz"'),
+ ('[]'),('{}'),('[true,false,null,123,-234,34.5e+6,{},[]]'),
+ ('{"a":true,"b":{"c":false}}');
+ SELECT * FROM j1 WHERE NOT json_valid(x);
+} {}
+do_execsql_test json101-4.2 {
+ SELECT * FROM j1 WHERE NOT json_valid(char(0x20,0x09,0x0a,0x0d)||x);
+} {}
+do_execsql_test json101-4.3 {
+ SELECT * FROM j1 WHERE NOT json_valid(x||char(0x20,0x09,0x0a,0x0d));
+} {}
+
+# But an empty string, or a string of pure whitespace is not valid JSON.
+#
+do_execsql_test json101-4.4 {
+ SELECT json_valid(''), json_valid(char(0x20,0x09,0x0a,0x0d));
+} {0 0}
+
+# json_remove() and similar functions with no edit operations return their
+# input unchanged.
+#
+do_execsql_test json101-4.5 {
+ SELECT x FROM j1 WHERE json_remove(x)<>x;
+} {}
+do_execsql_test json101-4.6 {
+ SELECT x FROM j1 WHERE json_replace(x)<>x;
+} {}
+do_execsql_test json101-4.7 {
+ SELECT x FROM j1 WHERE json_set(x)<>x;
+} {}
+do_execsql_test json101-4.8 {
+ SELECT x FROM j1 WHERE json_insert(x)<>x;
+} {}
+do_execsql_test json101-4.9 {
+ SELECT json_insert('{"a":1}','$.b',CAST(x'0000' AS text));
+} {{{"a":1,"b":"\u0000\u0000"}}}
+
+# json_extract(JSON,'$') will return objects and arrays without change.
+#
+do_execsql_test json101-4.10 {
+ SELECT count(*) FROM j1 WHERE json_type(x) IN ('object','array');
+ SELECT x FROM j1
+ WHERE json_extract(x,'$')<>x
+ AND json_type(x) IN ('object','array');
+} {4}
+do_execsql_test json101-4.10b {
+ CREATE TABLE j1b AS SELECT jsonb(x) AS "x" FROM j1;
+ SELECT count(*) FROM j1b WHERE json_type(x) IN ('object','array');
+ SELECT json(x) FROM j1b
+ WHERE json_extract(x,'$')<>json(x)
+ AND json_type(x) IN ('object','array');
+} {4}
+
+do_execsql_test json101-5.1 {
+ CREATE TABLE j2(id INTEGER PRIMARY KEY, json, src);
+ INSERT INTO j2(id,json,src)
+ VALUES(1,'{
+ "firstName": "John",
+ "lastName": "Smith",
+ "isAlive": true,
+ "age": 25,
+ "address": {
+ "streetAddress": "21 2nd Street",
+ "city": "New York",
+ "state": "NY",
+ "postalCode": "10021-3100"
+ },
+ "phoneNumbers": [
+ {
+ "type": "home",
+ "number": "212 555-1234"
+ },
+ {
+ "type": "office",
+ "number": "646 555-4567"
+ }
+ ],
+ "children": [],
+ "spouse": null
+ }','https://en.wikipedia.org/wiki/JSON');
+ INSERT INTO j2(id,json,src)
+ VALUES(2, '{
+ "id": "0001",
+ "type": "donut",
+ "name": "Cake",
+ "ppu": 0.55,
+ "batters":
+ {
+ "batter":
+ [
+ { "id": "1001", "type": "Regular" },
+ { "id": "1002", "type": "Chocolate" },
+ { "id": "1003", "type": "Blueberry" },
+ { "id": "1004", "type": "Devil''s Food" }
+ ]
+ },
+ "topping":
+ [
+ { "id": "5001", "type": "None" },
+ { "id": "5002", "type": "Glazed" },
+ { "id": "5005", "type": "Sugar" },
+ { "id": "5007", "type": "Powdered Sugar" },
+ { "id": "5006", "type": "Chocolate with Sprinkles" },
+ { "id": "5003", "type": "Chocolate" },
+ { "id": "5004", "type": "Maple" }
+ ]
+ }','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html');
+ INSERT INTO j2(id,json,src)
+ VALUES(3,'[
+ {
+ "id": "0001",
+ "type": "donut",
+ "name": "Cake",
+ "ppu": 0.55,
+ "batters":
+ {
+ "batter":
+ [
+ { "id": "1001", "type": "Regular" },
+ { "id": "1002", "type": "Chocolate" },
+ { "id": "1003", "type": "Blueberry" },
+ { "id": "1004", "type": "Devil''s Food" }
+ ]
+ },
+ "topping":
+ [
+ { "id": "5001", "type": "None" },
+ { "id": "5002", "type": "Glazed" },
+ { "id": "5005", "type": "Sugar" },
+ { "id": "5007", "type": "Powdered Sugar" },
+ { "id": "5006", "type": "Chocolate with Sprinkles" },
+ { "id": "5003", "type": "Chocolate" },
+ { "id": "5004", "type": "Maple" }
+ ]
+ },
+ {
+ "id": "0002",
+ "type": "donut",
+ "name": "Raised",
+ "ppu": 0.55,
+ "batters":
+ {
+ "batter":
+ [
+ { "id": "1001", "type": "Regular" }
+ ]
+ },
+ "topping":
+ [
+ { "id": "5001", "type": "None" },
+ { "id": "5002", "type": "Glazed" },
+ { "id": "5005", "type": "Sugar" },
+ { "id": "5003", "type": "Chocolate" },
+ { "id": "5004", "type": "Maple" }
+ ]
+ },
+ {
+ "id": "0003",
+ "type": "donut",
+ "name": "Old Fashioned",
+ "ppu": 0.55,
+ "batters":
+ {
+ "batter":
+ [
+ { "id": "1001", "type": "Regular" },
+ { "id": "1002", "type": "Chocolate" }
+ ]
+ },
+ "topping":
+ [
+ { "id": "5001", "type": "None" },
+ { "id": "5002", "type": "Glazed" },
+ { "id": "5003", "type": "Chocolate" },
+ { "id": "5004", "type": "Maple" }
+ ]
+ }
+ ]','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html');
+ SELECT count(*) FROM j2;
+ CREATE TABLE j2b(id INTEGER PRIMARY KEY, json, src);
+ INSERT INTO J2b(id,json,src) SELECT id, jsonb(json), src FROM j2;
+ SELECT count(*) FROM j2b;
+} {3 3}
+
+do_execsql_test json101-5.2 {
+ SELECT id, json_valid(json), json_type(json), '|' FROM j2 ORDER BY id;
+} {1 1 object | 2 1 object | 3 1 array |}
+do_execsql_test json101-5.2b {
+ SELECT id, json_valid(json,5), json_type(json), '|' FROM j2b ORDER BY id;
+} {1 1 object | 2 1 object | 3 1 array |}
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+# fullkey is always the same as path+key (with appropriate formatting)
+#
+do_execsql_test json101-5.3 {
+ SELECT j2.rowid, jx.rowid, fullkey, path, key
+ FROM j2, json_tree(j2.json) AS jx
+ WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']'
+ ELSE '.'||key END);
+} {}
+do_execsql_test json101-5.3b {
+ SELECT j2b.rowid, jx.rowid, fullkey, path, key
+ FROM j2b, json_tree(j2b.json) AS jx
+ WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']'
+ ELSE '.'||key END);
+} {}
+do_execsql_test json101-5.4 {
+ SELECT j2.rowid, jx.rowid, fullkey, path, key
+ FROM j2, json_each(j2.json) AS jx
+ WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']'
+ ELSE '.'||key END);
+} {}
+
+
+# Verify that the json_each.json and json_tree.json output is always the
+# same as input.
+#
+do_execsql_test json101-5.5 {
+ SELECT j2.rowid, jx.rowid, fullkey, path, key
+ FROM j2, json_each(j2.json) AS jx
+ WHERE jx.json<>j2.json;
+} {}
+do_execsql_test json101-5.6 {
+ SELECT j2.rowid, jx.rowid, fullkey, path, key
+ FROM j2, json_tree(j2.json) AS jx
+ WHERE jx.json<>j2.json;
+} {}
+do_execsql_test json101-5.7 {
+ SELECT j2.rowid, jx.rowid, fullkey, path, key
+ FROM j2, json_each(j2.json) AS jx
+ WHERE jx.value<>jx.atom AND type NOT IN ('array','object');
+} {}
+do_execsql_test json101-5.8 {
+ SELECT j2.rowid, jx.rowid, fullkey, path, key
+ FROM j2, json_tree(j2.json) AS jx
+ WHERE jx.value<>jx.atom AND type NOT IN ('array','object');
+} {}
+
+do_execsql_test json101-6.1 {
+ SELECT json_valid('{"a":55,"b":72,}');
+} {0}
+do_execsql_test json101-6.2 {
+ SELECT json_error_position('{"a":55,"b":72,}');
+} {0}
+do_execsql_test json101-6.3 {
+ SELECT json_valid(json('{"a":55,"b":72,}'));
+} {1}
+do_execsql_test json101-6.4 {
+ SELECT json_valid('{"a":55,"b":72 , }');
+} {0}
+do_execsql_test json101-6.5 {
+ SELECT json_error_position('{"a":55,"b":72 , }');
+} {0}
+do_execsql_test json101-6.6 {
+ SELECT json_error_position('{"a":55,"b":72,,}');
+} {16}
+do_execsql_test json101-6.7 {
+ SELECT json_valid('{"a":55,"b":72}');
+} {1}
+do_execsql_test json101-6.8 {
+ SELECT json_error_position('["a",55,"b",72,]');
+} {0}
+do_execsql_test json101-6.9 {
+ SELECT json_error_position('["a",55,"b",72 , ]');
+} {0}
+do_execsql_test json101-6.10 {
+ SELECT json_error_position('["a",55,"b",72,,]');
+} {16}
+do_execsql_test json101-6.11 {
+ SELECT json_valid('["a",55,"b",72]');
+} {1}
+
+# White-space tests. Note that form-feed is not white-space in JSON.
+# ticket [57eec374ae1d0a1d4a23077a95f4e173fe269113]
+#
+foreach {tn isvalid ws} {
+ 7.1 1 char(0x20)
+ 7.2 1 char(0x09)
+ 7.3 1 char(0x0A)
+ 7.4 1 char(0x0D)
+ 7.5 0 char(0x0C)
+ 7.6 1 char(0x20,0x09,0x0a,0x0d,0x20)
+ 7.7 0 char(0x20,0x09,0x0a,0x0c,0x0d,0x20)
+} {
+ do_execsql_test json101-$tn.1 \
+ "SELECT json_valid(printf('%s{%s\"x\"%s:%s9%s}%s',
+ $::ws,$::ws,$::ws,$::ws,$::ws,$::ws));" \
+ $isvalid
+}
+
+# Ticket https://www.sqlite.org/src/info/ad2559db380abf8e
+# Control characters must be escaped in JSON strings.
+#
+do_execsql_test json101-8.1 {
+ DROP TABLE IF EXISTS t8;
+ CREATE TABLE t8(a,b);
+ INSERT INTO t8(a) VALUES('abc' || char(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35) || 'xyz');
+ UPDATE t8 SET b=json_array(a);
+ SELECT b FROM t8;
+} {{["abc\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#xyz"]}}
+do_execsql_test json101-8.1b {
+ DROP TABLE IF EXISTS t8;
+ CREATE TABLE t8(a,b);
+ INSERT INTO t8(a) VALUES('abc' || char(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35) || 'xyz');
+ UPDATE t8 SET b=jsonb_array(a);
+ SELECT json(b) FROM t8;
+} {{["abc\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#xyz"]}}
+do_execsql_test json101-8.2 {
+ SELECT a=json_extract(b,'$[0]') FROM t8;
+} {1}
+
+# 2017-04-12. Regression reported on the mailing list by Rolf Ade
+#
+do_execsql_test json101-8.3 {
+ SELECT json_valid(char(0x22,0xe4,0x22));
+} {1}
+do_execsql_test json101-8.4 {
+ SELECT unicode(json_extract(char(0x22,228,0x22),'$'));
+} {228}
+
+# The json_quote() function transforms an SQL value into a JSON value.
+# String values are quoted and interior quotes are escaped. NULL values
+# are rendered as the unquoted string "null".
+#
+do_execsql_test json101-9.1 {
+ SELECT json_quote('abc"xyz');
+} {{"abc\"xyz"}}
+do_execsql_test json101-9.2 {
+ SELECT json_quote(3.14159);
+} {3.14159}
+do_execsql_test json101-9.3 {
+ SELECT json_quote(12345);
+} {12345}
+do_execsql_test json101-9.4 {
+ SELECT json_quote(null);
+} {"null"}
+do_catchsql_test json101-9.5 {
+ SELECT json_quote(x'3031323334');
+} {1 {JSON cannot hold BLOB values}}
+do_catchsql_test json101-9.6 {
+ SELECT json_quote(123,456)
+} {1 {wrong number of arguments to function json_quote()}}
+do_catchsql_test json101-9.7 {
+ SELECT json_quote()
+} {1 {wrong number of arguments to function json_quote()}}
+
+# Make sure only valid backslash-escapes are accepted.
+#
+do_execsql_test json101-10.1 {
+ SELECT json_valid('" \ "');
+} {0}
+do_execsql_test json101-10.2 {
+ SELECT json_valid('" \! "');
+} {0}
+do_execsql_test json101-10.3 {
+ SELECT json_valid('" \" "');
+} {1}
+do_execsql_test json101-10.4 {
+ SELECT json_valid('" \# "');
+} {0}
+do_execsql_test json101-10.5 {
+ SELECT json_valid('" \$ "');
+} {0}
+do_execsql_test json101-10.6 {
+ SELECT json_valid('" \% "');
+} {0}
+do_execsql_test json101-10.7 {
+ SELECT json_valid('" \& "');
+} {0}
+do_execsql_test json101-10.8 {
+ SELECT json_valid('" \'' "');
+} {0}
+do_execsql_test json101-10.9 {
+ SELECT json_valid('" \( "');
+} {0}
+do_execsql_test json101-10.10 {
+ SELECT json_valid('" \) "');
+} {0}
+do_execsql_test json101-10.11 {
+ SELECT json_valid('" \* "');
+} {0}
+do_execsql_test json101-10.12 {
+ SELECT json_valid('" \+ "');
+} {0}
+do_execsql_test json101-10.13 {
+ SELECT json_valid('" \, "');
+} {0}
+do_execsql_test json101-10.14 {
+ SELECT json_valid('" \- "');
+} {0}
+do_execsql_test json101-10.15 {
+ SELECT json_valid('" \. "');
+} {0}
+do_execsql_test json101-10.16 {
+ SELECT json_valid('" \/ "');
+} {1}
+do_execsql_test json101-10.17 {
+ SELECT json_valid('" \0 "');
+} {0}
+do_execsql_test json101-10.18 {
+ SELECT json_valid('" \1 "');
+} {0}
+do_execsql_test json101-10.19 {
+ SELECT json_valid('" \2 "');
+} {0}
+do_execsql_test json101-10.20 {
+ SELECT json_valid('" \3 "');
+} {0}
+do_execsql_test json101-10.21 {
+ SELECT json_valid('" \4 "');
+} {0}
+do_execsql_test json101-10.22 {
+ SELECT json_valid('" \5 "');
+} {0}
+do_execsql_test json101-10.23 {
+ SELECT json_valid('" \6 "');
+} {0}
+do_execsql_test json101-10.24 {
+ SELECT json_valid('" \7 "');
+} {0}
+do_execsql_test json101-10.25 {
+ SELECT json_valid('" \8 "');
+} {0}
+do_execsql_test json101-10.26 {
+ SELECT json_valid('" \9 "');
+} {0}
+do_execsql_test json101-10.27 {
+ SELECT json_valid('" \: "');
+} {0}
+do_execsql_test json101-10.28 {
+ SELECT json_valid('" \; "');
+} {0}
+do_execsql_test json101-10.29 {
+ SELECT json_valid('" \< "');
+} {0}
+do_execsql_test json101-10.30 {
+ SELECT json_valid('" \= "');
+} {0}
+do_execsql_test json101-10.31 {
+ SELECT json_valid('" \> "');
+} {0}
+do_execsql_test json101-10.32 {
+ SELECT json_valid('" \? "');
+} {0}
+do_execsql_test json101-10.33 {
+ SELECT json_valid('" \@ "');
+} {0}
+do_execsql_test json101-10.34 {
+ SELECT json_valid('" \A "');
+} {0}
+do_execsql_test json101-10.35 {
+ SELECT json_valid('" \B "');
+} {0}
+do_execsql_test json101-10.36 {
+ SELECT json_valid('" \C "');
+} {0}
+do_execsql_test json101-10.37 {
+ SELECT json_valid('" \D "');
+} {0}
+do_execsql_test json101-10.38 {
+ SELECT json_valid('" \E "');
+} {0}
+do_execsql_test json101-10.39 {
+ SELECT json_valid('" \F "');
+} {0}
+do_execsql_test json101-10.40 {
+ SELECT json_valid('" \G "');
+} {0}
+do_execsql_test json101-10.41 {
+ SELECT json_valid('" \H "');
+} {0}
+do_execsql_test json101-10.42 {
+ SELECT json_valid('" \I "');
+} {0}
+do_execsql_test json101-10.43 {
+ SELECT json_valid('" \J "');
+} {0}
+do_execsql_test json101-10.44 {
+ SELECT json_valid('" \K "');
+} {0}
+do_execsql_test json101-10.45 {
+ SELECT json_valid('" \L "');
+} {0}
+do_execsql_test json101-10.46 {
+ SELECT json_valid('" \M "');
+} {0}
+do_execsql_test json101-10.47 {
+ SELECT json_valid('" \N "');
+} {0}
+do_execsql_test json101-10.48 {
+ SELECT json_valid('" \O "');
+} {0}
+do_execsql_test json101-10.49 {
+ SELECT json_valid('" \P "');
+} {0}
+do_execsql_test json101-10.50 {
+ SELECT json_valid('" \Q "');
+} {0}
+do_execsql_test json101-10.51 {
+ SELECT json_valid('" \R "');
+} {0}
+do_execsql_test json101-10.52 {
+ SELECT json_valid('" \S "');
+} {0}
+do_execsql_test json101-10.53 {
+ SELECT json_valid('" \T "');
+} {0}
+do_execsql_test json101-10.54 {
+ SELECT json_valid('" \U "');
+} {0}
+do_execsql_test json101-10.55 {
+ SELECT json_valid('" \V "');
+} {0}
+do_execsql_test json101-10.56 {
+ SELECT json_valid('" \W "');
+} {0}
+do_execsql_test json101-10.57 {
+ SELECT json_valid('" \X "');
+} {0}
+do_execsql_test json101-10.58 {
+ SELECT json_valid('" \Y "');
+} {0}
+do_execsql_test json101-10.59 {
+ SELECT json_valid('" \Z "');
+} {0}
+do_execsql_test json101-10.60 {
+ SELECT json_valid('" \[ "');
+} {0}
+do_execsql_test json101-10.61 {
+ SELECT json_valid('" \\ "');
+} {1}
+do_execsql_test json101-10.62 {
+ SELECT json_valid('" \] "');
+} {0}
+do_execsql_test json101-10.63 {
+ SELECT json_valid('" \^ "');
+} {0}
+do_execsql_test json101-10.64 {
+ SELECT json_valid('" \_ "');
+} {0}
+do_execsql_test json101-10.65 {
+ SELECT json_valid('" \` "');
+} {0}
+do_execsql_test json101-10.66 {
+ SELECT json_valid('" \a "');
+} {0}
+do_execsql_test json101-10.67 {
+ SELECT json_valid('" \b "');
+} {1}
+do_execsql_test json101-10.68 {
+ SELECT json_valid('" \c "');
+} {0}
+do_execsql_test json101-10.69 {
+ SELECT json_valid('" \d "');
+} {0}
+do_execsql_test json101-10.70 {
+ SELECT json_valid('" \e "');
+} {0}
+do_execsql_test json101-10.71 {
+ SELECT json_valid('" \f "');
+} {1}
+do_execsql_test json101-10.72 {
+ SELECT json_valid('" \g "');
+} {0}
+do_execsql_test json101-10.73 {
+ SELECT json_valid('" \h "');
+} {0}
+do_execsql_test json101-10.74 {
+ SELECT json_valid('" \i "');
+} {0}
+do_execsql_test json101-10.75 {
+ SELECT json_valid('" \j "');
+} {0}
+do_execsql_test json101-10.76 {
+ SELECT json_valid('" \k "');
+} {0}
+do_execsql_test json101-10.77 {
+ SELECT json_valid('" \l "');
+} {0}
+do_execsql_test json101-10.78 {
+ SELECT json_valid('" \m "');
+} {0}
+do_execsql_test json101-10.79 {
+ SELECT json_valid('" \n "');
+} {1}
+do_execsql_test json101-10.80 {
+ SELECT json_valid('" \o "');
+} {0}
+do_execsql_test json101-10.81 {
+ SELECT json_valid('" \p "');
+} {0}
+do_execsql_test json101-10.82 {
+ SELECT json_valid('" \q "');
+} {0}
+do_execsql_test json101-10.83 {
+ SELECT json_valid('" \r "');
+} {1}
+do_execsql_test json101-10.84 {
+ SELECT json_valid('" \s "');
+} {0}
+do_execsql_test json101-10.85 {
+ SELECT json_valid('" \t "');
+} {1}
+do_execsql_test json101-10.86.0 {
+ SELECT json_valid('" \u "');
+} {0}
+do_execsql_test json101-10.86.1 {
+ SELECT json_valid('" \ua "');
+} {0}
+do_execsql_test json101-10.86.2 {
+ SELECT json_valid('" \uab "');
+} {0}
+do_execsql_test json101-10.86.3 {
+ SELECT json_valid('" \uabc "');
+} {0}
+do_execsql_test json101-10.86.4 {
+ SELECT json_valid('" \uabcd "');
+} {1}
+do_execsql_test json101-10.86.5 {
+ SELECT json_valid('" \uFEDC "');
+} {1}
+do_execsql_test json101-10.86.6 {
+ SELECT json_valid('" \u1234 "');
+} {1}
+do_execsql_test json101-10.87 {
+ SELECT json_valid('" \v "');
+} {0}
+do_execsql_test json101-10.88 {
+ SELECT json_valid('" \w "');
+} {0}
+do_execsql_test json101-10.89 {
+ SELECT json_valid('" \x "');
+} {0}
+do_execsql_test json101-10.90 {
+ SELECT json_valid('" \y "');
+} {0}
+do_execsql_test json101-10.91 {
+ SELECT json_valid('" \z "');
+} {0}
+do_execsql_test json101-10.92 {
+ SELECT json_valid('" \{ "');
+} {0}
+do_execsql_test json101-10.93 {
+ SELECT json_valid('" \| "');
+} {0}
+do_execsql_test json101-10.94 {
+ SELECT json_valid('" \} "');
+} {0}
+do_execsql_test json101-10.95 {
+ SELECT json_valid('" \~ "');
+} {0}
+
+#--------------------------------------------------------------------------
+# 2017-04-11. https://www.sqlite.org/src/info/981329adeef51011
+# Stack overflow on deeply nested JSON.
+#
+# The following tests confirm that deeply nested JSON is considered invalid.
+#
+do_execsql_test json101-11.0 {
+ /* Shallow enough to be parsed */
+ SELECT json_valid(printf('%.1000c0%.1000c','[',']'));
+} {1}
+do_execsql_test json101-11.1 {
+ /* Too deep by one */
+ SELECT json_valid(printf('%.1001c0%.1001c','[',']'));
+} {0}
+do_execsql_test json101-11.2 {
+ /* Shallow enough to be parsed { */
+ SELECT json_valid(replace(printf('%.1000c0%.1000c','[','}'),'[','{"a":'));
+ /* } */
+} {1}
+do_execsql_test json101-11.3 {
+ /* Too deep by one { */
+ SELECT json_valid(replace(printf('%.1001c0%.1001c','[','}'),'[','{"a":'));
+ /* } */
+} {0}
+
+# 2017-10-27. Demonstrate the ability to access an element from
+# a json structure even though the element name constains a "."
+# character, by quoting the element name in the path.
+#
+do_execsql_test json101-12.100 {
+ CREATE TABLE t12(x);
+ INSERT INTO t12(x) VALUES(
+ '{"settings":
+ {"layer2":
+ {"hapax.legomenon":
+ {"forceDisplay":true,
+ "transliterate":true,
+ "add.footnote":true,
+ "summary.report":true},
+ "dis.legomenon":
+ {"forceDisplay":true,
+ "transliterate":false,
+ "add.footnote":false,
+ "summary.report":true},
+ "tris.legomenon":
+ {"forceDisplay":true,
+ "transliterate":false,
+ "add.footnote":false,
+ "summary.report":false}
+ }
+ }
+ }');
+} {}
+
+do_execsql_test json101-12.110 {
+ SELECT json_remove(x, '$.settings.layer2."dis.legomenon".forceDisplay')
+ FROM t12;
+} {{{"settings":{"layer2":{"hapax.legomenon":{"forceDisplay":true,"transliterate":true,"add.footnote":true,"summary.report":true},"dis.legomenon":{"transliterate":false,"add.footnote":false,"summary.report":true},"tris.legomenon":{"forceDisplay":true,"transliterate":false,"add.footnote":false,"summary.report":false}}}}}}
+do_execsql_test json101-12.110b {
+ SELECT json_remove(jsonb(x), '$.settings.layer2."dis.legomenon".forceDisplay')
+ FROM t12;
+} {{{"settings":{"layer2":{"hapax.legomenon":{"forceDisplay":true,"transliterate":true,"add.footnote":true,"summary.report":true},"dis.legomenon":{"transliterate":false,"add.footnote":false,"summary.report":true},"tris.legomenon":{"forceDisplay":true,"transliterate":false,"add.footnote":false,"summary.report":false}}}}}}
+do_execsql_test json101-12.120 {
+ SELECT json_extract(x, '$.settings.layer2."tris.legomenon"."summary.report"')
+ FROM t12;
+} {0}
+do_execsql_test json101-12.120b {
+ SELECT json_extract(jsonb(x), '$.settings.layer2."tris.legomenon"."summary.report"')
+ FROM t12;
+} {0}
+
+# 2018-01-26
+# ticket https://www.sqlite.org/src/tktview/80177f0c226ff54f6ddd41
+# Make sure the query planner knows about the arguments to table-valued functions.
+#
+do_execsql_test json101-13.100 {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE TABLE t1(id, json);
+ INSERT INTO t1(id,json) VALUES(1,'{"items":[3,5]}');
+ CREATE TABLE t2(id, json);
+ INSERT INTO t2(id,json) VALUES(2,'{"value":2}');
+ INSERT INTO t2(id,json) VALUES(3,'{"value":3}');
+ INSERT INTO t2(id,json) VALUES(4,'{"value":4}');
+ INSERT INTO t2(id,json) VALUES(5,'{"value":5}');
+ INSERT INTO t2(id,json) VALUES(6,'{"value":6}');
+ SELECT * FROM t1 CROSS JOIN t2
+ WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z
+ WHERE Z.value==t2.id);
+} {1 {{"items":[3,5]}} 3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}}}
+do_execsql_test json101-13.110 {
+ SELECT * FROM t2 CROSS JOIN t1
+ WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z
+ WHERE Z.value==t2.id);
+} {3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}} 1 {{"items":[3,5]}}}
+
+# 2018-05-16
+# Incorrect fullkey output from json_each()
+# when the input JSON is not an array or object.
+#
+do_execsql_test json101-14.100 {
+ SELECT fullkey FROM json_each('123');
+} {$}
+do_execsql_test json101-14.110 {
+ SELECT fullkey FROM json_each('123.56');
+} {$}
+do_execsql_test json101-14.120 {
+ SELECT fullkey FROM json_each('"hello"');
+} {$}
+do_execsql_test json101-14.130 {
+ SELECT fullkey FROM json_each('null');
+} {$}
+do_execsql_test json101-14.140 {
+ SELECT fullkey FROM json_tree('123');
+} {$}
+do_execsql_test json101-14.150 {
+ SELECT fullkey FROM json_tree('123.56');
+} {$}
+do_execsql_test json101-14.160 {
+ SELECT fullkey FROM json_tree('"hello"');
+} {$}
+do_execsql_test json101-14.170 {
+ SELECT fullkey FROM json_tree('null');
+} {$}
+
+# 2018-12-03
+# Make sure the table-valued functions contained within parentheses
+# work correctly.
+#
+# Bug reported via private email. See TH3 for more information.
+#
+do_execsql_test json101-15.100 {
+ SELECT * FROM JSON_EACH('{"a":1, "b":2}');
+} {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}}
+do_execsql_test json101-15.110 {
+ SELECT xyz.* FROM JSON_EACH('{"a":1, "b":2}') AS xyz;
+} {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}}
+do_execsql_test json101-15.120 {
+ SELECT * FROM (JSON_EACH('{"a":1, "b":2}'));
+} {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}}
+do_execsql_test json101-15.130 {
+ SELECT xyz.* FROM (JSON_EACH('{"a":1, "b":2}')) AS xyz;
+} {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}}
+
+# 2019-11-10
+# Mailing list bug report on the handling of surrogate pairs
+# in JSON.
+#
+do_execsql_test json101-16.10 {
+ SELECT length(json_extract('"abc\uD834\uDD1Exyz"','$'));
+} {7}
+do_execsql_test json101-16.20 {
+ SELECT length(json_extract('"\uD834\uDD1E"','$'));
+} {1}
+do_execsql_test json101-16.30 {
+ SELECT unicode(json_extract('"\uD834\uDD1E"','$'));
+} {119070}
+
+# 2022-01-30 dbsqlfuzz 4678cf825d27f87c9b8343720121e12cf944b71a
+do_execsql_test json101-17.1 {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE TABLE t1(a,b,c);
+ CREATE TABLE t2(d);
+ SELECT * FROM t1 LEFT JOIN t2 ON (SELECT b FROM json_each ORDER BY 1);
+} {}
+
+# 2022-04-04 forum post https://sqlite.org/forum/forumpost/c082aeab43
+do_execsql_test json101-18.1 {
+ SELECT json_valid('{"":5}');
+} {1}
+do_execsql_test json101-18.2 {
+ SELECT json_extract('{"":5}', '$.""');
+} {5}
+do_execsql_test json101-18.3 {
+ SELECT json_extract('[3,{"a":4,"":[5,{"hi":6},7]},8]', '$[1].""[1].hi');
+} {6}
+do_execsql_test json101-18.4 {
+ SELECT json_extract('[3,{"a":4,"":[5,{"hi":6},7]},8]', '$[1].""[1]."hi"');
+} {6}
+do_catchsql_test json101-18.5 {
+ SELECT json_extract('{"":8}', '$.');
+} {1 {bad JSON path: '$.'}}
+
+# 2022-08-29 https://sqlite.org/forum/forumpost/9b9e4716c0d7bbd1
+# This is not a problem specifically with JSON functions. It is
+# a problem with transaction control. But the json() function makes
+# the problem more easily accessible, so it is tested here.
+#
+do_execsql_test json101-19.1 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(x);
+} {}
+do_catchsql_test json101-19.2 {
+ BEGIN;
+ INSERT INTO t1 VALUES(0), (json('not-valid-json'));
+} {1 {malformed JSON}}
+do_execsql_test json101-19.3 {
+ COMMIT;
+ SELECT * FROM t1;
+} {}
+
+# 2023-03-17 positive and negative infinities
+#
+do_execsql_test json101-20.1 {
+ SELECT json_object('a',2e370,'b',-3e380);
+} {{{"a":9.0e+999,"b":-9.0e+999}}}
+do_execsql_test json101-20.2 {
+ SELECT json_object('a',2e370,'b',-3e380)->>'a';
+} Inf
+do_execsql_test json101-20.3 {
+ SELECT json_object('a',2e370,'b',-3e380)->>'b';
+} {-Inf}
+
+# 2023-05-02 https://sqlite.org/forum/forumpost/06c6334412
+# JSON functions should normally return NULL when given
+# a NULL value as the JSON input.
+#
+db null NULL
+if {[db exists {SELECT * FROM pragma_compile_options WHERE compile_options LIKE '%legacy_json_valid%'}]} {
+ do_execsql_test json101-21.1-legacy {
+ SELECT json_valid(NULL);
+ } 0
+} else {
+ do_execsql_test json101-21.1-correct {
+ SELECT json_valid(NULL);
+ } NULL
+}
+do_execsql_test json101-21.2 {
+ SELECT json_error_position(NULL);
+} NULL
+do_execsql_test json101-21.3 {
+ SELECT json(NULL);
+} NULL
+do_execsql_test json101-21.4 {
+ SELECT json_array(NULL);
+} {[null]}
+do_execsql_test json101-21.5 {
+ SELECT json_extract(NULL);
+} NULL
+do_execsql_test json101-21.6 {
+ SELECT json_insert(NULL,'$',123);
+} NULL
+do_execsql_test json101-21.7 {
+ SELECT NULL->0;
+} NULL
+do_execsql_test json101-21.8 {
+ SELECT NULL->>0;
+} NULL
+do_execsql_test json101-21.9 {
+ SELECT '{a:5}'->NULL;
+} NULL
+do_execsql_test json101-21.10 {
+ SELECT '{a:5}'->>NULL;
+} NULL
+do_catchsql_test json101-21.11 {
+ SELECT json_object(NULL,5);
+} {1 {json_object() labels must be TEXT}}
+do_execsql_test json101-21.12 {
+ SELECT json_patch(NULL,'{a:5}');
+} NULL
+do_execsql_test json101-21.13 {
+ SELECT json_patch('{a:5}',NULL);
+} NULL
+do_execsql_test json101-21.14 {
+ SELECT json_patch(NULL,NULL);
+} NULL
+do_execsql_test json101-21.15 {
+ SELECT json_remove(NULL,'$');
+} NULL
+do_execsql_test json101-21.16 {
+ SELECT json_remove('{a:5,b:7}',NULL);
+} NULL
+do_execsql_test json101-21.17 {
+ SELECT json_replace(NULL,'$.a',123);
+} NULL
+do_execsql_test json101-21.18 {
+ SELECT json_replace('{a:5,b:7}',NULL,NULL);
+} {{{"a":5,"b":7}}}
+do_execsql_test json101-21.19 {
+ SELECT json_set(NULL,'$.a',123);
+} NULL
+do_execsql_test json101-21.20 {
+ SELECT json_set('{a:5,b:7}',NULL,NULL);
+} {{{"a":5,"b":7}}}
+do_execsql_test json101-21.21 {
+ SELECT json_type(NULL);
+} NULL
+do_execsql_test json101-21.22 {
+ SELECT json_type('{a:5,b:7}',NULL);
+} NULL
+do_execsql_test json101-21.23 {
+ SELECT json_quote(NULL);
+} null
+do_execsql_test json101-21.24 {
+ SELECT count(*) FROM json_each(NULL);
+} 0
+do_execsql_test json101-21.25 {
+ SELECT count(*) FROM json_tree(NULL);
+} 0
+do_execsql_test json101-21.26 {
+ WITH c(x) AS (VALUES(1),(2.0),(NULL),('three'))
+ SELECT json_group_array(x) FROM c;
+} {[1,2.0,null,"three"]}
+do_execsql_test json101-21.27 {
+ WITH c(x,y) AS (VALUES('a',1),('b',2.0),('c',NULL),(NULL,'three'),('e','four'))
+ SELECT json_group_object(x,y) FROM c;
+} {{{"a":1,"b":2.0,"c":null,:"three","e":"four"}}}
+
+# 2023-10-09 https://sqlite.org/forum/forumpost/b25edc1d46
+# UAF due to JSON cache overflow
+#
+do_execsql_test json101-22.1 {
+ SELECT json_set(
+ '{}',
+ '$.a', json('1'),
+ '$.a', json('2'),
+ '$.b', json('3'),
+ '$.b', json('4'),
+ '$.c', json('5'),
+ '$.c', json('6')
+ );
+} {{{"a":2,"b":4,"c":6}}}
+do_execsql_test json101-22.2 {
+ SELECT json_replace(
+ '{"a":7,"b":8,"c":9}',
+ '$.a', json('1'),
+ '$.a', json('2'),
+ '$.b', json('3'),
+ '$.b', json('4'),
+ '$.c', json('5'),
+ '$.c', json('6')
+ );
+} {{{"a":2,"b":4,"c":6}}}
+
+# 2023-10-17 https://sqlite.org/forum/forumpost/fc0e3f1e2a
+# Incorrect accesss to '$[0]' in parsed + edited JSON.
+#
+do_execsql_test json101-23.1 {
+ SELECT j, j->>0, j->>1
+ FROM (SELECT json_set(json_set('[]','$[#]',0), '$[#]',1) AS j);
+} {{[0,1]} 0 1}
+do_execsql_test json101-23.2 {
+ SELECT j, j->>0, j->>1
+ FROM (SELECT json_set('[]','$[#]',0,'$[#]',1) AS j);
+} {{[0,1]} 0 1}
+
+# Insert/Set/Replace where the path specifies substructure that
+# does not yet exist
+#
+proc tx x {return [string map [list ( \173 ) \175 ' \042 < \133 > \135] $x]}
+foreach {id start path ins set repl} {
+ 1 {{}} {$.a.b.c} ('a':('b':('c':9))) ('a':('b':('c':9))) ()
+ 2 {{a:4}} {$.a.b.c} ('a':4) ('a':4) ('a':4)
+ 3 {{a:{}}} {$.a.b.c} ('a':('b':('c':9))) ('a':('b':('c':9))) ('a':())
+ 4 {[0,1,2]} {$[3].a[0].b} <0,1,2,('a':<('b':9)>)> <0,1,2,('a':<('b':9)>)> <0,1,2>
+ 5 {[0,1,2]} {$[1].a[0].b} <0,1,2> <0,1,2> <0,1,2>
+ 6 {[0,{},2]} {$[1].a[0].b} <0,('a':<('b':9)>),2> <0,('a':<('b':9)>),2> <0,(),2>
+ 7 {[0,1,2]} {$[3][0].b} <0,1,2,<('b':9)>> <0,1,2,<('b':9)>> <0,1,2>
+ 8 {[0,1,2]} {$[1][0].b} <0,1,2> <0,1,2> <0,1,2>
+} {
+ do_execsql_test json101-24.$id.insert {
+ SELECT json_insert($start,$path,9);
+ } [list [tx $ins]]
+ do_execsql_test json101-24.$id.set {
+ SELECT json_set($start,$path,9);
+ } [list [tx $set]]
+ do_execsql_test json101-24.$id.replace {
+ SELECT json_replace($start,$path,9);
+ } [list [tx $repl]]
+}
+
+finish_test
diff --git a/test/json102.test b/test/json102.test
new file mode 100644
index 0000000..15a54b4
--- /dev/null
+++ b/test/json102.test
@@ -0,0 +1,767 @@
+# 2015-08-12
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for JSON SQL functions extension to the
+# SQLite library.
+#
+# This file contains tests automatically generated from the json1
+# documentation.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test json102-100 {
+ SELECT json_object('ex','[52,3.14159]');
+} {{{"ex":"[52,3.14159]"}}}
+do_execsql_test json102-100b {
+ SELECT json(jsonb_object('ex','[52,3.14159]'));
+} {{{"ex":"[52,3.14159]"}}}
+do_execsql_test json102-110 {
+ SELECT json_object('ex',json('[52,3.14159]'));
+} {{{"ex":[52,3.14159]}}}
+do_execsql_test json102-110-2 {
+ SELECT json(jsonb_object('ex',json('[52,3.14159]')));
+} {{{"ex":[52,3.14159]}}}
+do_execsql_test json102-110-3 {
+ SELECT json_object('ex',jsonb('[52,3.14159]'));
+} {{{"ex":[52,3.14159]}}}
+do_execsql_test json102-110-3 {
+ SELECT json(jsonb_object('ex',jsonb('[52,3.14159]')));
+} {{{"ex":[52,3.14159]}}}
+do_execsql_test json102-120 {
+ SELECT json_object('ex',json_array(52,3.14159));
+} {{{"ex":[52,3.14159]}}}
+do_execsql_test json102-120-2 {
+ SELECT json(jsonb_object('ex',json_array(52,3.14159)));
+} {{{"ex":[52,3.14159]}}}
+do_execsql_test json102-120-3 {
+ SELECT json_object('ex',jsonb_array(52,3.14159));
+} {{{"ex":[52,3.14159]}}}
+do_execsql_test json102-120-4 {
+ SELECT json(jsonb_object('ex',jsonb_array(52,3.14159)));
+} {{{"ex":[52,3.14159]}}}
+do_execsql_test json102-130 {
+ SELECT json(' { "this" : "is", "a": [ "test" ] } ');
+} {{{"this":"is","a":["test"]}}}
+do_execsql_test json102-130b {
+ SELECT json(jsonb(' { "this" : "is", "a": [ "test" ] } '));
+} {{{"this":"is","a":["test"]}}}
+do_execsql_test json102-140 {
+ SELECT json_array(1,2,'3',4);
+} {{[1,2,"3",4]}}
+do_execsql_test json102-140b {
+ SELECT json(jsonb_array(1,2,'3',4));
+} {{[1,2,"3",4]}}
+do_execsql_test json102-150 {
+ SELECT json_array('[1,2]');
+} {{["[1,2]"]}}
+do_execsql_test json102-150b {
+ SELECT json(jsonb_array('[1,2]'));
+} {{["[1,2]"]}}
+do_execsql_test json102-160 {
+ SELECT json_array(json_array(1,2));
+} {{[[1,2]]}}
+do_execsql_test json102-160-2 {
+ SELECT json_array(jsonb_array(1,2));
+} {{[[1,2]]}}
+do_execsql_test json102-160-3 {
+ SELECT json(jsonb_array(json_array(1,2)));
+} {{[[1,2]]}}
+do_execsql_test json102-160-4 {
+ SELECT json(jsonb_array(jsonb_array(1,2)));
+} {{[[1,2]]}}
+do_execsql_test json102-170 {
+ SELECT json_array(1,null,'3','[4,5]','{"six":7.7}');
+} {{[1,null,"3","[4,5]","{\"six\":7.7}"]}}
+do_execsql_test json102-170b {
+ SELECT json(jsonb_array(1,null,'3','[4,5]','{"six":7.7}'));
+} {{[1,null,"3","[4,5]","{\"six\":7.7}"]}}
+do_execsql_test json102-180 {
+ SELECT json_array(1,null,'3',json('[4,5]'),json('{"six":7.7}'));
+} {{[1,null,"3",[4,5],{"six":7.7}]}}
+do_execsql_test json102-180-2 {
+ SELECT json_array(1,null,'3',jsonb('[4,5]'),json('{"six":7.7}'));
+} {{[1,null,"3",[4,5],{"six":7.7}]}}
+do_execsql_test json102-180-3 {
+ SELECT json(jsonb_array(1,null,'3',json('[4,5]'),json('{"six":7.7}')));
+} {{[1,null,"3",[4,5],{"six":7.7}]}}
+do_execsql_test json102-180-4 {
+ SELECT json(jsonb_array(1,null,'3',jsonb('[4,5]'),jsonb('{"six":7.7}')));
+} {{[1,null,"3",[4,5],{"six":7.7}]}}
+do_execsql_test json102-190 {
+ SELECT json_array_length('[1,2,3,4]');
+} {{4}}
+do_execsql_test json102-190b {
+ SELECT json_array_length(jsonb('[1,2,3,4]'));
+} {{4}}
+do_execsql_test json102-191 {
+ SELECT json_array_length( json_remove('[1,2,3,4]','$[2]') );
+} {{3}}
+do_execsql_test json102-191b {
+ SELECT json_array_length( jsonb_remove('[1,2,3,4]','$[2]') );
+} {{3}}
+do_execsql_test json102-200 {
+ SELECT json_array_length('[1,2,3,4]', '$');
+} {{4}}
+do_execsql_test json102-200b {
+ SELECT json_array_length(jsonb('[1,2,3,4]'), '$');
+} {{4}}
+do_execsql_test json102-210 {
+ SELECT json_array_length('[1,2,3,4]', '$[2]');
+} {{0}}
+do_execsql_test json102-210b {
+ SELECT json_array_length(jsonb('[1,2,3,4]'), '$[2]');
+} {{0}}
+do_execsql_test json102-220 {
+ SELECT json_array_length('{"one":[1,2,3]}');
+} {{0}}
+do_execsql_test json102-220 {
+ SELECT json_array_length('{"one":[1,2,3]}');
+} {{0}}
+do_execsql_test json102-230b {
+ SELECT json_array_length(jsonb('{"one":[1,2,3]}'), '$.one');
+} {{3}}
+do_execsql_test json102-240 {
+ SELECT json_array_length('{"one":[1,2,3]}', '$.two');
+} {{}}
+do_execsql_test json102-240b {
+ SELECT json_array_length(jsonb('{"one":[1,2,3]}'), '$.two');
+} {{}}
+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-250-2 {
+ SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$');
+} {{{"a":2,"c":[4,5,{"f":7}]}}}
+do_execsql_test json102-250-3 {
+ SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$'));
+} {{{"a":2,"c":[4,5,{"f":7}]}}}
+do_execsql_test json102-250-4 {
+ SELECT json(jsonb_extract(jsonb('{"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-260-2 {
+ SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c');
+} {{[4,5,{"f":7}]}}
+do_execsql_test json102-260-3 {
+ SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c'));
+} {{[4,5,{"f":7}]}}
+do_execsql_test json102-260-4 {
+ SELECT json(jsonb_extract(jsonb('{"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-270-2 {
+ SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c[2]');
+} {{{"f":7}}}
+do_execsql_test json102-270-3 {
+ SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c[2]'));
+} {{{"f":7}}}
+do_execsql_test json102-270-4 {
+ SELECT json(jsonb_extract('{"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-280b {
+ SELECT jsonb_extract('{"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-290-2 {
+ SELECT json_extract(jsonb('{"a":2,"c":[4,5],"f":7}'),'$.c','$.a');
+} {{[[4,5],2]}}
+do_execsql_test json102-290-3 {
+ SELECT json(jsonb_extract('{"a":2,"c":[4,5],"f":7}','$.c','$.a'));
+} {{[[4,5],2]}}
+do_execsql_test json102-290-4 {
+ SELECT json(jsonb_extract(jsonb('{"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-300b {
+ SELECT jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x');
+} {{}}
+do_execsql_test json102-310 {
+ SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x', '$.a');
+} {{[null,2]}}
+do_execsql_test json102-310-2 {
+ SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.x', '$.a');
+} {{[null,2]}}
+do_execsql_test json102-310-3 {
+ SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.x', '$.a'));
+} {{[null,2]}}
+do_execsql_test json102-310-43 {
+ SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x', '$.a'));
+} {{[null,2]}}
+do_execsql_test json102-320 {
+ SELECT json_insert('{"a":2,"c":4}', '$.a', 99);
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-320-2 {
+ SELECT json_insert(jsonb('{"a":2,"c":4}'), '$.a', 99);
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-320-3 {
+ SELECT json(jsonb_insert('{"a":2,"c":4}', '$.a', 99));
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-320-4 {
+ SELECT json(jsonb_insert(jsonb('{"a":2,"c":4}'), '$.a', 99));
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-330 {
+ SELECT json_insert('{"a":2,"c":4}', '$.e', 99);
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-330-2 {
+ SELECT json_insert(jsonb('{"a":2,"c":4}'), '$.e', 99);
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-330-3 {
+ SELECT json(jsonb_insert('{"a":2,"c":4}', '$.e', 99));
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-330-4 {
+ SELECT json(jsonb_insert(jsonb('{"a":2,"c":4}'), '$.e', 99));
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-340 {
+ SELECT json_replace('{"a":2,"c":4}', '$.a', 99);
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-340-2 {
+ SELECT json_replace(jsonb('{"a":2,"c":4}'), '$.a', 99);
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-340-3 {
+ SELECT json(jsonb_replace('{"a":2,"c":4}', '$.a', 99));
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-340-4 {
+ SELECT json(jsonb_replace(jsonb('{"a":2,"c":4}'), '$.a', 99));
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-350 {
+ SELECT json_replace('{"a":2,"c":4}', '$.e', 99);
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-350-2 {
+ SELECT json_replace(jsonb('{"a":2,"c":4}'), '$.e', 99);
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-350-3 {
+ SELECT json(jsonb_replace('{"a":2,"c":4}', '$.e', 99));
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-350-4 {
+ SELECT json(jsonb_replace(jsonb('{"a":2,"c":4}'), '$.e', 99));
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-360 {
+ SELECT json_set('{"a":2,"c":4}', '$.a', 99);
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-360-2 {
+ SELECT json_set(jsonb('{"a":2,"c":4}'), '$.a', 99);
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-360-3 {
+ SELECT json(jsonb_set('{"a":2,"c":4}', '$.a', 99));
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-360-4 {
+ SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.a', 99));
+} {{{"a":99,"c":4}}}
+do_execsql_test json102-370 {
+ SELECT json_set('{"a":2,"c":4}', '$.e', 99);
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-370-2 {
+ SELECT json_set(jsonb('{"a":2,"c":4}'), '$.e', 99);
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-370-3 {
+ SELECT json(jsonb_set('{"a":2,"c":4}', '$.e', 99));
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-370-4 {
+ SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.e', 99));
+} {{{"a":2,"c":4,"e":99}}}
+do_execsql_test json102-380 {
+ SELECT json_set('{"a":2,"c":4}', '$.c', '[97,96]');
+} {{{"a":2,"c":"[97,96]"}}}
+do_execsql_test json102-380-2 {
+ SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', '[97,96]');
+} {{{"a":2,"c":"[97,96]"}}}
+do_execsql_test json102-380-3 {
+ SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', '[97,96]'));
+} {{{"a":2,"c":"[97,96]"}}}
+do_execsql_test json102-380-4 {
+ SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', '[97,96]'));
+} {{{"a":2,"c":"[97,96]"}}}
+do_execsql_test json102-390 {
+ SELECT json_set('{"a":2,"c":4}', '$.c', json('[97,96]'));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-390-2 {
+ SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', json('[97,96]'));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-390-3 {
+ SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', json('[97,96]')));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-390-4 {
+ SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', json('[97,96]')));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-390-5 {
+ SELECT json_set('{"a":2,"c":4}', '$.c', jsonb('[97,96]'));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-390-6 {
+ SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb('[97,96]'));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-390-7 {
+ SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', jsonb('[97,96]')));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-390-8 {
+ SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb('[97,96]')));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-400 {
+ SELECT json_set('{"a":2,"c":4}', '$.c', json_array(97,96));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-400-2 {
+ SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', json_array(97,96));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-400-3 {
+ SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', json_array(97,96)));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-400-4 {
+ SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', json_array(97,96)));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-400-5 {
+ SELECT json_set('{"a":2,"c":4}', '$.c', jsonb_array(97,96));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-400-6 {
+ SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb_array(97,96));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-400-7 {
+ SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', jsonb_array(97,96)));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-400-8 {
+ SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb_array(97,96)));
+} {{{"a":2,"c":[97,96]}}}
+do_execsql_test json102-410 {
+ SELECT json_object('a',2,'c',4);
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-410b {
+ SELECT json(jsonb_object('a',2,'c',4));
+} {{{"a":2,"c":4}}}
+do_execsql_test json102-420 {
+ SELECT json_object('a',2,'c','{e:5}');
+} {{{"a":2,"c":"{e:5}"}}}
+do_execsql_test json102-420b {
+ SELECT json(jsonb_object('a',2,'c','{e:5}'));
+} {{{"a":2,"c":"{e:5}"}}}
+do_execsql_test json102-430 {
+ SELECT json_object('a',2,'c',json_object('e',5));
+} {{{"a":2,"c":{"e":5}}}}
+do_execsql_test json102-430-2 {
+ SELECT json(jsonb_object('a',2,'c',json_object('e',5)));
+} {{{"a":2,"c":{"e":5}}}}
+do_execsql_test json102-430-3 {
+ SELECT json_object('a',2,'c',jsonb_object('e',5));
+} {{{"a":2,"c":{"e":5}}}}
+do_execsql_test json102-430-4 {
+ SELECT json(jsonb_object('a',2,'c',jsonb_object('e',5)));
+} {{{"a":2,"c":{"e":5}}}}
+do_execsql_test json102-440 {
+ SELECT json_remove('[0,1,2,3,4]','$[2]');
+} {{[0,1,3,4]}}
+do_execsql_test json102-440-2 {
+ SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[2]');
+} {{[0,1,3,4]}}
+do_execsql_test json102-440-3 {
+ SELECT json(jsonb_remove('[0,1,2,3,4]','$[2]'));
+} {{[0,1,3,4]}}
+do_execsql_test json102-440-4 {
+ SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[2]'));
+} {{[0,1,3,4]}}
+do_execsql_test json102-450 {
+ SELECT json_remove('[0,1,2,3,4]','$[2]','$[0]');
+} {{[1,3,4]}}
+do_execsql_test json102-450-2 {
+ SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[2]','$[0]');
+} {{[1,3,4]}}
+do_execsql_test json102-450-3 {
+ SELECT json(jsonb_remove('[0,1,2,3,4]','$[2]','$[0]'));
+} {{[1,3,4]}}
+do_execsql_test json102-450-4 {
+ SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[2]','$[0]'));
+} {{[1,3,4]}}
+do_execsql_test json102-460 {
+ SELECT json_remove('[0,1,2,3,4]','$[0]','$[2]');
+} {{[1,2,4]}}
+do_execsql_test json102-460-2 {
+ SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[0]','$[2]');
+} {{[1,2,4]}}
+do_execsql_test json102-460-3 {
+ SELECT json(jsonb_remove('[0,1,2,3,4]','$[0]','$[2]'));
+} {{[1,2,4]}}
+do_execsql_test json102-460-4 {
+ SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[0]','$[2]'));
+} {{[1,2,4]}}
+do_execsql_test json102-470 {
+ SELECT json_remove('{"x":25,"y":42}');
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-470-2 {
+ SELECT json_remove(jsonb('{"x":25,"y":42}'));
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-470-3 {
+ SELECT json(jsonb_remove('{"x":25,"y":42}'));
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-470-4 {
+ SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}')));
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-480 {
+ SELECT json_remove('{"x":25,"y":42}','$.z');
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-480-2 {
+ SELECT json_remove(jsonb('{"x":25,"y":42}'),'$.z');
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-480-3 {
+ SELECT json(jsonb_remove('{"x":25,"y":42}','$.z'));
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-480-4 {
+ SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$.z'));
+} {{{"x":25,"y":42}}}
+do_execsql_test json102-490 {
+ SELECT json_remove('{"x":25,"y":42}','$.y');
+} {{{"x":25}}}
+do_execsql_test json102-490-2 {
+ SELECT json_remove(jsonb('{"x":25,"y":42}'),'$.y');
+} {{{"x":25}}}
+do_execsql_test json102-490-3 {
+ SELECT json(jsonb_remove('{"x":25,"y":42}','$.y'));
+} {{{"x":25}}}
+do_execsql_test json102-490-4 {
+ SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$.y'));
+} {{{"x":25}}}
+do_execsql_test json102-500 {
+ SELECT json_remove('{"x":25,"y":42}','$');
+} {{}}
+do_execsql_test json102-500-2 {
+ SELECT json_remove(jsonb('{"x":25,"y":42}'),'$');
+} {{}}
+do_execsql_test json102-500-3 {
+ SELECT json(jsonb_remove('{"x":25,"y":42}','$'));
+} {{}}
+do_execsql_test json102-500-4 {
+ SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$'));
+} {{}}
+do_execsql_test json102-510 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}');
+} {{object}}
+do_execsql_test json102-510b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778');
+} {{object}}
+do_execsql_test json102-520 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$');
+} {{object}}
+do_execsql_test json102-520b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$');
+} {{object}}
+do_execsql_test json102-530 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a');
+} {{array}}
+do_execsql_test json102-530b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a');
+} {{array}}
+do_execsql_test json102-540 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[0]');
+} {{integer}}
+do_execsql_test json102-540b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[0]');
+} {{integer}}
+do_execsql_test json102-550 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[1]');
+} {{real}}
+do_execsql_test json102-550b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[1]');
+} {{real}}
+do_execsql_test json102-560 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[2]');
+} {{true}}
+do_execsql_test json102-560b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[2]');
+} {{true}}
+do_execsql_test json102-570 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[3]');
+} {{false}}
+do_execsql_test json102-570b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[3]');
+} {{false}}
+do_execsql_test json102-580 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[4]');
+} {{null}}
+do_execsql_test json102-580b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[4]');
+} {{null}}
+do_execsql_test json102-590 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[5]');
+} {{text}}
+do_execsql_test json102-590b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[5]');
+} {{text}}
+do_execsql_test json102-600 {
+ SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[6]');
+} {{}}
+do_execsql_test json102-600b {
+ SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[6]');
+} {{}}
+do_execsql_test json102-610 {
+ SELECT json_valid(char(123)||'"x":35'||char(125));
+} {{1}}
+do_execsql_test json102-620 {
+ SELECT json_valid(char(123)||'"x":35');
+} {{0}}
+
+ifcapable vtab {
+do_execsql_test json102-1000 {
+ CREATE TABLE user(name,phone,phoneb);
+ INSERT INTO user(name,phone) VALUES
+ ('Alice','["919-555-2345","804-555-3621"]'),
+ ('Bob','["201-555-8872"]'),
+ ('Cindy','["704-555-9983"]'),
+ ('Dave','["336-555-8421","704-555-4321","803-911-4421"]');
+ UPDATE user SET phoneb=jsonb(phone);
+ SELECT DISTINCT user.name
+ FROM user, json_each(user.phone)
+ WHERE json_each.value LIKE '704-%'
+ ORDER BY 1;
+} {Cindy Dave}
+do_execsql_test json102-1000b {
+ SELECT DISTINCT user.name
+ FROM user, json_each(user.phoneb)
+ WHERE json_each.value LIKE '704-%'
+ ORDER BY 1;
+} {Cindy Dave}
+
+do_execsql_test json102-1010 {
+ UPDATE user
+ SET phone=json_extract(phone,'$[0]')
+ WHERE json_array_length(phone)<2;
+ SELECT name, substr(phone,1,5) FROM user ORDER BY name;
+} {Alice {["919} Bob 201-5 Cindy 704-5 Dave {["336}}
+do_execsql_test json102-1011 {
+ SELECT name FROM user WHERE phone LIKE '704-%'
+ UNION
+ SELECT user.name
+ FROM user, json_each(user.phone)
+ WHERE json_valid(user.phone)
+ AND json_each.value LIKE '704-%';
+} {Cindy Dave}
+
+do_execsql_test json102-1100 {
+ CREATE TABLE big(json JSON);
+ INSERT INTO big(json) VALUES('{
+ "id":123,
+ "stuff":[1,2,3,4],
+ "partlist":[
+ {"uuid":"bb108722-572e-11e5-9320-7f3b63a4ca74"},
+ {"uuid":"c690dc14-572e-11e5-95f9-dfc8861fd535"},
+ {"subassembly":[
+ {"uuid":"6fa5181e-5721-11e5-a04e-57f3d7b32808"}
+ ]}
+ ]
+ }');
+ INSERT INTO big(json) VALUES('{
+ "id":456,
+ "stuff":["hello","world","xyzzy"],
+ "partlist":[
+ {"uuid":false},
+ {"uuid":"c690dc14-572e-11e5-95f9-dfc8861fd535"}
+ ]
+ }');
+} {}
+set correct_answer [list \
+ 1 {$.id} 123 \
+ 1 {$.stuff[0]} 1 \
+ 1 {$.stuff[1]} 2 \
+ 1 {$.stuff[2]} 3 \
+ 1 {$.stuff[3]} 4 \
+ 1 {$.partlist[0].uuid} bb108722-572e-11e5-9320-7f3b63a4ca74 \
+ 1 {$.partlist[1].uuid} c690dc14-572e-11e5-95f9-dfc8861fd535 \
+ 1 {$.partlist[2].subassembly[0].uuid} 6fa5181e-5721-11e5-a04e-57f3d7b32808 \
+ 2 {$.id} 456 \
+ 2 {$.stuff[0]} hello \
+ 2 {$.stuff[1]} world \
+ 2 {$.stuff[2]} xyzzy \
+ 2 {$.partlist[0].uuid} 0 \
+ 2 {$.partlist[1].uuid} c690dc14-572e-11e5-95f9-dfc8861fd535]
+do_execsql_test json102-1110 {
+ SELECT big.rowid, fullkey, value
+ FROM big, json_tree(big.json)
+ WHERE json_tree.type NOT IN ('object','array')
+ ORDER BY +big.rowid, +json_tree.id
+} $correct_answer
+do_execsql_test json102-1110b {
+ SELECT big.rowid, fullkey, value
+ FROM big, json_tree(jsonb(big.json))
+ WHERE json_tree.type NOT IN ('object','array')
+ ORDER BY +big.rowid, +json_tree.id
+} $correct_answer
+do_execsql_test json102-1120 {
+ SELECT big.rowid, fullkey, atom
+ FROM big, json_tree(big.json)
+ WHERE atom IS NOT NULL
+ ORDER BY +big.rowid, +json_tree.id
+} $correct_answer
+
+do_execsql_test json102-1130 {
+ SELECT DISTINCT json_extract(big.json,'$.id')
+ FROM big, json_tree(big.json,'$.partlist')
+ WHERE json_tree.key='uuid'
+ AND json_tree.value='6fa5181e-5721-11e5-a04e-57f3d7b32808';
+} {123}
+do_execsql_test json102-1131 {
+ SELECT DISTINCT json_extract(big.json,'$.id')
+ FROM big, json_tree(big.json,'$')
+ WHERE json_tree.key='uuid'
+ AND json_tree.value='6fa5181e-5721-11e5-a04e-57f3d7b32808';
+} {123}
+do_execsql_test json102-1132 {
+ SELECT DISTINCT json_extract(big.json,'$.id')
+ FROM big, json_tree(big.json)
+ WHERE json_tree.key='uuid'
+ AND json_tree.value='6fa5181e-5721-11e5-a04e-57f3d7b32808';
+} {123}
+} ;# end ifcapable vtab
+
+#-------------------------------------------------------------------------
+# Test that json_valid() correctly identifies non-ascii range
+# characters as non-whitespace.
+#
+do_execsql_test json102-1201 { SELECT json_valid(char(32) || '"xyz"') } 1
+do_execsql_test json102-1202 { SELECT json_valid(char(200) || '"xyz"') } 0
+
+# Off-by-one error in jsonAppendString()
+#
+for {set i 0} {$i<100} {incr i} {
+ set str abcdef[string repeat \" [expr {$i+50}]]uvwxyz
+ do_test json102-[format %d [expr {$i+1300}]] {
+ db eval {SELECT json_extract(json_array($::str),'$[0]')==$::str}
+ } {1}
+}
+
+#-------------------------------------------------------------------------
+# 2017-04-08 ticket b93be8729a895a528e2849fca99f7
+# JSON extension accepts invalid numeric values
+#
+# JSON does not allow leading zeros. But the JSON extension was
+# allowing them. The following tests verify that the problem is now
+# fixed.
+#
+foreach {id j x0 x5} {
+ 1401 {'{"x":01}'} 0 0
+ 1402 {'{"x":-01}'} 0 0
+ 1403 {'{"x":0}'} 1 1
+ 1404 {'{"x":-0}'} 1 1
+ 1405 {'{"x":0.1}'} 1 1
+ 1406 {'{"x":-0.1}'} 1 1
+ 1407 {'{"x":0.0000}'} 1 1
+ 1408 {'{"x":-0.0000}'} 1 1
+ 1409 {'{"x":01.5}'} 0 0
+ 1410 {'{"x":-01.5}'} 0 0
+ 1411 {'{"x":00}'} 0 0
+ 1412 {'{"x":-00}'} 0 0
+ 1413 {'{"x":+0}'} 0 1
+ 1414 {'{"x":+5}'} 0 1
+ 1415 {'{"x":+5.5}'} 0 1
+} {
+ do_execsql_test json102-$id "
+ SELECT json_valid($j), NOT json_error_position($j);
+ " [list $x0 $x5]
+}
+
+#------------------------------------------------------------------------
+# 2017-04-10 ticket 6c9b5514077fed34551f98e64c09a10dc2fc8e16
+# JSON extension accepts strings containing control characters.
+#
+# The JSON spec requires that all control characters be escaped.
+#
+do_execsql_test json102-1500 {
+ WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<0x20)
+ SELECT x FROM c WHERE json_valid(printf('{"a":"x%sz"}', char(x))) ORDER BY x;
+} {32}
+
+# All control characters are escaped
+#
+do_execsql_test json102-1501 {
+ WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<0x1f)
+ 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
+]
+
+reset_db
+do_execsql_test json102-1700 {
+ CREATE TABLE t1(a1 DATE, a2 INTEGER PRIMARY KEY, a3 INTEGER, memo TEXT);
+ CREATE INDEX t1x1 ON t1(a3, a1, memo->>'y');
+ INSERT INTO t1(a2,a1,a3,memo) VALUES (876, '2023-08-03', 5, '{"x":77,"y":4}');
+}
+do_execsql_test json102-1710 {
+ UPDATE t1 SET memo = JSON_REMOVE(memo, '$.y');
+ PRAGMA integrity_check;
+ SELECT * FROM t1;
+} {ok 2023-08-03 876 5 {{"x":77}}}
+do_execsql_test json102-1720 {
+ UPDATE t1 SET memo = JSON_SET(memo, '$.y', 6)
+ WHERE a2 IN (876) AND JSON_TYPE(memo, '$.y') IS NULL;
+ PRAGMA integrity_check;
+ SELECT * FROM t1;
+} {ok 2023-08-03 876 5 {{"x":77,"y":6}}}
+
+finish_test
diff --git a/test/json103.test b/test/json103.test
new file mode 100644
index 0000000..e748307
--- /dev/null
+++ b/test/json103.test
@@ -0,0 +1,93 @@
+# 2015-12-30
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for JSON aggregate SQL functions
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test json103-100 {
+ CREATE TABLE t1(a,b,c);
+ WITH RECURSIVE c(x) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<100)
+ INSERT INTO t1(a,b,c) SELECT x, x%3, printf('n%d',x) FROM c;
+ UPDATE t1 SET a='orange' WHERE rowid=39;
+ UPDATE t1 SET a=32.5 WHERE rowid=31;
+ UPDATE t1 SET a=x'303132' WHERE rowid=29;
+ UPDATE t1 SET a=NULL WHERE rowid=37;
+ SELECT json_group_array(a) FROM t1 WHERE a<0 AND typeof(a)!='blob';
+} {{[]}}
+do_catchsql_test json103-101 {
+ SELECT json_group_array(a) FROM t1;
+} {1 {JSON cannot hold BLOB values}}
+do_execsql_test json103-110 {
+ SELECT json_group_array(a) FROM t1
+ WHERE rowid BETWEEN 31 AND 39;
+} {{[32.5,32,33,34,35,36,null,38,"orange"]}}
+do_execsql_test json103-111 {
+ SELECT json_array_length(json_group_array(a)) FROM t1
+ WHERE rowid BETWEEN 31 AND 39;
+} {9}
+do_execsql_test json103-120 {
+ SELECT b, json_group_array(a) FROM t1 WHERE rowid<10 GROUP BY b ORDER BY b;
+} {0 {[3,6,9]} 1 {[1,4,7]} 2 {[2,5,8]}}
+
+do_execsql_test json103-200 {
+ SELECT json_group_object(c,a) FROM t1 WHERE a<0 AND typeof(a)!='blob';
+} {{{}}}
+do_catchsql_test json103-201 {
+ SELECT json_group_object(c,a) FROM t1;
+} {1 {JSON cannot hold BLOB values}}
+
+do_execsql_test json103-210 {
+ SELECT json_group_object(c,a) FROM t1
+ WHERE rowid BETWEEN 31 AND 39 AND rowid%2==1;
+} {{{"n31":32.5,"n33":33,"n35":35,"n37":null,"n39":"orange"}}}
+do_execsql_test json103-220 {
+ SELECT b, json_group_object(c,a) FROM t1
+ WHERE rowid<7 GROUP BY b ORDER BY b;
+} {0 {{"n3":3,"n6":6}} 1 {{"n1":1,"n4":4}} 2 {{"n2":2,"n5":5}}}
+
+# ticket https://www.sqlite.org/src/info/f45ac567eaa9f93c 2016-01-30
+# Invalid JSON generated by json_group_array()
+#
+# The underlying problem is a failure to reset Mem.eSubtype
+#
+do_execsql_test json103-300 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1),('abc');
+ SELECT
+ json_group_array(x),
+ json_group_array(json_object('x',x))
+ FROM t1;
+} {{[1,"abc"]} {[{"x":1},{"x":"abc"}]}}
+
+# json_group_array() and json_group_object() work as window functions.
+#
+ifcapable windowfunc {
+ do_execsql_test json103-400 {
+ CREATE TABLE t4(x);
+ INSERT INTO t4 VALUES
+ (1),
+ ('a,b'),
+ (3),
+ ('x"y'),
+ (5),
+ (6),
+ (7);
+ SELECT json_group_array(x) OVER (ROWS 2 PRECEDING) FROM t4;
+ } {{[1]} {[1,"a,b"]} {[1,"a,b",3]} {["a,b",3,"x\"y"]} {[3,"x\"y",5]} {["x\"y",5,6]} {[5,6,7]}}
+ do_execsql_test json103-410 {
+ SELECT json_group_object(rowid, x) OVER (ROWS 2 PRECEDING) FROM t4;
+ } {{{"1":1}} {{"1":1,"2":"a,b"}} {{"1":1,"2":"a,b","3":3}} {{"2":"a,b","3":3,"4":"x\"y"}} {{"3":3,"4":"x\"y","5":5}} {{"4":"x\"y","5":5,"6":6}} {{"5":5,"6":6,"7":7}}}
+}
+
+finish_test
diff --git a/test/json104.test b/test/json104.test
new file mode 100644
index 0000000..c3c43d1
--- /dev/null
+++ b/test/json104.test
@@ -0,0 +1,195 @@
+# 2017-03-22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for json_patch(A,B) SQL function.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix json104
+
+# This is the example from pages 2 and 3 of RFC-7396
+do_execsql_test json104-100 {
+ SELECT json_patch('{
+ "a": "b",
+ "c": {
+ "d": "e",
+ "f": "g"
+ }
+ }','{
+ "a":"z",
+ "c": {
+ "f": null
+ }
+ }');
+} {{{"a":"z","c":{"d":"e"}}}}
+do_execsql_test json104-101 {
+ SELECT json_patch('{
+ "a": "b",
+ "c": {
+ "d": "e",
+ "f": "g"
+ }
+ }','{
+ a:"z",
+ c: {
+ f: null
+ }
+ }');
+} {{{"a":"z","c":{"d":"e"}}}}
+do_execsql_test json104-102 {
+ SELECT json_patch('{
+ a: "b",
+ c: {
+ d: "e",
+ f: "g"
+ }
+ }','{
+ "a":"z",
+ "c": {
+ "f": null
+ }
+ }');
+} {{{"a":"z","c":{"d":"e"}}}}
+do_execsql_test json104-103 {
+ SELECT json_patch('{
+ a: "b",
+ c: {
+ d: "e",
+ f: "g"
+ }
+ }','{
+ a:"z",
+ c: {
+ f: null
+ }
+ }');
+} {{{"a":"z","c":{"d":"e"}}}}
+
+
+# This is the example from pages 4 and 5 of RFC-7396
+do_execsql_test json104-110 {
+ SELECT json_patch('{
+ "title": "Goodbye!",
+ "author" : {
+ "givenName" : "John",
+ "familyName" : "Doe"
+ },
+ "tags":[ "example", "sample" ],
+ "content": "This will be unchanged"
+ }','{
+ "title": "Hello!",
+ "phoneNumber": "+01-123-456-7890",
+ "author": {
+ "familyName": null
+ },
+ "tags": [ "example" ]
+ }');
+} {{{"title":"Hello!","author":{"givenName":"John"},"tags":["example"],"content":"This will be unchanged","phoneNumber":"+01-123-456-7890"}}}
+
+do_execsql_test json104-200 {
+ SELECT json_patch('[1,2,3]','{"x":null}');
+} {{{}}}
+do_execsql_test json104-210 {
+ SELECT json_patch('[1,2,3]','{"x":null,"y":1,"z":null}');
+} {{{"y":1}}}
+do_execsql_test json104-220 {
+ SELECT json_patch('{}','{"a":{"bb":{"ccc":null}}}');
+} {{{"a":{"bb":{}}}}}
+do_execsql_test json104-221 {
+ SELECT json_patch('{}','{"a":{"bb":{"ccc":[1,null,3]}}}');
+} {{{"a":{"bb":{"ccc":[1,null,3]}}}}}
+do_execsql_test json104-222 {
+ SELECT json_patch('{}','{"a":{"bb":{"ccc":[1,{"dddd":null},3]}}}');
+} {{{"a":{"bb":{"ccc":[1,{"dddd":null},3]}}}}}
+
+# Example test cases at the end of the RFC-7396 document
+do_execsql_test json104-300 {
+ SELECT json_patch('{"a":"b"}','{"a":"c"}');
+} {{{"a":"c"}}}
+do_execsql_test json104-300a {
+ SELECT coalesce(json_patch(null,'{"a":"c"}'), 'real-null');
+} {{real-null}}
+do_execsql_test json104-301 {
+ SELECT json_patch('{"a":"b"}','{"b":"c"}');
+} {{{"a":"b","b":"c"}}}
+do_execsql_test json104-302 {
+ SELECT json_patch('{"a":"b"}','{"a":null}');
+} {{{}}}
+do_execsql_test json104-303 {
+ SELECT json_patch('{"a":"b","b":"c"}','{"a":null}');
+} {{{"b":"c"}}}
+do_execsql_test json104-304 {
+ SELECT json_patch('{"a":["b"]}','{"a":"c"}');
+} {{{"a":"c"}}}
+do_execsql_test json104-305 {
+ SELECT json_patch('{"a":"c"}','{"a":["b"]}');
+} {{{"a":["b"]}}}
+do_execsql_test json104-306 {
+ SELECT json_patch('{"a":{"b":"c"}}','{"a":{"b":"d","c":null}}');
+} {{{"a":{"b":"d"}}}}
+do_execsql_test json104-307 {
+ SELECT json_patch('{"a":[{"b":"c"}]}','{"a":[1]}');
+} {{{"a":[1]}}}
+do_execsql_test json104-308 {
+ SELECT json_patch('["a","b"]','["c","d"]');
+} {{["c","d"]}}
+do_execsql_test json104-309 {
+ SELECT json_patch('{"a":"b"}','["c"]');
+} {{["c"]}}
+do_execsql_test json104-310 {
+ SELECT json_patch('{"a":"foo"}','null');
+} {{null}}
+do_execsql_test json104-310a {
+ SELECT coalesce(json_patch('{"a":"foo"}',null), 'real-null');
+} {{real-null}}
+do_execsql_test json104-311 {
+ SELECT json_patch('{"a":"foo"}','"bar"');
+} {{"bar"}}
+do_execsql_test json104-312 {
+ SELECT json_patch('{"e":null}','{"a":1}');
+} {{{"e":null,"a":1}}}
+do_execsql_test json104-313 {
+ SELECT json_patch('[1,2]','{"a":"b","c":null}');
+} {{{"a":"b"}}}
+do_execsql_test json104-314 {
+ SELECT json_patch('{}','{"a":{"bb":{"ccc":null}}}');
+} {{{"a":{"bb":{}}}}}
+do_execsql_test json104-320 {
+ SELECT json_patch('{"x":{"one":1}}','{"x":{"two":2},"x":"three"}');
+} {{{"x":"three"}}}
+
+#-------------------------------------------------------------------------
+
+do_execsql_test 401 {
+ CREATE TABLE obj(x);
+ INSERT INTO obj VALUES('{"a":1,"b":2}');
+ SELECT * FROM obj;
+} {{{"a":1,"b":2}}}
+do_execsql_test 402 {
+ UPDATE obj SET x = json_insert(x, '$.c', 3);
+ SELECT * FROM obj;
+} {{{"a":1,"b":2,"c":3}}}
+do_execsql_test 403 {
+ SELECT json_extract(x, '$.b') FROM obj;
+ SELECT json_extract(x, '$."b"') FROM obj;
+} {2 2}
+do_execsql_test 404 {
+ UPDATE obj SET x = json_set(x, '$."b"', 555);
+ SELECT json_extract(x, '$.b') FROM obj;
+ SELECT json_extract(x, '$."b"') FROM obj;
+} {555 555}
+do_execsql_test 405 {
+ UPDATE obj SET x = json_set(x, '$."d"', 4);
+ SELECT json_extract(x, '$."d"') FROM obj;
+} {4}
+
+
+finish_test
diff --git a/test/json105.test b/test/json105.test
new file mode 100644
index 0000000..509db94
--- /dev/null
+++ b/test/json105.test
@@ -0,0 +1,113 @@
+# 2019-11-22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for "[#]" extension to json-path
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix json105
+
+# This is the example from pages 2 and 3 of RFC-7396
+db eval {
+ CREATE TABLE t1(j);
+ INSERT INTO t1(j) VALUES('{"a":1,"b":[1,[2,3],4],"c":99}');
+}
+proc json_extract_test {testnum path result} {
+ do_execsql_test json105-1.$testnum "SELECT quote(json_extract(j,$path)) FROM t1" $result
+}
+json_extract_test 10 {'$.b[#]'} NULL
+json_extract_test 20 {'$.b[#-1]'} 4
+json_extract_test 30 {'$.b[#-2]'} {'[2,3]'}
+json_extract_test 31 {'$.b[#-02]'} {'[2,3]'}
+json_extract_test 40 {'$.b[#-3]'} 1
+json_extract_test 50 {'$.b[#-4]'} NULL
+json_extract_test 60 {'$.b[#-2][#-1]'} 3
+json_extract_test 70 {'$.b[0]','$.b[#-1]'} {'[1,4]'}
+
+json_extract_test 100 {'$.a[#-1]'} NULL
+json_extract_test 110 {'$.b[#-000001]'} 4
+
+proc json_remove_test {testnum path result} {
+ do_execsql_test json105-2.$testnum "SELECT quote(json_remove(j,$path)) FROM t1" $result
+}
+json_remove_test 10 {'$.b[#]'} {'{"a":1,"b":[1,[2,3],4],"c":99}'}
+json_remove_test 20 {'$.b[#-0]'} {'{"a":1,"b":[1,[2,3],4],"c":99}'}
+json_remove_test 30 {'$.b[#-1]'} {'{"a":1,"b":[1,[2,3]],"c":99}'}
+json_remove_test 40 {'$.b[#-2]'} {'{"a":1,"b":[1,4],"c":99}'}
+json_remove_test 50 {'$.b[#-3]'} {'{"a":1,"b":[[2,3],4],"c":99}'}
+json_remove_test 60 {'$.b[#-4]'} {'{"a":1,"b":[1,[2,3],4],"c":99}'}
+json_remove_test 70 {'$.b[#-2][#-1]'} {'{"a":1,"b":[1,[2],4],"c":99}'}
+
+json_remove_test 100 {'$.b[0]','$.b[#-1]'} {'{"a":1,"b":[[2,3]],"c":99}'}
+json_remove_test 110 {'$.b[#-1]','$.b[0]'} {'{"a":1,"b":[[2,3]],"c":99}'}
+json_remove_test 120 {'$.b[#-1]','$.b[#-2]'} {'{"a":1,"b":[[2,3]],"c":99}'}
+json_remove_test 130 {'$.b[#-1]','$.b[#-1]'} {'{"a":1,"b":[1],"c":99}'}
+json_remove_test 140 {'$.b[#-2]','$.b[#-1]'} {'{"a":1,"b":[1],"c":99}'}
+
+proc json_insert_test {testnum x result} {
+ do_execsql_test json105-3.$testnum "SELECT quote(json_insert(j,$x)) FROM t1" $result
+}
+json_insert_test 10 {'$.b[#]','AAA'} {'{"a":1,"b":[1,[2,3],4,"AAA"],"c":99}'}
+json_insert_test 20 {'$.b[1][#]','AAA'} {'{"a":1,"b":[1,[2,3,"AAA"],4],"c":99}'}
+json_insert_test 30 {'$.b[1][#]','AAA','$.b[#]','BBB'} \
+ {'{"a":1,"b":[1,[2,3,"AAA"],4,"BBB"],"c":99}'}
+json_insert_test 40 {'$.b[#]','AAA','$.b[#]','BBB'} \
+ {'{"a":1,"b":[1,[2,3],4,"AAA","BBB"],"c":99}'}
+
+proc json_set_test {testnum x result} {
+ do_execsql_test json105-4.$testnum "SELECT quote(json_set(j,$x)) FROM t1" $result
+}
+json_set_test 10 {'$.b[#]','AAA'} {'{"a":1,"b":[1,[2,3],4,"AAA"],"c":99}'}
+json_set_test 20 {'$.b[1][#]','AAA'} {'{"a":1,"b":[1,[2,3,"AAA"],4],"c":99}'}
+json_set_test 30 {'$.b[1][#]','AAA','$.b[#]','BBB'} \
+ {'{"a":1,"b":[1,[2,3,"AAA"],4,"BBB"],"c":99}'}
+json_set_test 40 {'$.b[#]','AAA','$.b[#]','BBB'} \
+ {'{"a":1,"b":[1,[2,3],4,"AAA","BBB"],"c":99}'}
+json_set_test 50 {'$.b[#-1]','AAA'} {'{"a":1,"b":[1,[2,3],"AAA"],"c":99}'}
+json_set_test 60 {'$.b[1][#-1]','AAA'} {'{"a":1,"b":[1,[2,"AAA"],4],"c":99}'}
+json_set_test 70 {'$.b[1][#-1]','AAA','$.b[#-1]','BBB'} \
+ {'{"a":1,"b":[1,[2,"AAA"],"BBB"],"c":99}'}
+json_set_test 80 {'$.b[#-1]','AAA','$.b[#-1]','BBB'} \
+ {'{"a":1,"b":[1,[2,3],"BBB"],"c":99}'}
+
+proc json_replace_test {testnum x result} {
+ do_execsql_test json105-5.$testnum "SELECT quote(json_replace(j,$x)) FROM t1" $result
+}
+json_replace_test 10 {'$.b[#]','AAA'} {'{"a":1,"b":[1,[2,3],4],"c":99}'}
+json_replace_test 20 {'$.b[1][#]','AAA'} {'{"a":1,"b":[1,[2,3],4],"c":99}'}
+json_replace_test 30 {'$.b[1][#]','AAA','$.b[#]','BBB'} \
+ {'{"a":1,"b":[1,[2,3],4],"c":99}'}
+json_replace_test 40 {'$.b[#]','AAA','$.b[#]','BBB'} \
+ {'{"a":1,"b":[1,[2,3],4],"c":99}'}
+json_replace_test 50 {'$.b[#-1]','AAA'} {'{"a":1,"b":[1,[2,3],"AAA"],"c":99}'}
+json_replace_test 60 {'$.b[1][#-1]','AAA'} {'{"a":1,"b":[1,[2,"AAA"],4],"c":99}'}
+json_replace_test 70 {'$.b[1][#-1]','AAA','$.b[#-1]','BBB'} \
+ {'{"a":1,"b":[1,[2,"AAA"],"BBB"],"c":99}'}
+json_replace_test 80 {'$.b[#-1]','AAA','$.b[#-1]','BBB'} \
+ {'{"a":1,"b":[1,[2,3],"BBB"],"c":99}'}
+
+do_catchsql_test json105-6.10 {
+ SELECT json_extract(j, '$.b[#-]') FROM t1;
+} {1 {bad JSON path: '$.b[#-]'}}
+do_catchsql_test json105-6.20 {
+ SELECT json_extract(j, '$.b[#9]') FROM t1;
+} {1 {bad JSON path: '$.b[#9]'}}
+do_catchsql_test json105-6.30 {
+ SELECT json_extract(j, '$.b[#+2]') FROM t1;
+} {1 {bad JSON path: '$.b[#+2]'}}
+do_catchsql_test json105-6.40 {
+ SELECT json_extract(j, '$.b[#-1') FROM t1;
+} {1 {bad JSON path: '$.b[#-1'}}
+do_catchsql_test json105-6.50 {
+ SELECT json_extract(j, '$.b[#-1x]') FROM t1;
+} {1 {bad JSON path: '$.b[#-1x]'}}
+
+finish_test
diff --git a/test/json106.test b/test/json106.test
new file mode 100644
index 0000000..23fa028
--- /dev/null
+++ b/test/json106.test
@@ -0,0 +1,73 @@
+# 2023-12-18
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# Invariant tests for JSON built around the randomjson extension
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix json106
+
+# These tests require virtual table "json_tree" to run.
+ifcapable !vtab { finish_test ; return }
+
+load_static_extension db randomjson
+db eval {
+ CREATE TEMP TABLE t1(j0,j5,p);
+ CREATE TEMP TABLE kv(n,key,val);
+}
+unset -nocomplain ii
+for {set ii 1} {$ii<=5000} {incr ii} {
+ do_execsql_test $ii.1 {
+ DELETE FROM t1;
+ INSERT INTO t1(j0,j5) VALUES(random_json($ii),random_json5($ii));
+ SELECT json_valid(j0), json_valid(j5,2) FROM t1;
+ } {1 1}
+ do_execsql_test $ii.2 {
+ SELECT count(*)
+ FROM t1, json_tree(j0) AS rt
+ WHERE rt.type NOT IN ('object','array')
+ AND rt.atom IS NOT (j0 ->> rt.fullkey);
+ } 0
+ do_execsql_test $ii.3 {
+ SELECT count(*)
+ FROM t1, json_tree(j5) AS rt
+ WHERE rt.type NOT IN ('object','array')
+ AND rt.atom IS NOT (j0 ->> rt.fullkey);
+ } 0
+ do_execsql_test $ii.4 {
+ DELETE FROM kv;
+ INSERT INTO kv
+ SELECT rt.rowid, rt.fullkey, rt.atom
+ FROM t1, json_tree(j0) AS rt
+ WHERE rt.type NOT IN ('object','array');
+ }
+ do_execsql_test $ii.5 {
+ SELECT count(*)
+ FROM t1, kv
+ WHERE key NOT LIKE '%]'
+ AND json_remove(j5,key)->>key IS NOT NULL
+ } 0
+ do_execsql_test $ii.6 {
+ SELECT count(*)
+ FROM t1, kv
+ WHERE key NOT LIKE '%]'
+ AND json_insert(json_remove(j5,key),key,val)->>key IS NOT val
+ } 0
+ do_execsql_test $ii.7 {
+ UPDATE t1 SET p=json_patch(j0,j5);
+ SELECT count(*)
+ FROM t1, kv
+ WHERE p->>key IS NOT val
+ } 0
+}
+
+
+finish_test
diff --git a/test/json107.test b/test/json107.test
new file mode 100644
index 0000000..779b557
--- /dev/null
+++ b/test/json107.test
@@ -0,0 +1,86 @@
+# 2024-01-23
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# Legacy JSON bug: If the input is a BLOB that when cast into TEXT looks
+# like valid JSON, then treat it as valid JSON.
+#
+# The original intent of the JSON functions was to raise an error on any
+# BLOB input. That intent was clearly documented, but the code failed to
+# to implement it. Subsequently, many applications began to depend on the
+# incorrect behavior, especially apps that used readfile() to read JSON
+# content, since readfile() returns a BLOB. So we need to support the
+# bug moving forward.
+#
+# The tests in this fail verify that the original buggy behavior is
+# preserved.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix json107
+
+if {[db one {PRAGMA encoding}]!="UTF-8"} {
+ # These tests only work for a UTF-8 encoding.
+ finish_test
+ return
+}
+
+do_execsql_test 1.1 {
+ SELECT json_valid( CAST('{"a":1}' AS BLOB) );
+} 1
+do_execsql_test 1.1.1 {
+ SELECT json_valid( CAST('{"a":1}' AS BLOB), 1);
+} 1
+do_execsql_test 1.1.2 {
+ SELECT json_valid( CAST('{"a":1}' AS BLOB), 2);
+} 1
+do_execsql_test 1.1.4 {
+ SELECT json_valid( CAST('{"a":1}' AS BLOB), 4);
+} 0
+do_execsql_test 1.1.8 {
+ SELECT json_valid( CAST('{"a":1}' AS BLOB), 8);
+} 0
+
+do_execsql_test 1.2.1 {
+ SELECT CAST('{"a":123}' AS blob) -> 'a';
+} 123
+do_execsql_test 1.2.2 {
+ SELECT CAST('{"a":123}' AS blob) ->> 'a';
+} 123
+do_execsql_test 1.2.3 {
+ SELECT json_extract(CAST('{"a":123}' AS blob), '$.a');
+} 123
+do_execsql_test 1.3 {
+ SELECT json_insert(CAST('{"a":123}' AS blob),'$.b',456);
+} {{{"a":123,"b":456}}}
+do_execsql_test 1.4 {
+ SELECT json_remove(CAST('{"a":123,"b":456}' AS blob),'$.a');
+} {{{"b":456}}}
+do_execsql_test 1.5 {
+ SELECT json_set(CAST('{"a":123,"b":456}' AS blob),'$.a',789);
+} {{{"a":789,"b":456}}}
+do_execsql_test 1.6 {
+ SELECT json_replace(CAST('{"a":123,"b":456}' AS blob),'$.a',789);
+} {{{"a":789,"b":456}}}
+do_execsql_test 1.7 {
+ SELECT json_type(CAST('{"a":123,"b":456}' AS blob));
+} object
+do_execsql_test 1.8 {
+ SELECT json(CAST('{"a":123,"b":456}' AS blob));
+} {{{"a":123,"b":456}}}
+
+ifcapable vtab {
+ do_execsql_test 2.1 {
+ SELECT key, value FROM json_tree( CAST('{"a":123,"b":456}' AS blob) )
+ WHERE atom;
+ } {a 123 b 456}
+}
+finish_test
diff --git a/test/json501.test b/test/json501.test
new file mode 100644
index 0000000..40b3b56
--- /dev/null
+++ b/test/json501.test
@@ -0,0 +1,309 @@
+# 2023-04-27
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for the JSON5 enhancements to the
+# JSON SQL functions extension to the SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix json501
+
+# From https://spec.json5.org/#introduction
+#
+#-----------------------------------------------------------------------------
+# Summary of Features
+#
+# The following ECMAScript 5.1 features, which are not supported in JSON, have
+# been extended to JSON5.
+#
+# Objects
+#
+# 1) Object keys may be an ECMAScript 5.1 IdentifierName.
+# 2) Objects may have a single trailing comma.
+#
+# Arrays
+#
+# 3) Arrays may have a single trailing comma.
+#
+# Strings
+#
+# 4) Strings may be single quoted.
+# 5) Strings may span multiple lines by escaping new line characters.
+# 6) Strings may include character escapes.
+#
+# Numbers
+#
+# 7) Numbers may be hexadecimal.
+# 8) Numbers may have a leading or trailing decimal point.
+# 9) Numbers may be IEEE 754 positive infinity, negative infinity, and NaN.
+# 10) Numbers may begin with an explicit plus sign.
+#
+# Comments
+#
+# 11) Single and multi-line comments are allowed.
+#
+# White Space
+#
+# 12) Additional white space characters are allowed.
+#-----------------------------------------------------------------------------
+#
+# Test number in this file are of the form X.Y where X is one of the item
+# numbers in the feature list above and Y is the test sequence number.
+#
+
+###############################################################################
+# 1) Object keys may be an ECMAScript 5.1 IdentifierName.
+do_execsql_test 1.1 {
+ WITH c(x) AS (VALUES('{a:5,b:6}'))
+ SELECT x->>'a', json(x), json_valid(x), NOT json_error_position(x) FROM c;
+} {5 {{"a":5,"b":6}} 0 1}
+do_execsql_test 1.2 {
+ SELECT '[7,null,{a:5,b:6},[8,9]]'->>'$[2].b';
+} {6}
+do_execsql_test 1.3 {
+ SELECT '{ $123 : 789 }'->>'$."$123"';
+} 789
+do_execsql_test 1.4 {
+ SELECT '{ _123$xyz : 789 }'->>'$."_123$xyz"';
+} 789
+do_execsql_test 1.5 {
+ SELECT '{ MNO_123$xyz : 789 }'->>'$."MNO_123$xyz"';
+} 789
+
+do_execsql_test 1.6 {
+ SELECT json('{ MNO_123$xyz : 789 }');
+} [list {{"MNO_123$xyz":789}}]
+
+do_catchsql_test 1.10 {
+ SELECT json('{ MNO_123/xyz : 789 }');
+} {1 {malformed JSON}}
+
+do_execsql_test 1.11 {
+ SELECT '{ MNO_123æxyz : 789 }'->>'MNO_123æxyz';
+} {789}
+
+###############################################################################
+# 2) Objects may have a single trailing comma.
+
+do_execsql_test 2.1 {
+ WITH c(x) AS (VALUES('{"a":5, "b":6, }'))
+ SELECT x->>'b', json(x), json_valid(x), NOT json_error_position(x) FROM c;
+} {6 {{"a":5,"b":6}} 0 1}
+do_execsql_test 2.2 {
+ SELECT '{a:5, b:6 , }'->>'b';
+} 6
+do_catchsql_test 2.3 {
+ SELECT '{a:5, b:6 ,, }'->>'b';
+} {1 {malformed JSON}}
+do_catchsql_test 2.4 {
+ SELECT '{a:5, b:6, ,}'->>'b';
+} {1 {malformed JSON}}
+
+###############################################################################
+# 3) Arrays may have a single trailing comma.
+
+do_execsql_test 3.1 {
+ WITH c(x) AS (VALUES('[5, 6,]'))
+ SELECT x->>1, json(x), json_valid(x), NOT json_error_position(x) FROM c;
+} {6 {[5,6]} 0 1}
+do_execsql_test 3.2 {
+ SELECT '[5, 6 , ]'->>1;
+} 6
+do_catchsql_test 3.3 {
+ SELECT '[5, 6,,]'->>1;
+} {1 {malformed JSON}}
+do_catchsql_test 3.4 {
+ SELECT '[5, 6 , , ]'->>1;
+} {1 {malformed JSON}}
+
+###############################################################################
+# 4) Strings may be single quoted.
+
+do_execsql_test 4.1 {
+ WITH c(x) AS (VALUES('{"a": ''abcd''}'))
+ SELECT x->>'a', json(x), json_valid(x), NOT json_error_position(x) FROM c;
+} {abcd {{"a":"abcd"}} 0 1}
+do_execsql_test 4.2 {
+ SELECT '{b: 123, ''a'': ''ab\''cd''}'->>'a';
+} {ab'cd}
+
+###############################################################################
+# 5) Strings may span multiple lines by escaping new line characters.
+
+do_execsql_test 5.1 {
+ WITH c(x) AS (VALUES('{a: "abc'||char(0x5c,0x0a)||'xyz"}'))
+ SELECT x->>'a', json(x), json_valid(x), NOT json_error_position(x) FROM c;
+} {abcxyz {{"a":"abcxyz"}} 0 1}
+do_execsql_test 5.2 {
+ SELECT ('{a: "abc'||char(0x5c,0x0d)||'xyz"}')->>'a';
+} {abcxyz}
+do_execsql_test 5.3 {
+ SELECT ('{a: "abc'||char(0x5c,0x0d,0x0a)||'xyz"}')->>'a';
+} {abcxyz}
+do_execsql_test 5.4 {
+ SELECT ('{a: "abc'||char(0x5c,0x2028)||'xyz"}')->>'a';
+} {abcxyz}
+do_execsql_test 5.5 {
+ SELECT ('{a: "abc'||char(0x5c,0x2029)||'xyz"}')->>'a';
+} {abcxyz}
+
+
+###############################################################################
+# 6) Strings may include character escapes.
+
+do_execsql_test 6.1 {
+ SELECT ('{a: "abc'||char(0x5c,0x27)||'xyz"}')->>'a';
+} {abc'xyz}
+do_execsql_test 6.2 {
+ SELECT ('{a: "abc'||char(0x5c,0x22)||'xyz"}')->>'a';
+} {abc"xyz}
+do_execsql_test 6.3 {
+ SELECT ('{a: "abc'||char(0x5c,0x5c)||'xyz"}')->>'a';
+} {{abc\xyz}}
+do_execsql_test 6.4 {
+ SELECT hex(('{a: "abc\bxyz"}')->>'a');
+} {6162630878797A}
+do_execsql_test 6.5 {
+ SELECT hex(('{a: "abc\f\n\r\t\vxyz"}')->>'a');
+} {6162630C0A0D090B78797A}
+do_execsql_test 6.6 {
+ SELECT hex(('{a: "abc\0xyz"}')->>'a');
+} {6162630078797A}
+do_execsql_test 6.7 {
+ SELECT '{a: "abc\x35\x4f\x6Exyz"}'->>'a';
+} {abc5Onxyz}
+do_execsql_test 6.8 {
+ SELECT '{a: "\x6a\x6A\x6b\x6B\x6c\x6C\x6d\x6D\x6e\x6E\x6f\x6F"}'->>'a';
+} {jjkkllmmnnoo}
+
+###############################################################################
+# 7) Numbers may be hexadecimal.
+
+do_execsql_test 7.1 {
+ SELECT '{a: 0x0}'->>'a';
+} 0
+do_execsql_test 7.2 {
+ SELECT '{a: -0x0}'->>'a';
+} 0
+do_execsql_test 7.3 {
+ SELECT '{a: +0x0}'->>'a';
+} 0
+do_execsql_test 7.4 {
+ SELECT '{a: 0xabcdef}'->>'a';
+} 11259375
+do_execsql_test 7.5 {
+ SELECT '{a: -0xaBcDeF}'->>'a';
+} -11259375
+do_execsql_test 7.6 {
+ SELECT '{a: +0xABCDEF}'->>'a';
+} 11259375
+
+###############################################################################
+# 8) Numbers may have a leading or trailing decimal point.
+
+do_execsql_test 8.1 {
+ WITH c(x) AS (VALUES('{x: 4.}')) SELECT x->>'x', json(x) FROM c;
+} {4.0 {{"x":4.0}}}
+do_execsql_test 8.2 {
+ WITH c(x) AS (VALUES('{x: +4.}')) SELECT x->>'x', json(x) FROM c;
+} {4.0 {{"x":4.0}}}
+do_execsql_test 8.3 {
+ WITH c(x) AS (VALUES('{x: -4.}')) SELECT x->>'x', json(x) FROM c;
+} {-4.0 {{"x":-4.0}}}
+do_execsql_test 8.3 {
+ WITH c(x) AS (VALUES('{x: .5}')) SELECT x->>'x', json(x) FROM c;
+} {0.5 {{"x":0.5}}}
+do_execsql_test 8.4 {
+ WITH c(x) AS (VALUES('{x: -.5}')) SELECT x->>'x', json(x) FROM c;
+} {-0.5 {{"x":-0.5}}}
+do_execsql_test 8.5 {
+ WITH c(x) AS (VALUES('{x: +.5}')) SELECT x->>'x', json(x) FROM c;
+} {0.5 {{"x":0.5}}}
+do_execsql_test 8.6 {
+ WITH c(x) AS (VALUES('{x: 4.e0}')) SELECT x->>'x', json(x) FROM c;
+} {4.0 {{"x":4.0e0}}}
+do_execsql_test 8.7 {
+ WITH c(x) AS (VALUES('{x: +4.e1}')) SELECT x->>'x', json(x) FROM c;
+} {40.0 {{"x":4.0e1}}}
+do_execsql_test 8.8 {
+ WITH c(x) AS (VALUES('{x: -4.e2}')) SELECT x->>'x', json(x) FROM c;
+} {-400.0 {{"x":-4.0e2}}}
+do_execsql_test 8.9 {
+ WITH c(x) AS (VALUES('{x: .5e3}')) SELECT x->>'x', json(x) FROM c;
+} {500.0 {{"x":0.5e3}}}
+do_execsql_test 8.10 {
+ WITH c(x) AS (VALUES('{x: -.5e-1}')) SELECT x->>'x', json(x) FROM c;
+} {-0.05 {{"x":-0.5e-1}}}
+do_execsql_test 8.11 {
+ WITH c(x) AS (VALUES('{x: +.5e-2}')) SELECT x->>'x', json(x) FROM c;
+} {0.005 {{"x":0.5e-2}}}
+
+
+###############################################################################
+# 9) Numbers may be IEEE 754 positive infinity, negative infinity, and NaN.
+
+do_execsql_test 9.1 {
+ WITH c(x) AS (VALUES('{x: +Infinity}')) SELECT x->>'x', json(x) FROM c;
+} {Inf {{"x":9e999}}}
+do_execsql_test 9.2 {
+ WITH c(x) AS (VALUES('{x: -Infinity}')) SELECT x->>'x', json(x) FROM c;
+} {-Inf {{"x":-9e999}}}
+do_execsql_test 9.3 {
+ WITH c(x) AS (VALUES('{x: Infinity}')) SELECT x->>'x', json(x) FROM c;
+} {Inf {{"x":9e999}}}
+do_execsql_test 9.4 {
+ WITH c(x) AS (VALUES('{x: NaN}')) SELECT x->>'x', json(x) FROM c;
+} {{} {{"x":null}}}
+
+###############################################################################
+# 10) Numbers may begin with an explicit plus sign.
+
+do_execsql_test 10.1 {
+ SELECT '{a: +123}'->'a';
+} 123
+
+###############################################################################
+# 11) Single and multi-line comments are allowed.
+
+do_execsql_test 11.1 {
+ SELECT ' /* abc */ { /*def*/ aaa /* xyz */ : // to the end of line
+ 123 /* xyz */ , /* 123 */ }'->>'aaa';
+} 123
+
+###############################################################################
+# 12) Additional white space characters are allowed.
+
+do_execsql_test 12.1 {
+ SELECT (char(0x09,0x0a,0x0b,0x0c,0x0d,0x20,0xa0,0x2028,0x2029)
+ || '{a: "xyz"}')->>'a';
+} xyz
+do_execsql_test 12.2 {
+ SELECT ('{a:' || char(0x09,0x0a,0x0b,0x0c,0x0d,0x20,0xa0,0x2028,0x2029)
+ || '"xyz"}')->>'a';
+} xyz
+do_execsql_test 12.3 {
+ SELECT (char(0x1680,0x2000,0x2001,0x2002,0x2003,0x2004,0x2005,
+ 0x2006,0x2007,0x2008,0x2009,0x200a,0x3000,0xfeff)
+ || '{a: "xyz"}')->>'a';
+} xyz
+do_execsql_test 12.4 {
+ SELECT ('{a: ' ||char(0x1680,0x2000,0x2001,0x2002,0x2003,0x2004,0x2005,
+ 0x2006,0x2007,0x2008,0x2009,0x200a,0x3000,0xfeff)
+ || ' "xyz"}')->>'a';
+} xyz
+
+# 2023-11-08 forum/forumpost/ddcad3e884
+#
+do_execsql_test 13.1 {
+ SELECT json('{x:''a "b" c''}');
+} {{{"x":"a \"b\" c"}}}
+
+finish_test
diff --git a/test/json502.test b/test/json502.test
new file mode 100644
index 0000000..cc14b8c
--- /dev/null
+++ b/test/json502.test
@@ -0,0 +1,67 @@
+# 2023-04-28
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for the JSON5 enhancements to the
+# JSON SQL functions extension to the SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix json502
+
+ifcapable vtab {
+
+do_execsql_test 1.1 {
+ CREATE TABLE t1(x JSON);
+ INSERT INTO t1(x) VALUES('{a:{b:{c:"hello",},},}');
+ SELECT fullkey FROM t1, json_tree(x);
+} {{$} {$.a} {$.a.b} {$.a.b.c}}
+
+}
+
+do_execsql_test 2.1 {
+ SELECT json_error_position('{a:null,{"h":[1,[1,2,3]],"j":"abc"}:true}');
+} 9
+do_catchsql_test 2.2 {
+ SELECT json('{a:null,{"h":[1,[1,2,3]],"j":"abc"}:true}');
+} {1 {malformed JSON}}
+do_catchsql_test 2.3 {
+ SELECT '{a:null,{"h":[1,[1,2,3]],"j":"abc"}:true}'->'$h[#-1]';
+} {1 {malformed JSON}}
+
+# Verify that escaped label names are compared correctly.
+#
+do_execsql_test 3.1 {
+ SELECT '{"a\x62c":123}' ->> 'abc';
+} 123
+do_execsql_test 3.2 {
+ SELECT '{"abc":123}' ->> 'a\x62c';
+} 123
+
+db null null
+do_execsql_test 3.3 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(json_insert('{}','$.a\',111,'$."b\\"',222));
+ INSERT INTO t1 VALUES(jsonb_insert('{}','$.a\',111,'$."b\\"',222));
+ SELECT x->'$.a\', x->'$.a\\', x->'$."a\\"', x->'$."b\\"' FROM t1;
+} {111 null 111 222 111 null 111 222}
+
+do_execsql_test 3.4 {
+ SELECT json_patch('{"a\x62c":123}','{"ab\x63":456}') ->> 'abc';
+} 456
+
+ifcapable vtab {
+ do_execsql_test 4.1 {
+ SELECT * FROM json_tree('{"\u0017":1}','$."\x17"');
+ } {{\x17} 1 integer 1 1 null {$."\x17"} {$}}
+}
+
+finish_test
diff --git a/test/jsonb01.test b/test/jsonb01.test
new file mode 100644
index 0000000..8f16428
--- /dev/null
+++ b/test/jsonb01.test
@@ -0,0 +1,53 @@
+# 2023-11-15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# Test cases for JSONB
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test jsonb01-1.1 {
+ CREATE TABLE t1(x JSON BLOB);
+ INSERT INTO t1 VALUES(jsonb('{a:5,b:{x:10,y:11},c:[1,2,3,4]}'));
+}
+foreach {id path res} {
+ 1 {$.a} {{{"b":{"x":10,"y":11},"c":[1,2,3,4]}}}
+ 2 {$.b} {{{"a":5,"c":[1,2,3,4]}}}
+ 3 {$.c} {{{"a":5,"b":{"x":10,"y":11}}}}
+ 4 {$.d} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}}
+ 5 {$.b.x} {{{"a":5,"b":{"y":11},"c":[1,2,3,4]}}}
+ 6 {$.b.y} {{{"a":5,"b":{"x":10},"c":[1,2,3,4]}}}
+ 7 {$.c[0]} {{{"a":5,"b":{"x":10,"y":11},"c":[2,3,4]}}}
+ 8 {$.c[1]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,3,4]}}}
+ 9 {$.c[2]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,4]}}}
+ 10 {$.c[3]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3]}}}
+ 11 {$.c[4]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}}
+ 12 {$.c[#]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}}
+ 13 {$.c[#-1]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3]}}}
+ 14 {$.c[#-2]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,4]}}}
+ 15 {$.c[#-3]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,3,4]}}}
+ 16 {$.c[#-4]} {{{"a":5,"b":{"x":10,"y":11},"c":[2,3,4]}}}
+ 17 {$.c[#-5]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}}
+ 18 {$.c[#-6]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}}
+} {
+ do_execsql_test jsonb01-1.2.$id.1 {
+ SELECT json(jsonb_remove(x,$path)) FROM t1;
+ } $res
+ do_execsql_test jsonb01-1.2.$id.2 {
+ SELECT json_remove(x,$path) FROM t1;
+ } $res
+}
+
+do_catchsql_test jsonb01-2.0 {
+ SELECT x'8ce6ffffffff171333' -> '$';
+} {1 {malformed JSON}}
+
+finish_test