1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-11 01:42:22 +03:00

Incremental progress toward improved caching of parsed JSON.

FossilOrigin-Name: f2c063884685a79d5a787590447c292f51e898a98c9508159c788f505227ba85
This commit is contained in:
drh
2023-07-24 17:59:25 +00:00
parent 7d70571142
commit 9125d5ab6c
3 changed files with 256 additions and 92 deletions

View File

@@ -1,5 +1,5 @@
C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\sjson-opt\sbranch. C Incremental\sprogress\stoward\simproved\scaching\sof\sparsed\sJSON.
D 2023-07-24T12:37:23.834 D 2023-07-24T17:59:25.604
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -598,7 +598,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276
F src/json.c d34d10ea1520bb71ee69c500deb4a20847a57b4c59c1ecb807c935f53c870885 F src/json.c 3584f5fb0ff48009ebabb34704328c2086d924fc2403fdf0e48afce5b9f22777
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465 F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465
F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002 F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002
@@ -2044,8 +2044,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 1955e66cfc4614df97b8d68b0e662f309513d62dc8aeec71af5a54e66b79c707 c1b080e39397c983c13a5e79303223827de7b4946c18a79396851ec1814782f3 P 00bfc4918be49ac74a3e7851c88ab7ec226e6a37853f8ad4c77f758751960456
R 2ab9518ef262a1aca9fe7f6a2ac934e0 R 831d8993ebdb0e60bc4ab13513e5d282
U drh U drh
Z 40e6676e6b6212e46c825865d3333a87 Z 8cedb8d1881aa5ce4f4f5c5fea73c221
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
00bfc4918be49ac74a3e7851c88ab7ec226e6a37853f8ad4c77f758751960456 f2c063884685a79d5a787590447c292f51e898a98c9508159c788f505227ba85

View File

