]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Raise an error if any PATH argument to json_array_insert() is not the path json_array_insert
authordrh <>
Sat, 17 Jan 2026 19:01:37 +0000 (19:01 +0000)
committerdrh <>
Sat, 17 Jan 2026 19:01:37 +0000 (19:01 +0000)
of an array element.  Add some test cases for json_array_insert().

FossilOrigin-Name: 96b7d2c60a3f8a9f9d69313bad599a3a5ad335cb382263e4254e2d20929d5e55

manifest
manifest.uuid
src/json.c
test/json109.test [new file with mode: 0644]

index 8638dbc376d8ffe8723a68ca9a56c8381d277a29..7c6c878aa314f62a8effdde4acb16964646a4d83 100644 (file)
--- 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.
index 857cf1ce1e593453be0791c369f588343c96aa08..553cf97fc0b2c060d0f72fb0e86d4f71a9e15b3c 100644 (file)
@@ -1 +1 @@
-d6c82c8248f8b1ce3d2c01a92e7d1058beac2b2ccac3b122a2cd385c77dc76df
+96b7d2c60a3f8a9f9d69313bad599a3a5ad335cb382263e4254e2d20929d5e55
index fb8dbf2e895b5b67394bbcbc62cd03b1ad487d9a..c33ce60ad3f2846acc88dd44bfead075bad78af6 100644 (file)
@@ -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( j<p->nBlob ){
@@ -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 (file)
index 0000000..1631a1f
--- /dev/null
@@ -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