From c2474105ca39070a40f53b3c36550c9b169c5469 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 25 Nov 2023 18:11:11 +0000 Subject: [PATCH] Generate the fullkey and path columns of json_tree(). FossilOrigin-Name: ffaa468ab8871906121df9ee5ef3dc00129a0086ed9c18831ecda69bf7f71455 --- manifest | 12 +++--- manifest.uuid | 2 +- src/json.c | 104 +++++++++++++++++++++++++------------------------- 3 files changed, 60 insertions(+), 58 deletions(-) diff --git a/manifest b/manifest index b7f38de853..af86c512c2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Handle\sthe\spath\sargument\sto\sjson_tree()\sand\sjson_each(). -D 2023-11-25T13:40:19.363 +C Generate\sthe\sfullkey\sand\spath\scolumns\sof\sjson_tree(). +D 2023-11-25T18:11:11.468 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c ff48d52463493f5f40d6e1e14ac3dc9277c40290df140a52bd17e7a562f766ea +F src/json.c 9a5ff28f7b55e83b92eeab9285eb4b0132f11526620565e2d5b81fb84d4e8611 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3df891cb11feee65e239ee2506eda34a9688341f05210d7c2e25a05338cb71ad -R 86973d4c807995bd645f2f5dbc7eac48 +P fded888469565b2a4687185a926bd23fccfbf167c8bebe6c10696fc7f972f41e +R 90c664426338898baffedf8dcba41bea U drh -Z e2d0e1a86b5ddc3117954e8a28079ed7 +Z e9b6e925dfee6d388855b55f2b458a4c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 555d0938d3..acd1f46af1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fded888469565b2a4687185a926bd23fccfbf167c8bebe6c10696fc7f972f41e \ No newline at end of file +ffaa468ab8871906121df9ee5ef3dc00129a0086ed9c18831ecda69bf7f71455 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 503fff9368..c8269436eb 100644 --- a/src/json.c +++ b/src/json.c @@ -796,7 +796,6 @@ static void jsonAppendSqlValue( } } - /* Make the text in p (which is probably a generated JSON text string) ** the result of the SQL function. ** @@ -5580,6 +5579,7 @@ struct JsonParent { u32 iHead; /* Start of object or array */ u32 iValue; /* Start of the value */ u32 iEnd; /* First byte past the end */ + u32 nPath; /* Length of path */ i64 iKey; /* Key for JSONB_ARRAY */ }; @@ -5598,6 +5598,7 @@ struct JsonEachCursor { char *zJson; /* Input JSON */ char *zRoot; /* Path by which to filter zJson */ sqlite3 *db; /* Database connection */ + JsonString path; /* Current path */ JsonParse sParse; /* Parse of the input JSON */ }; typedef struct JsonEachConnection JsonEachConnection; @@ -5666,6 +5667,7 @@ static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->db = pVtab->db; + jsonStringZero(&pCur->path); *ppCursor = &pCur->base; return SQLITE_OK; } @@ -5685,6 +5687,7 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ static void jsonEachCursorReset(JsonEachCursor *p){ sqlite3_free(p->zRoot); jsonParseReset(&p->sParse); + jsonStringReset(&p->path); sqlite3DbFree(p->db, p->aParent); p->iRowid = 0; p->i = 0; @@ -5701,6 +5704,7 @@ static void jsonEachCursorReset(JsonEachCursor *p){ static int jsonEachClose(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; jsonEachCursorReset(p); + sqlite3_free(cur); return SQLITE_OK; } @@ -5727,6 +5731,39 @@ static int jsonSkipLabel(JsonEachCursor *p){ } } +/* +** Append the path name for the current element. +*/ +static void jsonAppendPathName(JsonEachCursor *p){ + assert( p->nParent>0 ); + assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT ); + if( p->eType==JSONB_ARRAY ){ + jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey); + }else{ + u32 n, sz = 0, k, i; + const char *z; + int needQuote = 0; + n = jsonbPayloadSize(&p->sParse, p->i, &sz); + k = p->i + n; + z = (const char*)&p->sParse.aBlob[k]; + if( sz==0 || !sqlite3Isalpha(z[0]) ){ + needQuote = 1; + }else{ + for(i=0; ipath,".\"%.*s\"", sz, z); + }else{ + jsonPrintf(sz+2,&p->path,".%.*s", sz, z); + } + } +} + /* Advance the cursor to the next element for json_tree() */ static int jsonEachNext(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; @@ -5789,17 +5826,21 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ p->aParent = pNew; } levelChange = 1; - pParent = &p->aParent[p->nParent++]; + pParent = &p->aParent[p->nParent]; pParent->iHead = p->i; pParent->iValue = i; pParent->iEnd = i + n + sz; pParent->iKey = -1; + pParent->nPath = (u32)p->path.nUsed; + if( p->eType && p->nParent ) jsonAppendPathName(p); + p->nParent++; p->i = i + n; }else{ p->i = i + n + sz; } while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ p->nParent--; + p->path.nUsed = p->aParent[p->nParent].nPath; levelChange = 1; } if( levelChange ){ @@ -6034,53 +6075,16 @@ static int jsonEachColumn( break; } case JEACH_FULLKEY: { -#if 0 - u32 i; - JsonString x; - jsonStringInit(&x, ctx); - for(i=0; inParent; i++){ - jsonPrintf(200,&x,"(%u,%u,%u,%lld)", - p->aParent[i].iHead, - p->aParent[i].iValue, - p->aParent[i].iEnd, - p->aParent[i].iKey); - } - jsonReturnString(&x); -#endif -#if 0 - JsonString x; - jsonStringInit(&x, ctx); - if( p->bRecursive ){ - jsonEachComputePath(p, &x, p->i); - }else{ - if( p->zRoot ){ - jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); - }else{ - jsonAppendChar(&x, '$'); - } - if( p->eType==JSON_ARRAY ){ - jsonPrintf(30, &x, "[%d]", p->iRowid); - }else if( p->eType==JSON_OBJECT ){ - jsonAppendObjectPathElementOfNode(&x, pThis); - } - } - jsonReturnString(&x); -#endif + u64 nBase = p->path.nUsed; + if( p->nParent ) jsonAppendPathName(p); + sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, + SQLITE_TRANSIENT, SQLITE_UTF8); + p->path.nUsed = nBase; break; } case JEACH_PATH: { -#if 0 - if( p->bRecursive ){ - JsonString x; - jsonStringInit(&x, ctx); - jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); - jsonReturnString(&x); - break; - } - /* For json_each() path and root are the same so fall through - ** into the root case */ - /* no break */ deliberate_fall_through -#endif + sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, + SQLITE_TRANSIENT, SQLITE_UTF8); break; } default: { @@ -6209,10 +6213,6 @@ static int jsonEachFilter( if( idxNum==3 ){ zRoot = (const char*)sqlite3_value_text(argv[1]); if( zRoot==0 ) return SQLITE_OK; - n = sqlite3_value_bytes(argv[1]); - p->zRoot = sqlite3_malloc64( n+1 ); - if( p->zRoot==0 ) return SQLITE_NOMEM; - memcpy(p->zRoot, zRoot, (size_t)n+1); if( zRoot[0]!='$' ){ sqlite3_free(cur->pVtab->zErrMsg); cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot, 0); @@ -6223,7 +6223,7 @@ static int jsonEachFilter( i = p->i = 0; p->eType = 0; }else{ - i = jsonLookupBlobStep(&p->sParse, 0, p->zRoot+1, 0); + i = jsonLookupBlobStep(&p->sParse, 0, zRoot+1, 0); if( JSON_BLOB_ISERROR(i) ){ p->i = 0; p->eType = 0; @@ -6238,9 +6238,11 @@ static int jsonEachFilter( p->eType = JSONB_ARRAY; } } + jsonAppendRaw(&p->path, zRoot, sqlite3Strlen30(zRoot)); }else{ i = p->i = 0; p->eType = 0; + jsonAppendRaw(&p->path, "$", 1); } p->nParent = 0; p->sParse.isBinary = 1; -- 2.47.2