Cache the JSON parse used by json_extract().

FossilOrigin-Name: 44ca6c2c4639f3c50ae9233ee299ff0fc4566462c31f28d8676f8de7ffdcd7f0
This commit is contained in:
drh 2017-05-11 16:49:59 +00:00
parent f7fa4e71f4
commit 3fb153cb29
3 changed files with 74 additions and 24 deletions

View File

@ -171,6 +171,7 @@ struct JsonParse {
u8 oom; /* Set to true if out of memory */
u8 nErr; /* Number of errors seen */
u16 iDepth; /* Nesting depth */
int nJson; /* Length of the zJson string in bytes */
};
/*
@ -413,6 +414,14 @@ static void jsonParseReset(JsonParse *pParse){
pParse->aUp = 0;
}
/*
** Free a JsonParse object that was obtained from sqlite3_malloc().
*/
static void jsonParseFree(JsonParse *pParse){
jsonParseReset(pParse);
sqlite3_free(pParse);
}
/*
** Convert the JsonNode pNode into a pure JSON string and
** append to pOut. Subsubstructure is also included. Return
@ -964,6 +973,49 @@ static int jsonParseFindParents(JsonParse *pParse){
return SQLITE_OK;
}
/*
** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
*/
#define JSON_CACHE_ID (-429938)
/*
** Obtain a complete parse of the JSON found in the first argument
** of the argv array. Use the sqlite3_get_auxdata() cache for this
** parse if it is available. If the cache is not available or if it
** is no longer valid, parse the JSON again and return the new parse,
** and also register the new parse so that it will be available for
** future sqlite3_get_auxdata() calls.
*/
static JsonParse *jsonParseCached(
sqlite3_context *pCtx,
sqlite3_value **argv
){
const char *zJson = (const char*)sqlite3_value_text(argv[0]);
int nJson = sqlite3_value_bytes(argv[0]);
JsonParse *p;
if( zJson==0 ) return 0;
p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID);
if( p && p->nJson==nJson && memcmp(p->zJson,zJson,nJson)==0 ){
p->nErr = 0;
return p; /* The cached entry matches, so return it */
}
p = sqlite3_malloc( sizeof(*p) + nJson + 1 );
if( p==0 ){
sqlite3_result_error_nomem(pCtx);
return 0;
}
memset(p, 0, sizeof(*p));
p->zJson = (char*)&p[1];
memcpy((char*)p->zJson, zJson, nJson+1);
if( jsonParse(p, pCtx, p->zJson) ){
sqlite3_free(p);
return 0;
}
p->nJson = nJson;
sqlite3_set_auxdata(pCtx, JSON_CACHE_ID, p, (void(*)(void*))jsonParseFree);
return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID);
}
/*
** Compare the OBJECT label at pNode against zKey,nKey. Return true on
** a match.
@ -1329,29 +1381,30 @@ static void jsonArrayLengthFunc(
int argc,
sqlite3_value **argv
){
JsonParse x; /* The parse */
JsonParse *p; /* The parse */
sqlite3_int64 n = 0;
u32 i;
JsonNode *pNode;
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
assert( x.nNode );
p = jsonParseCached(ctx, argv);
if( p==0 ) return;
assert( p->nNode );
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
pNode = jsonLookup(&x, zPath, 0, ctx);
pNode = jsonLookup(p, zPath, 0, ctx);
}else{
pNode = x.aNode;
pNode = p->aNode;
}
if( pNode==0 ){
x.nErr = 1;
}else if( pNode->eType==JSON_ARRAY ){
return;
}
if( pNode->eType==JSON_ARRAY ){
assert( (pNode->jnFlags & JNODE_APPEND)==0 );
for(i=1; i<=pNode->n; n++){
i += jsonNodeSize(&pNode[i]);
}
}
if( x.nErr==0 ) sqlite3_result_int64(ctx, n);
jsonParseReset(&x);
sqlite3_result_int64(ctx, n);
}
/*
@ -1367,20 +1420,21 @@ static void jsonExtractFunc(
int argc,
sqlite3_value **argv
){
JsonParse x; /* The parse */
JsonParse *p; /* The parse */
JsonNode *pNode;
const char *zPath;
JsonString jx;
int i;
if( argc<2 ) return;
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
p = jsonParseCached(ctx, argv);
if( p==0 ) return;
jsonInit(&jx, ctx);
jsonAppendChar(&jx, '[');
for(i=1; i<argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
pNode = jsonLookup(&x, zPath, 0, ctx);
if( x.nErr ) break;
pNode = jsonLookup(p, zPath, 0, ctx);
if( p->nErr ) break;
if( argc>2 ){
jsonAppendSeparator(&jx);
if( pNode ){
@ -1398,7 +1452,6 @@ static void jsonExtractFunc(
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
jsonReset(&jx);
jsonParseReset(&x);
}
/* This is the RFC 7396 MergePatch algorithm.

View File

@ -1,5 +1,5 @@
C Negative\sN\svalues\sin\ssqlite3_get_auxdata()\sand\ssqlite3_set_auxdata()\scan\sbe\nused\sto\saccess\san\sauxiliary\sdata\scache\sover\sall\sfunctions\sin\sa\ssingle\sprepared\s\nstatement.
D 2017-05-11T15:20:18.112
C Cache\sthe\sJSON\sparse\sused\sby\sjson_extract().
D 2017-05-11T16:49:59.245
F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 6a8c838220f7c00820e1fc0ac1bccaaa8e5676067e1dbfa1bafa7a4ffecf8ae6
@ -219,7 +219,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
F ext/misc/json1.c bd3bbdefb6024c5e40287d61cdf876587c18a6e60fbe8cd6bcdde10eefd14bb1
F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984
F ext/misc/memvfs.c e5225bc22e79dde6b28380f3a068ddf600683a33
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
F ext/misc/percentile.c 92699c8cd7d517ff610e6037e56506f8904dae2e
@ -1579,10 +1579,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 3980ea0911b3ad3f86d7a7bdc6503f233315c274f473e18831e13eda2c238eeb
R dd46ebd5214a15a152d68d903a52e3e5
T *branch * auxdata-cache
T *sym-auxdata-cache *
T -sym-trunk *
P ff5306752e83e760255a10f20168c0f090929a4fee2a5f720dfab36f0ee72fae
R cae1848a63e8e13e207beadd6dc14b62
U drh
Z 41e6a797cec545b1cb4bc140fa542902
Z e9ec1c79d7987d6b2eda6f4466e2dc4b

View File

@ -1 +1 @@
ff5306752e83e760255a10f20168c0f090929a4fee2a5f720dfab36f0ee72fae
44ca6c2c4639f3c50ae9233ee299ff0fc4566462c31f28d8676f8de7ffdcd7f0