@@ -72,7 +72,6 @@ struct JsonString {
u64 nUsed; /* Bytes of zBuf[] currently used */ u64 nUsed; /* Bytes of zBuf[] currently used */
u8 bStatic; /* True if zBuf is static space */ u8 bStatic; /* True if zBuf is static space */
u8 bErr; /* True if an error has been encountered */ u8 bErr; /* True if an error has been encountered */
u8 bOrig; /* Ignore edits when rendering JSON */
char zSpace[100]; /* Initial static space */ char zSpace[100]; /* Initial static space */
}; };
@@ -87,14 +86,16 @@ struct JsonTask {
/* JSON type values /* JSON type values
*/ */
#define JSON_NULL 0 #define JSON_SUBST 0 /* Special edit node. Uses u.iPrev */
#define JSON_TRUE 1 #define JSON_PATCH 1 /* Special edit node. Uses u.pPatch */
#define JSON_FALSE 2 #define JSON_NULL 2
#define JSON_INT 3 #define JSON_TRUE 3
#define JSON_REAL 4 #define JSON_FALSE 4
#define JSON_STRING 5 #define JSON_INT 5
#define JSON_ARRAY 6 #define JSON_REAL 6
#define JSON_OBJECT 7 #define JSON_STRING 7
#define JSON_ARRAY 8
#define JSON_OBJECT 9
/* The "subtype" set for JSON values */ /* The "subtype" set for JSON values */
#define JSON_SUBTYPE 74 /* Ascii for "J" */ #define JSON_SUBTYPE 74 /* Ascii for "J" */
@@ -103,6 +104,7 @@ struct JsonTask {
** Names of the various JSON types: ** Names of the various JSON types:
*/ */
static const char * const jsonType[] = { static const char * const jsonType[] = {
"subst", "patch",
"null", "true", "false", "integer", "real", "text", "array", "object" "null", "true", "false", "integer", "real", "text", "array", "object"
}; };
@@ -124,13 +126,15 @@ struct JsonNode {
u8 eType; /* One of the JSON_ type values */ u8 eType; /* One of the JSON_ type values */
u8 jnFlags; /* JNODE flags */ u8 jnFlags; /* JNODE flags */
u8 eU; /* Which union element to use */ u8 eU; /* Which union element to use */
u32 n; /* Bytes of content, or number of sub-nodes */ u32 n; /* Bytes of content for INT, REAL or STRING
** Number of sub-nodes for ARRAY and OBJECT
** Node that SUBST applies to */
union { union {
const char *zJContent; /* 1: Content for INT, REAL, and STRING */ const char *zJContent; /* 1: Content for INT, REAL, and STRING */
u32 iAppend; /* 2: More terms for ARRAY and OBJECT */ u32 iAppend; /* 2: More terms for ARRAY and OBJECT */
u32 iKey; /* 3: Key for ARRAY objects in json_tree() */ u32 iKey; /* 3: Key for ARRAY objects in json_tree() */
u32 iReplace; /* 4: Replacement content for JNODE_REPLACE */
JsonNode *pPatch; /* 5: Node chain of patch for JNODE_PATCH */ JsonNode *pPatch; /* 5: Node chain of patch for JNODE_PATCH */
u32 iPrev; /* 6: Previous SUBST node, or 0 */
} u; } u;
}; };
@@ -149,9 +153,9 @@ struct JsonParse {
u8 oom; /* Set to true if out of memory */ u8 oom; /* Set to true if out of memory */
u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
u8 nJPRef; /* Number of references to this object */ u8 nJPRef; /* Number of references to this object */
u8 bOrig; /* Ignore edit marks during search */
int nJson; /* Length of the zJson string in bytes */ int nJson; /* Length of the zJson string in bytes */
u32 iErr; /* Error location in zJson[] */ u32 iErr; /* Error location in zJson[] */
u32 iSubst; /* Last known JSON_SUBST node */
u32 iHold; /* Replace cache line with the lowest iHold value */ u32 iHold; /* Replace cache line with the lowest iHold value */
}; };
@@ -175,7 +179,6 @@ static void jsonZero(JsonString *p){
p->nAlloc = sizeof(p->zSpace); p->nAlloc = sizeof(p->zSpace);
p->nUsed = 0; p->nUsed = 0;
p->bStatic = 1; p->bStatic = 1;
p->bOrig = 0;
} }
/* Initialize the JsonString object /* Initialize the JsonString object
@@ -548,7 +551,12 @@ static u32 jsonNodeSize(JsonNode *pNode){
** delete the JsonParse object itself. ** delete the JsonParse object itself.
*/ */
static void jsonParseReset(JsonParse *pParse){ static void jsonParseReset(JsonParse *pParse){
assert( pParse->pClean==0 ); while( pParse->pClean ){
JsonTask *pTask = pParse->pClean;
pParse->pClean = pTask->pJTNext;
pTask->xOp(pTask->pArg);
sqlite3_free(pTask);
}
assert( pParse->nJPRef<=1 ); assert( pParse->nJPRef<=1 );
sqlite3_free(pParse->aNode); sqlite3_free(pParse->aNode);
pParse->aNode = 0; pParse->aNode = 0;
@@ -566,35 +574,63 @@ static void jsonParseFree(JsonParse *pParse){
pParse->nJPRef--; pParse->nJPRef--;
return; return;
} }
while( pParse->pClean ){
JsonTask *pTask = pParse->pClean;
pParse->pClean = pTask->pJTNext;
pTask->xOp(pTask->pArg);
sqlite3_free(pTask);
}
jsonParseReset(pParse); jsonParseReset(pParse);
sqlite3_free(pParse); sqlite3_free(pParse);
} }
/*
** Add a cleanup task to the JsonParse object.
**
** If an OOM occurs, the cleanup operation happens immediately
** and this function returns SQLITE_NOMEM.
*/
static int jsonParseAddCleanup(
JsonParse *pParse, /* Add the cleanup task to this parser */
void(*xOp)(void*), /* The cleanup task */
void *pArg /* Argument to the cleanup */
){
JsonTask *pTask = sqlite3_malloc64( sizeof(*pTask) );
if( pTask==0 ){
pParse->oom = 1;
xOp(pArg);
return SQLITE_ERROR;
}
pTask->pJTNext = pParse->pClean;
pParse->pClean = pTask;
pTask->xOp = xOp;
pTask->pArg = pArg;
return SQLITE_OK;
}
/* /*
** Convert the JsonNode pNode into a pure JSON string and ** Convert the JsonNode pNode into a pure JSON string and
** append to pOut. Subsubstructure is also included. Return ** append to pOut. Subsubstructure is also included. Return
** the number of JsonNode objects that are encoded. ** the number of JsonNode objects that are encoded.
*/ */
static void jsonRenderNode( static void jsonRenderNode(
JsonParse *pParse, /* the complete parse of the JSON */
JsonNode *pNode, /* The node to render */ JsonNode *pNode, /* The node to render */
JsonString *pOut, /* Write JSON here */ JsonString *pOut /* Write JSON here */
sqlite3_value **aReplace /* Replacement values */
){ ){
assert( pNode!=0 ); assert( pNode!=0 );
if( (pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH)) && !pOut->bOrig ){ while( (pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH)) ){
if( (pNode->jnFlags & JNODE_REPLACE)!=0 && ALWAYS(aReplace!=0) ){ if( (pNode->jnFlags & JNODE_REPLACE)!=0 ){
assert( pNode->eU==4 ); u32 idx = (u32)(pNode - pParse->aNode);
jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); u32 i = pParse->iSubst;
return; while( 1 /*exit-by-break*/ ){
assert( i<pParse->nNode );
assert( pParse->aNode[i].eType==JSON_SUBST );
assert( pParse->aNode[i].eU==6 );
assert( pParse->aNode[i].u.iPrev<i );
if( pParse->aNode[i].n==idx ){
pNode = &pParse->aNode[i+1];
break;
}
i = pParse->aNode[i].u.iPrev;
}
}else{
pNode = pNode->u.pPatch;
} }
assert( pNode->eU==5 );
pNode = pNode->u.pPatch;
} }
switch( pNode->eType ){ switch( pNode->eType ){
default: { default: {
@@ -653,13 +689,13 @@ static void jsonRenderNode(
jsonAppendChar(pOut, '['); jsonAppendChar(pOut, '[');
for(;;){ for(;;){
while( j<=pNode->n ){ while( j<=pNode->n ){
if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pOut->bOrig ){ if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){
jsonAppendSeparator(pOut); jsonAppendSeparator(pOut);
jsonRenderNode(&pNode[j], pOut, aReplace); jsonRenderNode(pParse, &pNode[j], pOut);
} }
j += jsonNodeSize(&pNode[j]); j += jsonNodeSize(&pNode[j]);
} }
if( (pNode->jnFlags & JNODE_APPEND)==0 || pOut->bOrig ) break; if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
assert( pNode->eU==2 ); assert( pNode->eU==2 );
pNode = &pNode[pNode->u.iAppend]; pNode = &pNode[pNode->u.iAppend];
j = 1; j = 1;
@@ -672,15 +708,15 @@ static void jsonRenderNode(
jsonAppendChar(pOut, '{'); jsonAppendChar(pOut, '{');
for(;;){ for(;;){
while( j<=pNode->n ){ while( j<=pNode->n ){
if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pOut->bOrig ){ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){
jsonAppendSeparator(pOut); jsonAppendSeparator(pOut);
jsonRenderNode(&pNode[j], pOut, aReplace); jsonRenderNode(pParse, &pNode[j], pOut);
jsonAppendChar(pOut, ':'); jsonAppendChar(pOut, ':');
jsonRenderNode(&pNode[j+1], pOut, aReplace); jsonRenderNode(pParse, &pNode[j+1], pOut);
} }
j += 1 + jsonNodeSize(&pNode[j+1]); j += 1 + jsonNodeSize(&pNode[j+1]);
} }
if( (pNode->jnFlags & JNODE_APPEND)==0 || pOut->bOrig ) break; if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
assert( pNode->eU==2 ); assert( pNode->eU==2 );
pNode = &pNode[pNode->u.iAppend]; pNode = &pNode[pNode->u.iAppend];
j = 1; j = 1;
@@ -695,15 +731,13 @@ static void jsonRenderNode(
** Return a JsonNode and all its descendants as a JSON string. ** Return a JsonNode and all its descendants as a JSON string.
*/ */
static void jsonReturnJson( static void jsonReturnJson(
JsonParse *pParse, /* The complete JSON */
JsonNode *pNode, /* Node to return */ JsonNode *pNode, /* Node to return */
sqlite3_context *pCtx, /* Return value for this function */ sqlite3_context *pCtx /* Return value for this function */
sqlite3_value **aReplace, /* Array of replacement values */
int bOrig /* Ignore edits if true */
){ ){
JsonString s; JsonString s;
jsonInit(&s, pCtx); jsonInit(&s, pCtx);
s.bOrig = bOrig!=0; jsonRenderNode(pParse, pNode, &s);
jsonRenderNode(pNode, &s, aReplace);
jsonResult(&s); jsonResult(&s);
sqlite3_result_subtype(pCtx, JSON_SUBTYPE); sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
} }
@@ -743,10 +777,9 @@ static u32 jsonHexToInt4(const char *z){
** Make the JsonNode the return value of the function. ** Make the JsonNode the return value of the function.
*/ */
static void jsonReturn( static void jsonReturn(
JsonParse *pParse, /* Complete JSON parse tree */
JsonNode *pNode, /* Node to return */ JsonNode *pNode, /* Node to return */
sqlite3_context *pCtx, /* Return value for this function */ sqlite3_context *pCtx /* Return value for this function */
sqlite3_value **aReplace, /* Array of replacement values */
int bOrig /* Ignore edits if true */
){ ){
switch( pNode->eType ){ switch( pNode->eType ){
default: { default: {
@@ -893,7 +926,7 @@ static void jsonReturn(
} }
case JSON_ARRAY: case JSON_ARRAY:
case JSON_OBJECT: { case JSON_OBJECT: {
jsonReturnJson(pNode, pCtx, aReplace, bOrig); jsonReturnJson(pParse, pNode, pCtx);
break; break;
} }
} }
@@ -962,6 +995,22 @@ static int jsonParseAddNode(
return pParse->nNode++; return pParse->nNode++;
} }
/*
** Add a new JSON_SUBST node. The node immediately following
** this new node will be the substitute content for iNode.
*/
static int jsonParseAddSubstNode(
JsonParse *pParse, /* Add the JSON_SUBST here */
u32 iNode /* References this node */
){
int idx = jsonParseAddNode(pParse, JSON_SUBST, iNode, 0);
if( idx<=0 ) return idx;
pParse->aNode[idx].eU = 6;
pParse->aNode[idx].u.iPrev = pParse->iSubst;
pParse->iSubst = idx;
return idx;
}
/* /*
** Return true if z[] begins with 2 (or more) hexadecimal digits ** Return true if z[] begins with 2 (or more) hexadecimal digits
*/ */
@@ -1749,6 +1798,7 @@ static JsonParse *jsonParseCached(
sqlite3_free(p); sqlite3_free(p);
return 0; return 0;
} }
p->nJPRef = 1;
p->nJson = nJson; p->nJson = nJson;
p->iHold = iMaxHold+1; p->iHold = iMaxHold+1;
sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
@@ -1803,7 +1853,6 @@ static JsonNode *jsonLookupStep(
const char *zKey; const char *zKey;
JsonNode *pRoot = &pParse->aNode[iRoot]; JsonNode *pRoot = &pParse->aNode[iRoot];
if( zPath[0]==0 ) return pRoot; if( zPath[0]==0 ) return pRoot;
if( (pRoot->jnFlags & JNODE_REPLACE)!=0 && !pParse->bOrig ) return 0;
if( zPath[0]=='.' ){ if( zPath[0]=='.' ){
if( pRoot->eType!=JSON_OBJECT ) return 0; if( pRoot->eType!=JSON_OBJECT ) return 0;
zPath++; zPath++;
@@ -1836,7 +1885,7 @@ static JsonNode *jsonLookupStep(
j++; j++;
j += jsonNodeSize(&pRoot[j]); j += jsonNodeSize(&pRoot[j]);
} }
if( (pRoot->jnFlags & JNODE_APPEND)==0 || pParse->bOrig ) break; if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
assert( pRoot->eU==2 ); assert( pRoot->eU==2 );
iRoot += pRoot->u.iAppend; iRoot += pRoot->u.iAppend;
pRoot = &pParse->aNode[iRoot]; pRoot = &pParse->aNode[iRoot];
@@ -1845,7 +1894,6 @@ static JsonNode *jsonLookupStep(
if( pApnd ){ if( pApnd ){
u32 iStart, iLabel; u32 iStart, iLabel;
JsonNode *pNode; JsonNode *pNode;
assert( !pParse->bOrig );
iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
zPath += i; zPath += i;
@@ -1875,10 +1923,10 @@ static JsonNode *jsonLookupStep(
if( pRoot->eType!=JSON_ARRAY ) return 0; if( pRoot->eType!=JSON_ARRAY ) return 0;
for(;;){ for(;;){
while( j<=pBase->n ){ while( j<=pBase->n ){
if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->bOrig ) i++; if( (pBase[j].jnFlags & JNODE_REMOVE)==0 ) i++;
j += jsonNodeSize(&pBase[j]); j += jsonNodeSize(&pBase[j]);
} }
if( (pBase->jnFlags & JNODE_APPEND)==0 || pParse->bOrig ) break; if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
assert( pBase->eU==2 ); assert( pBase->eU==2 );
iBase += pBase->u.iAppend; iBase += pBase->u.iAppend;
pBase = &pParse->aNode[iBase]; pBase = &pParse->aNode[iBase];
@@ -1908,13 +1956,11 @@ static JsonNode *jsonLookupStep(
zPath += j + 1; zPath += j + 1;
j = 1; j = 1;
for(;;){ for(;;){
while( j<=pRoot->n while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
&& (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE) && !pParse->bOrig)) if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--;
){
if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->bOrig ) i--;
j += jsonNodeSize(&pRoot[j]); j += jsonNodeSize(&pRoot[j]);
} }
if( (pRoot->jnFlags & JNODE_APPEND)==0 || pParse->bOrig ) break; if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
assert( pRoot->eU==2 ); assert( pRoot->eU==2 );
iRoot += pRoot->u.iAppend; iRoot += pRoot->u.iAppend;
pRoot = &pParse->aNode[iRoot]; pRoot = &pParse->aNode[iRoot];
@@ -1926,7 +1972,6 @@ static JsonNode *jsonLookupStep(
if( i==0 && pApnd ){ if( i==0 && pApnd ){
u32 iStart; u32 iStart;
JsonNode *pNode; JsonNode *pNode;
assert( !pParse->bOrig );
iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
if( pParse->oom ) return 0; if( pParse->oom ) return 0;
@@ -2060,6 +2105,60 @@ static void jsonRemoveAllNulls(JsonNode *pNode){
** SQL functions used for testing and debugging ** SQL functions used for testing and debugging
****************************************************************************/ ****************************************************************************/
#if 0 /* One for debugging. zero normally */
/*
** Print N node entries.
*/
static void jsonDebugPrintNodeEntries(
JsonNode *aNode, /* First node entry to print */
int N, /* Number of node entries to print */
int ofst, /* Node number for p */
int nDent /* Indent by this many spaces */
){
int i;
for(i=0; i<N; i++){
const char *zType;
if( aNode[i].jnFlags & JNODE_LABEL ){
zType = "label";
}else{
zType = jsonType[aNode[i].eType];
}
if( nDent>0 ) printf("%*s", nDent, "");
if( (aNode[i].jnFlags & ~JNODE_LABEL)!=0 ){
printf("node %4u: %-7s %02x n=%-5d",
i+ofst, zType, aNode[i].jnFlags, aNode[i].n);
}else{
printf("node %4u: %-7s n=%-5d",
i+ofst, zType, aNode[i].n);
}
switch( aNode[i].eU ){
case 1: printf(" zJContent=[%.*s]\n",
aNode[i].n, aNode[i].u.zJContent); break;
case 2: printf(" iAppend=%u\n", aNode[i].u.iAppend); break;
case 3: printf(" iKey=%u\n", aNode[i].u.iKey); break;
case 5: {
JsonNode *pX = aNode[i].u.pPatch;
printf(" pPatch=...\n");
jsonDebugPrintNodeEntries(pX, jsonNodeSize(pX), 0, nDent+3);
break;
}
case 6: printf(" iPrev=%u\n", aNode[i].u.iPrev); break;
default: printf("\n");
}
}
}
static void jsonDebugPrintParse(JsonParse *p){
jsonDebugPrintNodeEntries(p->aNode, p->nNode, 0, 0);
}
static void jsonDebugPrintNode(JsonNode *pNode){
jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode), 0, 0);
}
#else
/* The usual case */
# define jsonDebugPrintNode(X)
# define jsonDebugPrintParse(X)
#endif
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
/* /*
** The json_parse(JSON) function returns a string which describes ** The json_parse(JSON) function returns a string which describes
@@ -2279,15 +2378,15 @@ static void jsonExtractFunc(
} }
if( pNode ){ if( pNode ){
if( flags & JSON_JSON ){ if( flags & JSON_JSON ){
jsonReturnJson(pNode, ctx, 0, 1); jsonReturnJson(p, pNode, ctx);
}else{ }else{
jsonReturn(pNode, ctx, 0, 1); jsonReturn(p, pNode, ctx);
sqlite3_result_subtype(ctx, 0); sqlite3_result_subtype(ctx, 0);
} }
} }
}else{ }else{
pNode = jsonLookup(p, zPath, 0, ctx); pNode = jsonLookup(p, zPath, 0, ctx);
if( p->nErr==0 && pNode ) jsonReturn(pNode, ctx, 0, 1); if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx);
} }
}else{ }else{
/* Two or more PATH arguments results in a JSON array with each /* Two or more PATH arguments results in a JSON array with each
@@ -2301,7 +2400,7 @@ static void jsonExtractFunc(
if( p->nErr ) break; if( p->nErr ) break;
jsonAppendSeparator(&jx); jsonAppendSeparator(&jx);
if( pNode ){ if( pNode ){
jsonRenderNode(pNode, &jx, 0); jsonRenderNode(p, pNode, &jx);
}else{ }else{
jsonAppendRawNZ(&jx, "null", 4); jsonAppendRawNZ(&jx, "null", 4);
} }
@@ -2415,7 +2514,8 @@ static void jsonPatchFunc(
pResult = jsonMergePatch(&x, 0, y.aNode); pResult = jsonMergePatch(&x, 0, y.aNode);
assert( pResult!=0 || x.oom ); assert( pResult!=0 || x.oom );
if( pResult ){ if( pResult ){
jsonReturnJson(pResult, ctx, 0, 0); jsonDebugPrintNode(pResult);
jsonReturnJson(&x, pResult, ctx);
}else{ }else{
sqlite3_result_error_nomem(ctx); sqlite3_result_error_nomem(ctx);
} }
@@ -2492,12 +2592,89 @@ static void jsonRemoveFunc(
if( pNode ) pNode->jnFlags |= JNODE_REMOVE; if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
} }
if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){ if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
jsonReturnJson(x.aNode, ctx, 0, 0); jsonReturnJson(&x, x.aNode, ctx);
} }
remove_done: remove_done:
jsonDebugPrintParse(&x);
jsonParseReset(&x); jsonParseReset(&x);
} }
/*
** Substitute the value at iNode with the pValue parameter.
*/
static void jsonReplaceNode(
sqlite3_context *pCtx,
JsonParse *p,
int iNode,
sqlite3_value *pValue
){
int idx = jsonParseAddSubstNode(p, iNode);
if( idx<=0 ){
assert( p->oom );
return;
}
switch( sqlite3_value_type(pValue) ){
case SQLITE_NULL: {
jsonParseAddNode(p, JSON_NULL, 0, 0);
break;
}
case SQLITE_FLOAT: {
char *z = sqlite3_mprintf("%!0.15g", sqlite3_value_double(pValue));
int n;
if( z==0 ){
p->oom = 1;
break;
}
n = sqlite3Strlen30(z);
jsonParseAddNode(p, JSON_REAL, n, z);
jsonParseAddCleanup(p, sqlite3_free, z);
break;
}
case SQLITE_INTEGER: {
char *z = sqlite3_mprintf("%lld", sqlite3_value_int64(pValue));
int n;
if( z==0 ){
p->oom = 1;
break;
}
n = sqlite3Strlen30(z);
jsonParseAddNode(p, JSON_INT, n, z);
jsonParseAddCleanup(p, sqlite3_free, z);
break;
}
case SQLITE_TEXT: {
const char *z = (const char*)sqlite3_value_text(pValue);
u32 n = (u32)sqlite3_value_bytes(pValue);
if( z==0 ){
p->oom = 1;
break;
}
if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){
int k = jsonParseAddNode(p, JSON_STRING, n, z);
if( k>0 ) p->aNode[k].jnFlags |= JNODE_RAW;
jsonParseAddCleanup(p, sqlite3_free, sqlite3DbStrDup(0,z));
}else{
int k= jsonParseAddNode(p, JSON_PATCH, 0, 0);
if( k>0 ){
JsonParse *pPatch = jsonParseCached(pCtx, &pValue, pCtx);
if( pPatch==0 ){
p->oom = 1;
break;
}
assert( pPatch->nJPRef>=1 );
pPatch->nJPRef++;
p->aNode[k].jnFlags |= JNODE_PATCH;
p->aNode[k].eU = 5;
p->aNode[k].u.pPatch = pPatch->aNode;
jsonParseAddCleanup(p, (void(*)(void*))jsonParseFree, pPatch);
}
}
break;
}
}
}
/* /*
** json_replace(JSON, PATH, VALUE, ...) ** json_replace(JSON, PATH, VALUE, ...)
** **
@@ -2526,20 +2703,13 @@ static void jsonReplaceFunc(
pNode = jsonLookup(&x, zPath, 0, ctx); pNode = jsonLookup(&x, zPath, 0, ctx);
if( x.nErr ) goto replace_err; if( x.nErr ) goto replace_err;
if( pNode ){ if( pNode ){
assert( pNode->eU==0 || pNode->eU==1 || pNode->eU==4 );
testcase( pNode->eU!=0 && pNode->eU!=1 );
pNode->jnFlags |= (u8)JNODE_REPLACE; pNode->jnFlags |= (u8)JNODE_REPLACE;
VVA( pNode->eU = 4 ); jsonReplaceNode(ctx, &x, (u32)(pNode - x.aNode), argv[i+1]);
pNode->u.iReplace = i + 1;
} }
} }
if( x.aNode[0].jnFlags & JNODE_REPLACE ){ jsonReturnJson(&x, x.aNode, ctx);
assert( x.aNode[0].eU==4 );
sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
}else{
jsonReturnJson(x.aNode, ctx, argv, 0);
}
replace_err: replace_err:
jsonDebugPrintParse(&x);
jsonParseReset(&x); jsonParseReset(&x);
} }
@@ -2585,19 +2755,13 @@ static void jsonSetFunc(
}else if( x.nErr ){ }else if( x.nErr ){
goto jsonSetDone; goto jsonSetDone;
}else if( pNode && (bApnd || bIsSet) ){ }else if( pNode && (bApnd || bIsSet) ){
testcase( pNode->eU!=0 && pNode->eU!=1 );
assert( pNode->eU!=3 && pNode->eU!=5 );
VVA( pNode->eU = 4 );
pNode->jnFlags |= (u8)JNODE_REPLACE; pNode->jnFlags |= (u8)JNODE_REPLACE;
pNode->u.iReplace = i + 1; jsonReplaceNode(ctx, &x, (u32)(pNode - x.aNode), argv[i+1]);
} }
} }
if( x.aNode[0].jnFlags & JNODE_REPLACE ){ jsonDebugPrintParse(&x);
assert( x.aNode[0].eU==4 ); jsonReturnJson(&x, x.aNode, ctx);
sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
}else{
jsonReturnJson(x.aNode, ctx, argv, 0);
}
jsonSetDone: jsonSetDone:
jsonParseReset(&x); jsonParseReset(&x);
} }
@@ -3105,7 +3269,7 @@ static int jsonEachColumn(
case JEACH_KEY: { case JEACH_KEY: {
if( p->i==0 ) break; if( p->i==0 ) break;
if( p->eType==JSON_OBJECT ){ if( p->eType==JSON_OBJECT ){
jsonReturn(pThis, ctx, 0, 0); jsonReturn(&p->sParse, pThis, ctx);
}else if( p->eType==JSON_ARRAY ){ }else if( p->eType==JSON_ARRAY ){
u32 iKey; u32 iKey;
if( p->bRecursive ){ if( p->bRecursive ){
@@ -3121,7 +3285,7 @@ static int jsonEachColumn(
} }
case JEACH_VALUE: { case JEACH_VALUE: {
if( pThis->jnFlags & JNODE_LABEL ) pThis++; if( pThis->jnFlags & JNODE_LABEL ) pThis++;
jsonReturn(pThis, ctx, 0, 0); jsonReturn(&p->sParse, pThis, ctx);
break; break;
} }
case JEACH_TYPE: { case JEACH_TYPE: {
@@ -3132,7 +3296,7 @@ static int jsonEachColumn(
case JEACH_ATOM: { case JEACH_ATOM: {
if( pThis->jnFlags & JNODE_LABEL ) pThis++; if( pThis->jnFlags & JNODE_LABEL ) pThis++;
if( pThis->eType>=JSON_ARRAY ) break; if( pThis->eType>=JSON_ARRAY ) break;
jsonReturn(pThis, ctx, 0, 0); jsonReturn(&p->sParse, pThis, ctx);
break; break;
} }
case JEACH_ID: { case JEACH_ID: {