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
This commit is contained in:
drh
2026-01-17 19:01:37 +00:00
parent 6f3a679a7d
commit 4720c16223
4 changed files with 109 additions and 25 deletions
+7 -9
View File
@@ -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.
+1 -1
View File
@@ -1 +1 @@
d6c82c8248f8b1ce3d2c01a92e7d1058beac2b2ccac3b122a2cd385c77dc76df
96b7d2c60a3f8a9f9d69313bad599a3a5ad335cb382263e4254e2d20929d5e55
+29 -15
View 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;
}
+72
View File
@@ -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