-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
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
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
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.
*/
#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 */
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;
/* 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);
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);
*/
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);
if( rc==JSON_LOOKUP_ERROR ){
sqlite3_result_error(ctx, "malformed JSON", -1);
}else{
- jsonBadPathError(ctx, zPath);
+ jsonBadPathError(ctx, zPath, rc);
}
return;
}
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);
}
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 ){
sqlite3_result_error(ctx, "malformed JSON", -1);
goto json_extract_error;
}else{
- jsonBadPathError(ctx, zPath);
+ jsonBadPathError(ctx, zPath, 0);
goto json_extract_error;
}
}
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);
}
return;
json_remove_patherror:
- jsonBadPathError(ctx, zPath);
+ jsonBadPathError(ctx, zPath, 0);
json_remove_done:
jsonParseFree(p);
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);
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);
}
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;
}
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;
}
--- /dev/null
+# 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