From: drh <> Date: Sat, 17 Jan 2026 19:01:37 +0000 (+0000) Subject: Raise an error if any PATH argument to json_array_insert() is not the path X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4720c162232918b84a7d11107edd346a26663ee9;p=thirdparty%2Fsqlite.git Raise an error if any PATH argument to json_array_insert() is not the path of an array element. Add some test cases for json_array_insert(). FossilOrigin-Name: 96b7d2c60a3f8a9f9d69313bad599a3a5ad335cb382263e4254e2d20929d5e55 --- diff --git a/manifest b/manifest index 8638dbc376..7c6c878aa3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snew\sSQL\sfunction:\sjson_array_insert().\s\sSuggested\sin\n[forum:/forumpost/2026-01-17T10:40:39z|forum\sthread\s2026-01-17T10:40:39z].\nThis\scheck-in\sseems\sto\swork,\sbut\scontains\sno\stest\scases.\s\sAlso,\sno\serror\nis\sraised\sif\sthe\sPATH\sdoes\snot\sreference\san\sarray.\s\sPrototype\sonly. -D 2026-01-17T14:23:28.371 +C Raise\san\serror\sif\sany\sPATH\sargument\sto\sjson_array_insert()\sis\snot\sthe\spath\nof\san\sarray\selement.\s\sAdd\ssome\stest\scases\sfor\sjson_array_insert(). +D 2026-01-17T19:01:37.597 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -699,7 +699,7 @@ F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf F src/hwtime.h 21c2cf1f736e7b97502c3674d0c386db3f06870d6f10d0cf8174e2a4b8cb726e F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd -F src/json.c 091c280965ebc673b8d0d15bd132b3243298d7603ce25749c9f0de28429e328e +F src/json.c 95c20b077a8ec3b471122d312a907b1e5298ae93f924b0a96640022b6420a941 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c d6559d2b39c9bde6b104b83adeafbe5db3a514aae4d3d40afc58de522a03043b F src/main.c 21fb86045bbf6b6329251a0ce6771735b6c71287cc9fcda1f2005d4ac5f25b52 @@ -1361,6 +1361,7 @@ F test/json105.test 043838b56e68f3252a0dcf5be1689016f6f3f05056f8dcfcdc9d074f4d93 F test/json106.test 4aed3afd16549045d198a8d9cea00deea96e1f2ecf55864dce96cac558b8abef F test/json107.test 59054e815c8f6b67d634d44ace421cf975828fb5651c4460aa66015c8e19d562 F test/json108.test 0a5f1e2d4b35a1bc33052563d2a5ede03052e2099e58cb424547656c898e0f49 +F test/json109.test 441cea5d73c24a1a34d284101740dfae5a082237c048c8a66b03aeebe5e3643e F test/json501.test b95e2d14988b682a5cadf079dd6162f0f85fb74cd59c6b1f1624110104a974eb F test/json502.test 4ef68e4f272dfb083d4cbceb4e9e51d67ec1186a185e0c13637c50a4dc2f9796 F test/jsonb01.test f4cdfb4cf5a0c940091b17675ed9583f45add0c938f07d65b0de0e19d3a9a101 @@ -2192,11 +2193,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P f8f89d2e10f06b54acf58a3b0543aef749c3d2b65670d4cc71530dea7997651e -R 5eaa4c6199eb51b03923e8f6a4a1b985 -T *branch * json_array_insert -T *sym-json_array_insert * -T -sym-trunk * +P d6c82c8248f8b1ce3d2c01a92e7d1058beac2b2ccac3b122a2cd385c77dc76df +R 767eeb44449b6ebf81d3929fe2071be1 U drh -Z 59a65265ad7dd2e433c1fd0eebddff23 +Z 9b7f5476077742ae15428954dd628d31 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 857cf1ce1e..553cf97fc0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d6c82c8248f8b1ce3d2c01a92e7d1058beac2b2ccac3b122a2cd385c77dc76df +96b7d2c60a3f8a9f9d69313bad599a3a5ad335cb382263e4254e2d20929d5e55 diff --git a/src/json.c b/src/json.c index fb8dbf2e89..c33ce60ad3 100644 --- a/src/json.c +++ b/src/json.c @@ -2874,7 +2874,8 @@ static int jsonLabelCompare( */ #define JSON_LOOKUP_ERROR 0xffffffff #define JSON_LOOKUP_NOTFOUND 0xfffffffe -#define JSON_LOOKUP_PATHERROR 0xfffffffd +#define JSON_LOOKUP_NOTARRAY 0xfffffffd +#define JSON_LOOKUP_PATHERROR 0xfffffffc #define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) /* Forward declaration */ @@ -2903,7 +2904,7 @@ static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); static u32 jsonCreateEditSubstructure( JsonParse *pParse, /* The original JSONB that is being edited */ JsonParse *pIns, /* Populate this with the blob data to insert */ - const char *zTail /* Tail of the path that determins substructure */ + const char *zTail /* Tail of the path that determines substructure */ ){ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; int rc; @@ -2968,7 +2969,11 @@ static u32 jsonLookupStep( /* Already exists, so json_insert() is a no-op */ }else if( pParse->eEdit==JEDIT_AINS ){ /* json_array_insert() */ - jsonBlobEdit(pParse, iRoot, 0, pParse->aIns, pParse->nIns); + if( zPath[-1]!=']' ){ + return JSON_LOOKUP_NOTARRAY; + }else{ + jsonBlobEdit(pParse, iRoot, 0, pParse->aIns, pParse->nIns); + } }else{ /* json_set() or json_replace() */ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); @@ -3041,6 +3046,9 @@ static u32 jsonLookupStep( testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); testcase( pParse->eEdit==JEDIT_AINS ); + if( pParse->eEdit==JEDIT_AINS && sqlite3_strglob("*]",&zPath[i])!=0 ){ + return JSON_LOOKUP_NOTARRAY; + } memset(&ix, 0, sizeof(ix)); ix.db = pParse->db; jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); @@ -3441,9 +3449,15 @@ static int jsonFunctionArgToBlob( */ static char *jsonBadPathError( sqlite3_context *ctx, /* The function call containing the error */ - const char *zPath /* The path with the problem */ + const char *zPath, /* The path with the problem */ + int rc /* Maybe JSON_LOOKUP_NOTARRAY */ ){ - char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); + char *zMsg; + if( rc==JSON_LOOKUP_NOTARRAY ){ + zMsg = sqlite3_mprintf("not an array element: %Q", zPath); + }else{ + zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); + } if( ctx==0 ) return zMsg; if( zMsg ){ sqlite3_result_error(ctx, zMsg, -1); @@ -3518,7 +3532,7 @@ jsonInsertIntoBlob_patherror: if( rc==JSON_LOOKUP_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else{ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, rc); } return; } @@ -3960,7 +3974,7 @@ static void jsonArrayLengthFunc( if( i==JSON_LOOKUP_NOTFOUND ){ /* no-op */ }else if( i==JSON_LOOKUP_PATHERROR ){ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); } @@ -4065,7 +4079,7 @@ static void jsonExtractFunc( j = jsonLookupStep(p, 0, jx.zBuf, 0); jsonStringReset(&jx); }else{ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); goto json_extract_error; } if( jnBlob ){ @@ -4100,7 +4114,7 @@ static void jsonExtractFunc( sqlite3_result_error(ctx, "malformed JSON", -1); goto json_extract_error; }else{ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); goto json_extract_error; } } @@ -4429,7 +4443,7 @@ static void jsonRemoveFunc( if( rc==JSON_LOOKUP_NOTFOUND ){ continue; /* No-op */ }else if( rc==JSON_LOOKUP_PATHERROR ){ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, rc); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); } @@ -4441,7 +4455,7 @@ static void jsonRemoveFunc( return; json_remove_patherror: - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); json_remove_done: jsonParseFree(p); @@ -4521,7 +4535,7 @@ static void jsonTypeFunc( zPath = (const char*)sqlite3_value_text(argv[1]); if( zPath==0 ) goto json_type_done; if( zPath[0]!='$' ){ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); goto json_type_done; } i = jsonLookupStep(p, 0, zPath+1, 0); @@ -4529,7 +4543,7 @@ static void jsonTypeFunc( if( i==JSON_LOOKUP_NOTFOUND ){ /* no-op */ }else if( i==JSON_LOOKUP_PATHERROR ){ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); } @@ -5433,7 +5447,7 @@ static int jsonEachFilter( if( zRoot==0 ) return SQLITE_OK; if( zRoot[0]!='$' ){ sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } @@ -5451,7 +5465,7 @@ static int jsonEachFilter( return SQLITE_OK; } sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } diff --git a/test/json109.test b/test/json109.test new file mode 100644 index 0000000000..1631a1f0ff --- /dev/null +++ b/test/json109.test @@ -0,0 +1,72 @@ +# 2026-01-17 +# +# 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 json_array_insert(). +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix json109 + +do_execsql_test 1.1 { + SELECT json_array_insert('[1,2,3]','$[0]',999,'$[0]',888); +} {{[888,999,1,2,3]}} +do_execsql_test 1.2 { + SELECT json_array_insert('[1,2,3]','$[0]',999,'$[#]',888); +} {{[999,1,2,3,888]}} +do_execsql_test 1.3 { + SELECT json_array_insert('[1,2,3]','$[1]',888); +} {{[1,888,2,3]}} +do_execsql_test 1.4 { + SELECT json_array_insert('[1,2,3]','$[2]',888); +} {{[1,2,888,3]}} +do_execsql_test 1.5 { + SELECT json_array_insert('[1,2,3]','$[3]',888); +} {{[1,2,3,888]}} +do_execsql_test 1.6 { + SELECT json_array_insert('[1,2,3]','$[#-1]',888); +} {{[1,2,888,3]}} +do_execsql_test 1.7 { + SELECT json_array_insert('[1,2,3]','$[#-2]',888); +} {{[1,888,2,3]}} +do_execsql_test 1.8 { + SELECT json_array_insert('[1,2,3]','$[#-3]',888); +} {{[888,1,2,3]}} +do_execsql_test 1.9 { + SELECT json_array_insert('[1,2,3]','$[#-4]',888); +} {{[1,2,3]}} + +do_catchsql_test 2.1 { + SELECT json_array_insert('{a:[1,2,3]}','$.a',888); +} {1 {not an array element: '$.a'}} +do_catchsql_test 2.2 { + SELECT json_array_insert('{a:[1,2,3]}','$.b',888); +} {1 {not an array element: '$.b'}} +do_catchsql_test 2.3 { + SELECT json_array_insert('{a:[1,2,3]}','$.b[0]',888); +} {0 {{{"a":[1,2,3],"b":[888]}}}} +do_catchsql_test 2.4 { + SELECT json_array_insert('{a:[1,2,3]}','$.b.c.d[0]',888); +} {0 {{{"a":[1,2,3],"b":{"c":{"d":[888]}}}}}} +do_catchsql_test 2.5 { + SELECT json_array_insert('{a:[1,2,3]}','$.b.c.d[0',888); +} {1 {not an array element: '$.b.c.d[0'}} +do_catchsql_test 2.6 { + SELECT json_array_insert('{a:[1,2,3]}','$.b.c.d',888); +} {1 {not an array element: '$.b.c.d'}} +do_catchsql_test 2.7 { + SELECT json_array_insert('{a:[1,2,3]}','$[0]',888); +} {0 {{{"a":[1,2,3]}}}} +do_catchsql_test 2.8 { + SELECT json_array_insert('{a:[1,2,3]}','$.b[0]',888,'$.a[1]','999','$.c',0); +} {1 {not an array element: '$.c'}} + +finish_test