From: drh <> Date: Tue, 25 Jul 2023 18:28:03 +0000 (+0000) Subject: Incremental improvements to JSON parsing - trying to fold in the RCStr object. X-Git-Tag: version-3.43.0~114^2~5^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=440e696d5e0b31a2542a01b3fba9eb356fea52ad;p=thirdparty%2Fsqlite.git Incremental improvements to JSON parsing - trying to fold in the RCStr object. FossilOrigin-Name: 4cb15d934a85ebc290fe6dd8cd3bd47b159561ca75d72bbffef30b9ea4623b09 --- diff --git a/manifest b/manifest index f3393df05d..09e5a7cfff 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sminor\sproblem\swith\serror\sreporting\sin\sJSON. -D 2023-07-25T15:43:01.908 +C Incremental\simprovements\sto\sJSON\sparsing\s-\strying\sto\sfold\sin\sthe\sRCStr\sobject. +D 2023-07-25T18:28:03.341 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -598,7 +598,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 8f44f1aadfe1967af70dc9e6390823806f5a42cc301f11df435a1c3d4950308d +F src/json.c 28fe8ed9e63293fd5708d129d4edd4a634887aabb0364685195974e35ee10762 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465 F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002 @@ -2044,8 +2044,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 c1b8725089bb3d006ec69add28f4fcb3f4e79412c7f438b5b1067c2227e77b9c -R 625da2283c68c7b3c0e2eb949802be15 +P c456e4a8999066cd96246327101b3cca78294511a71a2ac07939bb702bfcb5f4 +R 2b5df60a4958d2a70ce11e3569ae2bab U drh -Z 238deba72513ff58266287df7bec917f +Z d10e127820cbbcdc4a84e1b86eb42adb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 70df72c921..a811037656 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c456e4a8999066cd96246327101b3cca78294511a71a2ac07939bb702bfcb5f4 \ No newline at end of file +4cb15d934a85ebc290fe6dd8cd3bd47b159561ca75d72bbffef30b9ea4623b09 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 18c4721455..194d0421eb 100644 --- a/src/json.c +++ b/src/json.c @@ -142,7 +142,7 @@ struct JsonParse { u32 nNode; /* Number of slots of aNode[] used */ u32 nAlloc; /* Number of slots of aNode[] allocated */ JsonNode *aNode; /* Array of nodes containing the parse */ - const char *zJson; /* Original JSON string */ + char *zJson; /* Original JSON string */ u32 *aUp; /* Index of parent of each node */ JsonTask *pClean; /* Cleanup operations prior to freeing this object */ u16 iDepth; /* Nesting depth */ @@ -150,6 +150,8 @@ struct JsonParse { u8 oom; /* Set to true if out of memory */ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ u8 nJPRef; /* Number of references to this object */ + u8 bOwnsJson; /* This object owns zJson and response for freeing it */ + u8 isMod; /* aNode contains edits from the original zJson */ int nJson; /* Length of the zJson string in bytes */ u32 iErr; /* Error location in zJson[] */ u32 iSubst; /* Last known JSON_SUBST node */ @@ -565,24 +567,41 @@ static void jsonParseReset(JsonParse *pParse){ sqlite3_free(pTask); } assert( pParse->nJPRef<=1 ); - sqlite3_free(pParse->aNode); - pParse->aNode = 0; + if( pParse->aNode ){ + sqlite3_free(pParse->aNode); + pParse->aNode = 0; + } pParse->nNode = 0; pParse->nAlloc = 0; - sqlite3_free(pParse->aUp); - pParse->aUp = 0; + if( pParse->aUp ){ + sqlite3_free(pParse->aUp); + pParse->aUp = 0; + } + if( pParse->bOwnsJson ){ + /* Order operations so that if the destructor for pParse->zJson + ** invokes jsonParseFree(), the recursion will terminate harmlessly */ + char *z = pParse->zJson; + pParse->zJson = 0; + pParse->bOwnsJson = 0; + sqlite3RCStrUnref(z); + } } /* ** Free a JsonParse object that was obtained from sqlite3_malloc(). +** +** Note that destroying JsonParse might call sqlite3RCStrUnref() to +** destroy the zJson value. The RCStr object might recursively invoke +** JsonParse to destroy this pParse object again. Take care to ensure +** that this recursive destructor sequence terminates harmlessly. */ static void jsonParseFree(JsonParse *pParse){ if( pParse->nJPRef>1 ){ pParse->nJPRef--; - return; + }else{ + jsonParseReset(pParse); + sqlite3_free(pParse); } - jsonParseReset(pParse); - sqlite3_free(pParse); } /* @@ -1218,7 +1237,7 @@ static const struct NanInfName { ** ** Special return values: ** -** 0 End if input +** 0 End of input ** -1 Syntax error ** -2 '}' seen ** -3 ']' seen @@ -1685,7 +1704,7 @@ json_parse_restart: static int jsonParse( JsonParse *pParse, /* Initialize and fill this JsonParse object */ sqlite3_context *pCtx, /* Report errors here */ - const char *zJson /* Input JSON text to be parsed */ + char *zJson /* Input JSON text to be parsed */ ){ int i; memset(pParse, 0, sizeof(*pParse)); @@ -1719,6 +1738,121 @@ static int jsonParse( return 0; } +#if 0 +/* +** This is a destructor for JSON strings. We make it a separate function +** so that the sqlite3ValueIsOfClass() function can be used to unambiguously +** identify sqlite3_value objects that are known JSON strings. +*/ +static void jsonClass(void *p){ + sqlite3RCStrUnref((char*)p); +} +#endif + +#if 0 +/* +** Process SQL function argument pJson as a JSON string. Return a pointer +** to its parse. +** +** If any error is encountered, return a NULL pointer and leave an error +** message in pCtx. +*/ +static JsonParse *jsonParseFromFunctionArg( + sqlite3_value *pJson, /* An SQL function argument containing JSON */ + sqlite3_context *pCtx, /* For accessing the cache */ + sqlite3_context *pErrCtx, /* For reporting errors */ + int bUnchng /* Only accept cached parse that are unchanged */ +){ + JsonParse *pParse; + char *zJson; + int nJson; + JsonParse *pMatch = 0; + int iKey; + int iMinKey = 0; + u32 iMinHold = 0xffffffff; + u32 iMaxHold = 0; + + char *zJson = (char*)sqlite3_value_text(pJson); + int nJson = sqlite3_value_bytes(pJson); + if( zJson==0 ) goto json_parse_value_nomem; + if( sqlite3ValueIsOfClass(pJson, jsonClass) ){ + assert( zJson!=0 ); + (void)sqlite3RCStrRef(zJson); + pParse = sqlite3RCStrGetAttachment(zJson, (void(*)(void*)jsonParseFree); + if( pParse ){ + return pParse; + } + }else{ + char *z = zJson; + zJson = sqlite3RCStrNew( nJson ); + if( zJson==0 ) goto json_parse_value_nomem; + assert( strlen(z)==nJson ); + memcpy(zJson, z, nJson+1); + } + + /* At this point zJson is an RCStr object that does not yet have a + ** parse. This procedure is holding its own reference to that zJson + ** and needs to release it, or hand it off, prior to returning. + ** + ** The next step is to try to find a parse of the JSON that already + ** exists in cache. + */ + for(iKey=0; iKeynJson==nJson + && (p->isMod==0 || bUnchng==0) + && memcmp(p->zJson,zJson,nJson)==0 + ){ + p->nErr = 0; + pMatch = p; + }else if( p->iHoldiHold; + iMinKey = iKey; + } + if( p->iHold>iMaxHold ){ + iMaxHold = p->iHold; + } + } + + if( pMatch ){ + pParse = pMatch; + pParse->iHold = iMaxHold+1; + }else{ + /* No parse of zJson could be found in cache. So parse it afresh. + */ + pParse = sqlite3_malloc64( sizeof(JsonParse) ); + if( pParse==0 ) goto json_parse_value_nomem; + if( jsonParse(pParse, pCtx, zJson) ){ + sqlite3_free(pParse); + sqlite3RCStrUnref(zJson); + return 0; + } + pParse->bOwnsJson = 1; + pParse->nJson = nJson; + pParse->nJPRef = 1; + pParse->iHold = iMaxHold+1; + sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, pParse, + (void(*)(void*))jsonParseFree); + pParse = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); + } + if( pParse ){ + pParse->nJPRef++; /* The caller will own this object */ + } + return pParse; + +json_parse_value_nomem: + if( zJson ) sqlite3RCStrUnref(zJson); + if( pErrCtx ) sqlite3_result_error_nomem(pCtx); + return 0; +} +#endif + /* Mark node i of pParse as being a child of iParent. Call recursively ** to fill in all the descendants of node i. */ @@ -1786,11 +1920,11 @@ static int jsonParseFindParents(JsonParse *pParse){ */ static JsonParse *jsonParseCached( sqlite3_context *pCtx, - sqlite3_value **argv, + sqlite3_value *pJson, sqlite3_context *pErrCtx ){ - const char *zJson = (const char*)sqlite3_value_text(argv[0]); - int nJson = sqlite3_value_bytes(argv[0]); + char *zJson = (char*)sqlite3_value_text(pJson); + int nJson = sqlite3_value_bytes(pJson); JsonParse *p; JsonParse *pMatch = 0; int iKey; @@ -2223,7 +2357,7 @@ static void jsonParseFunc( u32 i; assert( argc==1 ); - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; + if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0])) ) return; jsonParseFindParents(&x); jsonInit(&s, ctx); for(i=0; inNode ); if( argc==2 ){ @@ -2393,7 +2527,7 @@ static void jsonExtractFunc( JsonString jx; if( argc<2 ) return; - p = jsonParseCached(ctx, argv, ctx); + p = jsonParseCached(ctx, argv[0], ctx); if( p==0 ) return; if( argc==2 ){ /* With a single PATH argument */ @@ -2547,8 +2681,8 @@ static void jsonPatchFunc( JsonNode *pResult; /* The result of the merge */ UNUSED_PARAMETER(argc); - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; - if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){ + if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0])) ) return; + if( jsonParse(&y, ctx, (char*)sqlite3_value_text(argv[1])) ){ jsonParseReset(&x); return; } @@ -2624,7 +2758,7 @@ static void jsonRemoveFunc( u32 i; if( argc<1 ) return; - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; + if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0])) ) return; assert( x.nNode ); for(i=1; i<(u32)argc; i++){ zPath = (const char*)sqlite3_value_text(argv[i]); @@ -2702,7 +2836,7 @@ static void jsonReplaceNode( sqlite3_result_error_nomem(pCtx); } }else{ - JsonParse *pPatch = jsonParseCached(pCtx, &pValue, pCtx); + JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx); if( pPatch==0 ){ p->oom = 1; break; @@ -2746,7 +2880,7 @@ static void jsonReplaceFunc( jsonWrongNumArgs(ctx, "replace"); return; } - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; + if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0])) ) return; assert( x.nNode ); for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); @@ -2792,7 +2926,7 @@ static void jsonSetFunc( jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); return; } - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; + if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0])) ) return; assert( x.nNode ); for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); @@ -2830,7 +2964,7 @@ static void jsonTypeFunc( const char *zPath; JsonNode *pNode; - p = jsonParseCached(ctx, argv, ctx); + p = jsonParseCached(ctx, argv[0], ctx); if( p==0 ) return; if( argc==2 ){ zPath = (const char*)sqlite3_value_text(argv[1]); @@ -2857,7 +2991,7 @@ static void jsonValidFunc( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - p = jsonParseCached(ctx, argv, 0); + p = jsonParseCached(ctx, argv[0], 0); if( p==0 || p->oom ){ sqlite3_result_error_nomem(ctx); sqlite3_free(p); @@ -2903,7 +3037,7 @@ static void jsonErrorFunc( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - p = jsonParseCached(ctx, argv, 0); + p = jsonParseCached(ctx, argv[0], 0); if( p==0 || p->oom ){ sqlite3_result_error_nomem(ctx); sqlite3_free(p);