From: drh <> Date: Tue, 26 Sep 2023 19:30:46 +0000 (+0000) Subject: Add in many jsonb_xxxx() interfaces. Still uses the internal JsonNode X-Git-Tag: version-3.45.0~116^2~142 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=42156fd90c65caa8f9f86a76624408f4dd8a3ef5;p=thirdparty%2Fsqlite.git Add in many jsonb_xxxx() interfaces. Still uses the internal JsonNode representation for transformations and search, but it does at least conform to the desired API design. Largely untested. FossilOrigin-Name: e6045b4e1bf3a8e33926fc12b3c039f5e1002eaecbe277ffa82b0ec271a29d17 --- diff --git a/manifest b/manifest index 06d1240d09..80c8adf66b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sjsonb\sbranch. -D 2023-09-26T15:13:04.116 +C Add\sin\smany\sjsonb_xxxx()\sinterfaces.\s\sStill\suses\sthe\sinternal\sJsonNode\nrepresentation\sfor\stransformations\sand\ssearch,\sbut\sit\sdoes\sat\sleast\sconform\nto\sthe\sdesired\sAPI\sdesign.\s\sLargely\suntested. +D 2023-09-26T19:30:46.857 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,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 ee823133ed6f56ffb42c99fa78814667be70cd1b8106714cff459ee35f5a063e +F src/json.c 6fb31345d252dc6992c707ec7e826ec9f2283b026092e5105f9905664a2210c5 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2122,8 +2122,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 a82ebbac3c542ec7f86d1e8414d7fd166db48450115ee3b26d12b5bb445f5896 7ad38254c37153efa72291d09800693ca60894359548eda877d59defa8c70d49 -R 2f21564c602d3b30115c3421d174134f +P ac242c4d47ec36aab1c2fa5e65e7b595e686f49473b75bd63708d05c59ce3f0f +R 3743d1571a08e262e44d6bfa0fe7cd5d U drh -Z bdae2aab159970d0c164367006c6960b +Z 4bd7fe1646cada0525f1a26ea76a3776 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4de851b60c..a8e9449ee7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac242c4d47ec36aab1c2fa5e65e7b595e686f49473b75bd63708d05c59ce3f0f \ No newline at end of file +e6045b4e1bf3a8e33926fc12b3c039f5e1002eaecbe277ffa82b0ec271a29d17 \ No newline at end of file diff --git a/src/json.c b/src/json.c index cfd37754f8..0b5068a9f0 100644 --- a/src/json.c +++ b/src/json.c @@ -159,6 +159,16 @@ static const char * const jsonType[] = { #define JNODE_LABEL 0x20 /* Is a label of an object */ #define JNODE_JSON5 0x40 /* Node contains JSON5 enhancements */ +/* +** Bit values for the flags passed into jsonExtractFunc() or +** jsonSetFunc() via the user-data value. +*/ +#define JSON_JSON 0x01 /* Result is always JSON */ +#define JSON_SQL 0x02 /* Result is always SQL */ +#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ +#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ +#define JSON_BLOB 0x08 /* Use the BLOB output format */ + /* A single node of parsed JSON. An array of these nodes describes ** a parse of JSON + edits. @@ -847,6 +857,13 @@ static void jsonRenderNode( } } +/* Forward reference */ +static void jsonRenderNodeAsBlob( + JsonParse *pParse, /* the complete parse of the JSON */ + JsonNode *pNode, /* The node to render */ + JsonParse *pOut /* Write the BLOB rendering of JSON here */ +); + /* ** Return a JsonNode and all its descendants as a JSON string. */ @@ -856,12 +873,22 @@ static void jsonReturnJson( sqlite3_context *pCtx, /* Return value for this function */ int bGenerateAlt /* Also store the rendered text in zAlt */ ){ + int flags; JsonString s; if( pParse->oom ){ sqlite3_result_error_nomem(pCtx); return; } - if( pParse->nErr==0 ){ + if( pParse->nErr ){ + return; + } + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); + if( flags & JSON_BLOB ){ + JsonParse x; + memset(&x, 0, sizeof(x)); + jsonRenderNodeAsBlob(pParse, pNode, &x); + sqlite3_result_blob(pCtx, x.aBlob, x.nBlob, sqlite3_free); + }else{ jsonInit(&s, pCtx); jsonRenderNode(pParse, pNode, &s); if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){ @@ -1811,7 +1838,7 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ } /* Forward reference */ -static int jsonParseValueFromBinary(JsonParse *pParse, u32 i); +static int jsonParseValueFromBlob(JsonParse *pParse, u32 i); /* ** Parse a complete JSON string. Return 0 on success or non-zero if there @@ -1830,7 +1857,7 @@ static int jsonParse( if( pParse->isBinary ){ pParse->aBlob = (u8*)pParse->zJson; pParse->nBlob = pParse->nJson; - i = jsonParseValueFromBinary(pParse, 0); + i = jsonParseValueFromBlob(pParse, 0); }else{ i = jsonParseValue(pParse, 0); } @@ -3247,7 +3274,7 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ ** is still in use. It will be removed after all processing translates ** to the new BLOB encoding. */ -static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){ +static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ u8 t; /* Node type */ u32 sz; /* Node size */ u32 x; /* Index of payload start */ @@ -3310,7 +3337,7 @@ static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){ int iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); u32 j = i+x; while( jaNode[pParse->nNode-1].jnFlags |= JNODE_LABEL; @@ -3335,6 +3362,137 @@ static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){ return i+x+sz; } +/* +** Convert pNode that belongs to pParse into the BLOB representation. +** The BLOB representation is added to the pOut->aBlob[] array. +*/ +static void jsonRenderNodeAsBlob( + JsonParse *pParse, /* the complete parse of the JSON */ + JsonNode *pNode, /* The node to render */ + JsonParse *pOut /* Write the BLOB rendering of JSON here */ +){ + assert( pNode!=0 ); + while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){ + u32 idx = (u32)(pNode - pParse->aNode); + u32 i = pParse->iSubst; + while( 1 /*exit-by-break*/ ){ + assert( inNode ); + assert( pParse->aNode[i].eType==JSON_SUBST ); + assert( pParse->aNode[i].eU==4 ); + assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ + pNode = &pParse->aNode[i+1]; + break; + } + i = pParse->aNode[i].u.iPrev; + } + } + switch( pNode->eType ){ + default: { + assert( pNode->eType==JSON_NULL ); + jsonBlobAppendNodeType(pOut, JSONB_NULL, 0); + break; + } + case JSON_TRUE: { + jsonBlobAppendNodeType(pOut, JSONB_TRUE, 0); + break; + } + case JSON_FALSE: { + jsonBlobAppendNodeType(pOut, JSONB_FALSE, 0); + break; + } + case JSON_STRING: { + int op; + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_RAW ){ + if( memchr(pNode->u.zJContent, '"', pNode->n)==0 + && memchr(pNode->u.zJContent, '\\', pNode->n)==0 + ){ + op = JSONB_TEXT; + }else{ + op = JSONB_TEXTRAW; + } + }else if( pNode->jnFlags & JNODE_JSON5 ){ + op = JSONB_TEXT5; + }else{ + op = JSONB_TEXTJ; + } + jsonBlobAppendNodeType(pOut, op, pNode->n); + jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); + break; + } + case JSON_REAL: { + int op; + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_JSON5 ){ + op = JSONB_FLOAT5; + }else{ + assert( pNode->n>0 ); + op = JSONB_FLOAT; + } + jsonBlobAppendNodeType(pOut, op, pNode->n); + jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); + break; + } + case JSON_INT: { + int op; + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_JSON5 ){ + op = JSONB_INT5; + }else{ + assert( pNode->n>0 ); + op = JSONB_INT; + } + jsonBlobAppendNodeType(pOut, op, pNode->n); + jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); + break; + } + case JSON_ARRAY: { + u32 j = 1; + u32 iStart, iThis = pOut->nBlob; + jsonBlobAppendNodeType(pOut, JSONB_ARRAY, 0x7fffffff); + iStart = pOut->nBlob; + for(;;){ + while( j<=pNode->n ){ + if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ + jsonRenderNodeAsBlob(pParse, &pNode[j], pOut); + } + j += jsonNodeSize(&pNode[j]); + } + if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pNode->eU==2 ); + pNode = &pParse->aNode[pNode->u.iAppend]; + j = 1; + } + jsonBlobChangePayloadSize(pOut, iThis, pOut->nBlob - iStart); + break; + } + case JSON_OBJECT: { + u32 j = 1; + u32 iStart, iThis = pOut->nBlob; + jsonBlobAppendNodeType(pOut, JSONB_OBJECT, 0x7fffffff); + iStart = pOut->nBlob; + for(;;){ + while( j<=pNode->n ){ + if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ + jsonRenderNodeAsBlob(pParse, &pNode[j], pOut); + jsonRenderNodeAsBlob(pParse, &pNode[j+1], pOut); + } + j += 1 + jsonNodeSize(&pNode[j+1]); + } + if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pNode->eU==2 ); + pNode = &pParse->aNode[pNode->u.iAppend]; + j = 1; + } + jsonBlobChangePayloadSize(pOut, iThis, pOut->nBlob - iStart); + break; + } + } +} + /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ @@ -3614,15 +3772,6 @@ static void jsonArrayLengthFunc( sqlite3_result_int64(ctx, n); } -/* -** Bit values for the flags passed into jsonExtractFunc() or -** jsonSetFunc() via the user-data value. -*/ -#define JSON_JSON 0x01 /* Result is always JSON */ -#define JSON_SQL 0x02 /* Result is always SQL */ -#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ -#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ - /* ** json_extract(JSON, PATH, ...) ** "->"(JSON,PATH) @@ -4913,26 +5062,32 @@ static sqlite3_module jsonTreeModule = { void sqlite3RegisterJsonFunctions(void){ #ifndef SQLITE_OMIT_JSON static FuncDef aJsonFunc[] = { - JFUNCTION(json, 1, 0, jsonRemoveFunc), - JFUNCTION(json_array, -1, 0, jsonArrayFunc), - JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), - JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), - JFUNCTION(json_error_position,1, 0, jsonErrorFunc), - JFUNCTION(json_extract, -1, 0, jsonExtractFunc), - JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), - JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), - JFUNCTION(json_insert, -1, 0, jsonSetFunc), - JFUNCTION(json_object, -1, 0, jsonObjectFunc), - JFUNCTION(json_patch, 2, 0, jsonPatchFunc), - JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), - JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), - JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), - JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), - JFUNCTION(json_type, 1, 0, jsonTypeFunc), - JFUNCTION(json_type, 2, 0, jsonTypeFunc), - JFUNCTION(json_valid, 1, 0, jsonValidFunc), - JFUNCTION(jsonb, 1, 0, jsonbFunc), - JFUNCTION(jsonb_test2, 1, 0, jsonbTest2), + JFUNCTION(json, 1, 0, jsonRemoveFunc), + JFUNCTION(json_array, -1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), + JFUNCTION(json_error_position,1, 0, jsonErrorFunc), + JFUNCTION(json_extract, -1, 0, jsonExtractFunc), + JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1, 0, jsonSetFunc), + JFUNCTION(json_object, -1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 0, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), + JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), + JFUNCTION(json_type, 1, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 0, jsonValidFunc), + JFUNCTION(jsonb, 1, JSON_BLOB, jsonbFunc), + JFUNCTION(jsonb_test2, 1, 0, jsonbTest2), + JFUNCTION(jsonb_insert, -1, JSON_BLOB, jsonSetFunc), + JFUNCTION(jsonb_patch, 2, JSON_BLOB, jsonPatchFunc), + JFUNCTION(jsonb_quote, 1, JSON_BLOB, jsonQuoteFunc), + JFUNCTION(jsonb_remove, -1, JSON_BLOB, jsonRemoveFunc), + JFUNCTION(jsonb_replace, -1, JSON_BLOB, jsonReplaceFunc), + JFUNCTION(jsonb_set, -1, JSON_ISSET|JSON_BLOB, jsonSetFunc), #if SQLITE_DEBUG JFUNCTION(json_parse, 1, 0, jsonParseFunc), JFUNCTION(json_test1, 1, 0, jsonTest1Func),