aboutsummaryrefslogtreecommitdiff
path: root/src/json.c
diff options
context:
space:
mode:
authordrh <>2023-07-28 13:52:08 +0000
committerdrh <>2023-07-28 13:52:08 +0000
commit859dd4fc2f09ba63bbf403d399e89baa1be60c26 (patch)
treeda7c065a5729abee8620b2968db37bb9d589982d /src/json.c
parentab12b750ded006bbbbc6aaea7655faadff3bec3a (diff)
parentb1926192fa1a8c0cdb3faf9dbe111a57b4ab9e5a (diff)
downloadsqlite-859dd4fc2f09ba63bbf403d399e89baa1be60c26.tar.gz
sqlite-859dd4fc2f09ba63bbf403d399e89baa1be60c26.zip
JSON performance improvements: (1) Add a cache for large string values
on OP_Column to avoid having to extract the string multiple times. (2) Internal JSON caching improvements. FossilOrigin-Name: 771fe35074b50b8d4a6583e61c53871b2445f7a58c82f3fc3bf6776e562e63af
Diffstat (limited to 'src/json.c')
-rw-r--r--src/json.c93
1 files changed, 50 insertions, 43 deletions
diff --git a/src/json.c b/src/json.c
index 493a1b99c..cc39264c0 100644
--- a/src/json.c
+++ b/src/json.c
@@ -174,6 +174,7 @@ struct JsonParse {
u16 iDepth; /* Nesting depth */
u8 nErr; /* Number of errors seen */
u8 oom; /* Set to true if out of memory */
+ u8 bJsonIsRCStr; /* True if zJson is an RCStr */
u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
u8 useMod; /* Actually use the edits contain inside aNode */
u8 hasMod; /* aNode contains edits from the original zJson */
@@ -619,6 +620,11 @@ static void jsonParseReset(JsonParse *pParse){
sqlite3_free(pParse->aUp);
pParse->aUp = 0;
}
+ if( pParse->bJsonIsRCStr ){
+ sqlite3RCStrUnref(pParse->zJson);
+ pParse->zJson = 0;
+ pParse->bJsonIsRCStr = 0;
+ }
if( pParse->zAlt ){
sqlite3RCStrUnref(pParse->zAlt);
pParse->zAlt = 0;
@@ -1745,26 +1751,15 @@ json_parse_restart:
** are any errors. If an error occurs, free all memory held by pParse,
** but not pParse itself.
**
-** pParse is uninitialized when this routine is called.
-**
-** pParse->nJPRef set to 1. The caller becomes the owner of the
-** the JsonParse object.
-**
-** pParse->bOwnsJson is set to bTakeJson. If bTakeJson is 1, the newly
-** initialized JsonParse object will become the owner of the zJson input
-** string. If bTakeJson is 0, then the caller is responsible for
-** preserving zJson for the lifetime of the JsonParse object.
+** pParse must be initialized to an empty parse object prior to calling
+** this routine.
*/
static int jsonParse(
JsonParse *pParse, /* Initialize and fill this JsonParse object */
- sqlite3_context *pCtx, /* Report errors here */
- char *zJson /* Input JSON text to be parsed */
+ sqlite3_context *pCtx /* Report errors here */
){
int i;
- memset(pParse, 0, sizeof(*pParse));
- if( zJson==0 ) return 1;
- pParse->zJson = zJson;
- pParse->nJPRef = 1;
+ const char *zJson = pParse->zJson;
i = jsonParseValue(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
@@ -1884,6 +1879,7 @@ static JsonParse *jsonParseCached(
int iMinKey = 0;
u32 iMinHold = 0xffffffff;
u32 iMaxHold = 0;
+ int bJsonRCStr;
if( zJson==0 ) return 0;
for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
@@ -1895,7 +1891,7 @@ static JsonParse *jsonParseCached(
if( pMatch==0
&& p->nJson==nJson
&& (p->hasMod==0 || bUnedited==0)
- && memcmp(p->zJson,zJson,nJson)==0
+ && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0)
){
p->nErr = 0;
p->useMod = 0;
@@ -1930,16 +1926,22 @@ static JsonParse *jsonParseCached(
/* The input JSON was not found anywhere in the cache. We will need
** to parse it ourselves and generate a new JsonParse object.
*/
- p = sqlite3_malloc64( sizeof(*p) + nJson + 1 );
+ bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref);
+ p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) );
if( p==0 ){
sqlite3_result_error_nomem(pCtx);
return 0;
}
memset(p, 0, sizeof(*p));
- p->zJson = (char*)&p[1];
- memcpy(p->zJson, zJson, nJson);
- p->zJson[nJson] = 0;
- if( jsonParse(p, pErrCtx, p->zJson) ){
+ if( bJsonRCStr ){
+ p->zJson = sqlite3RCStrRef(zJson);
+ p->bJsonIsRCStr = 1;
+ }else{
+ p->zJson = (char*)&p[1];
+ memcpy(p->zJson, zJson, nJson+1);
+ }
+ p->nJPRef = 1;
+ if( jsonParse(p, pErrCtx) ){
if( pErrCtx==0 ){
p->nErr = 1;
assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */
@@ -2663,29 +2665,27 @@ static void jsonPatchFunc(
int argc,
sqlite3_value **argv
){
- JsonParse x; /* The JSON that is being patched */
- JsonParse y; /* The patch */
+ JsonParse *pX; /* The JSON that is being patched */
+ JsonParse *pY; /* The patch */
JsonNode *pResult; /* The result of the merge */
UNUSED_PARAMETER(argc);
- if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0])) ) return;
- if( jsonParse(&y, ctx, (char*)sqlite3_value_text(argv[1])) ){
- jsonParseReset(&x);
- return;
- }
- x.useMod = 1;
- y.useMod = 1;
- pResult = jsonMergePatch(&x, 0, y.aNode);
- assert( pResult!=0 || x.oom );
- if( pResult && x.oom==0 ){
- jsonDebugPrintParse(&x);
+ pX = jsonParseCached(ctx, argv[0], ctx, 1);
+ if( pX==0 ) return;
+ pY = jsonParseCached(ctx, argv[1], ctx, 1);
+ if( pY==0 ) return;
+ pX->useMod = 1;
+ pX->hasMod = 1;
+ pY->useMod = 1;
+ pResult = jsonMergePatch(pX, 0, pY->aNode);
+ assert( pResult!=0 || pX->oom );
+ if( pResult && pX->oom==0 ){
+ jsonDebugPrintParse(pX);
jsonDebugPrintNode(pResult);
- jsonReturnJson(&x, pResult, ctx, 0);
+ jsonReturnJson(pX, pResult, ctx, 0);
}else{
sqlite3_result_error_nomem(ctx);
}
- jsonParseReset(&x);
- jsonParseReset(&y);
}
@@ -3312,7 +3312,6 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
/* Reset a JsonEachCursor back to its original state. Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){
- sqlite3_free(p->zJson);
sqlite3_free(p->zRoot);
jsonParseReset(&p->sParse);
p->iRowid = 0;
@@ -3632,11 +3631,19 @@ static int jsonEachFilter(
if( idxNum==0 ) return SQLITE_OK;
z = (const char*)sqlite3_value_text(argv[0]);
if( z==0 ) return SQLITE_OK;
- n = sqlite3_value_bytes(argv[0]);
- p->zJson = sqlite3_malloc64( n+1 );
- if( p->zJson==0 ) return SQLITE_NOMEM;
- memcpy(p->zJson, z, (size_t)n+1);
- if( jsonParse(&p->sParse, 0, p->zJson) ){
+ memset(&p->sParse, 0, sizeof(p->sParse));
+ p->sParse.nJPRef = 1;
+ if( sqlite3ValueIsOfClass(argv[0], (void(*)(void*))sqlite3RCStrUnref) ){
+ p->sParse.zJson = sqlite3RCStrRef((char*)z);
+ }else{
+ n = sqlite3_value_bytes(argv[0]);
+ p->sParse.zJson = sqlite3RCStrNew( n+1 );
+ if( p->sParse.zJson==0 ) return SQLITE_NOMEM;
+ memcpy(p->sParse.zJson, z, (size_t)n+1);
+ }
+ p->sParse.bJsonIsRCStr = 1;
+ p->zJson = p->sParse.zJson;
+ if( jsonParse(&p->sParse, 0) ){
int rc = SQLITE_NOMEM;
if( p->sParse.oom==0 ){
sqlite3_free(cur->pVtab->zErrMsg);