From f02cc9a3248b07095847f9a5e93e092e4fa6e116 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 25 Jul 2023 15:08:18 +0000 Subject: [PATCH 1/6] Create the new RCStr class of strings and try to use them for JSON storage. FossilOrigin-Name: c1b8725089bb3d006ec69add28f4fcb3f4e79412c7f438b5b1067c2227e77b9c --- manifest | 21 ++++--- manifest.uuid | 2 +- src/json.c | 42 +++++++++----- src/printf.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sqliteInt.h | 52 ++++++++++++++++++ src/vdbemem.c | 23 ++++++++ 6 files changed, 258 insertions(+), 25 deletions(-) diff --git a/manifest b/manifest index 192ed6f6b3..b4a115361a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C It\sis\san\serror\sto\stry\sto\sinsert\sa\sBLOB\svalue\sinto\sJSON. -D 2023-07-24T23:27:05.772 +C Create\sthe\snew\sRCStr\sclass\sof\sstrings\sand\stry\sto\suse\sthem\sfor\sJSON\sstorage. +D 2023-07-25T15:08:18.543 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 fc5b67025d4ca026d5c0696cfd5c265c1cd0fa2f669fbe78d268e955f853e75c +F src/json.c 3add12eb29f09a99dc260bcde427ea925f600cf0bb0d9abb0618a03d7e50eead F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465 F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002 @@ -634,7 +634,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c 84b7b4b647f336934a5ab2e7f0c52555833cc0778d2d60e016cca52ee8c6cd8f +F src/printf.c 21e410b0a3904dddb39ef1b245cfb991302022a6f0fc16f0a8a13539d6d2f713 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -643,7 +643,7 @@ F src/shell.c.in d320d8a13636de06d777cc1eab981caca304e175464e98183cf4ea68d93db81 F src/sqlite.h.in f999ef3642f381d69679b2516b430dbcb6c5a2a951b7f5e43dc4751b474a5774 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h dcb1a885e8b6cb78df618944b89d44361a99d0fe33e1bba2c150a855f7dc5599 +F src/sqliteInt.h 59b755dec944aa3b068e962ef53264de6fde3d6b6df2d5869ea3afdb7facdf60 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -714,7 +714,7 @@ F src/vdbeInt.h 401813862f9d75af01bdb2ab99253ad019e9d6ddcc8058e4fa61a43e9a60d1f7 F src/vdbeapi.c dde6c4d0f87486f056b9db4d1ea185bb1d84a6839102b86e76316ba590d07cc7 F src/vdbeaux.c b5e3f7e158518b4eca6f166ac43900640a3fe9735c710e12bfa119af21059339 F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce -F src/vdbemem.c 40afb83ed848e235848ffdd3ba25adca4ba602111b8ed3b05ae3b1b12e0eacee +F src/vdbemem.c aee9ac636666616494d9a395d29efc3fe9e1404a9f043db81c82560b43b78f35 F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015 F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c aae4bd769410eb7e1d02c42613eec961d514459b1c3c1c63cfc84e92a137daac @@ -2044,8 +2044,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 327fff501e36f75d4901c520123c5ca45e0e0da1d9cc8fa8fa877ceb68c686d2 -R bdfb959c0be321608fe703b9663eda08 +P 28c6e964b1e2257527df02b352f02e135f7c4b764b8b41eda6461f9538f5d042 +R 4c5edeff0b59e42b46536f2b483d14f6 +T *branch * json-opt-rcstr +T *sym-json-opt-rcstr * +T -sym-json-opt * U drh -Z 1ec3b6ff4cb1459575429b1da04d690b +Z 3f6f4e5856b228a13fb14e603baeb559 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9eac82e0e2..59c5808d17 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -28c6e964b1e2257527df02b352f02e135f7c4b764b8b41eda6461f9538f5d042 \ No newline at end of file +c1b8725089bb3d006ec69add28f4fcb3f4e79412c7f438b5b1067c2227e77b9c \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0ce68f1fcb..3bfa0c7484 100644 --- a/src/json.c +++ b/src/json.c @@ -191,7 +191,7 @@ static void jsonInit(JsonString *p, sqlite3_context *pCtx){ ** initial state. */ static void jsonReset(JsonString *p){ - if( !p->bStatic ) sqlite3_free(p->zBuf); + if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf); jsonZero(p); } @@ -212,7 +212,7 @@ static int jsonGrow(JsonString *p, u32 N){ char *zNew; if( p->bStatic ){ if( p->bErr ) return 1; - zNew = sqlite3_malloc64(nTotal); + zNew = sqlite3RCStrNew(nTotal); if( zNew==0 ){ jsonOom(p); return SQLITE_NOMEM; @@ -221,12 +221,12 @@ static int jsonGrow(JsonString *p, u32 N){ p->zBuf = zNew; p->bStatic = 0; }else{ - zNew = sqlite3_realloc64(p->zBuf, nTotal); - if( zNew==0 ){ - jsonOom(p); + p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal); + if( p->zBuf==0 ){ + p->bErr = 1; + jsonZero(p); return SQLITE_NOMEM; } - p->zBuf = zNew; } p->nAlloc = nTotal; return SQLITE_OK; @@ -514,16 +514,26 @@ static void jsonAppendValue( /* Make the JSON in p the result of the SQL function. +** +** The JSON string is reset. */ static void jsonResult(JsonString *p){ if( p->bErr==0 ){ - jsonAppendChar(p, 0); - sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed-1, - p->bStatic ? SQLITE_TRANSIENT : sqlite3_free, - SQLITE_UTF8); - jsonZero(p); + if( p->bStatic ){ + sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, SQLITE_TRANSIENT, + SQLITE_UTF8); + }else{ + jsonAppendChar(p, 0); + p->nUsed--; + sqlite3RCStrRef(p->zBuf); + sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, + (void(*)(void*))sqlite3RCStrUnref, + SQLITE_UTF8); + } + }else{ + sqlite3_result_error_nomem(p->pCtx); } - assert( p->bStatic ); + jsonReset(p); } /************************************************************************** @@ -1885,7 +1895,7 @@ static JsonNode *jsonLookupStep( JsonNode *pRoot = &pParse->aNode[iRoot]; while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){ u32 idx = (u32)(pRoot - pParse->aNode); - u32 i = pParse->iSubst; + i = pParse->iSubst; while( 1 /*exit-by-break*/ ){ assert( inNode ); assert( pParse->aNode[i].eType==JSON_SUBST ); @@ -2950,7 +2960,8 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ assert( pStr->bStatic ); }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, - pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); + pStr->bStatic ? SQLITE_TRANSIENT : + (void(*)(void*))sqlite3RCStrUnref); pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); @@ -3058,7 +3069,8 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ assert( pStr->bStatic ); }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, - pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); + pStr->bStatic ? SQLITE_TRANSIENT : + (void(*)(void*))sqlite3RCStrUnref); pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); diff --git a/src/printf.c b/src/printf.c index 3fb1a322a0..7b84288168 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1366,3 +1366,146 @@ void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ sqlite3_str_vappendf(p, zFormat, ap); va_end(ap); } + + +/***************************************************************************** +** Reference counted string storage +*****************************************************************************/ + +/* +** Increase the reference count of the string by one. +** +** The input parameter is returned. +*/ +char *sqlite3RCStrRef(char *z){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + p->nRCRef++; + return z; +} + +/* +** Decrease the reference count by one. Free the string when the +** reference count reaches zero. +*/ +void sqlite3RCStrUnref(char *z){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + assert( p->nRCRef>0 ); + assert( p->uMagic==SQLITE_RCSTR_MAGIC ); + if( p->nRCRef>=2 ){ + p->nRCRef--; + }else{ + if( p->xFree ) p->xFree(p->pAttach); +#ifdef SQLITE_DEBUG + p->uMagic = 0; +#endif + sqlite3_free(p); + } +} + +/* +** Return true if the reference count on the string is exactly one, meaning +** that the string can be modified. Return false if the reference count +** is greater than one. +*/ +int sqlite3RCStrIsWriteable(char *z){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + assert( p->nRCRef>0 ); + assert( p->uMagic==SQLITE_RCSTR_MAGIC ); + return p->nRCRef==1; +} + +/* +** Create a new string that is capable of holding N bytes of text, not counting +** the zero byte at the end. The string is uninitialized. +** +** The reference count is initially 1. Call sqlite3RCStrUnref() to free the +** newly allocated string. +** +** This routine returns 0 on an OOM. +*/ +char *sqlite3RCStrNew(u64 N){ + RCStr *p = sqlite3_malloc64( N + sizeof(*p) ); + if( p==0 ) return 0; + p->nRCRef = 1; + p->xFree = 0; + p->pAttach = 0; +#ifdef SQLITE_DEBUG + p->uMagic = SQLITE_RCSTR_MAGIC; +#endif + return (char*)&p[1]; +} + +/* +** Return the number of bytes allocated to the string. The value returned +** does not include the space for the zero-terminator at the end. +*/ +u64 sqlite3RCStrSize(char *z){ + RCStr *p = (RCStr*)z; + u64 N; + assert( p!=0 ); + p--; + assert( p->nRCRef>0 ); + assert( p->uMagic==SQLITE_RCSTR_MAGIC ); + N = sqlite3_msize(p); + N -= sizeof(p) + 1; + return N; +} + +/* +** Change the size of the string so that it is able to hold N bytes. +** The string might be reallocated, so return the new allocation. +*/ +char *sqlite3RCStrResize(char *z, u64 N){ + RCStr *p = (RCStr*)z; + RCStr *pNew; + assert( p!=0 ); + p--; + assert( p->nRCRef==1 ); + assert( p->uMagic==SQLITE_RCSTR_MAGIC ); + pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1); + if( pNew==0 ){ + sqlite3_free(p); + return 0; + }else{ + return (char*)&pNew[1]; + } +} + +/* +** Add a new attachment to the string. +** +** A string may have no more than one attachment. When a new attachment +** is added, any prior attachment is destroyed. Remove an attachment +** by adding a zero-attachment. +*/ +void sqlite3RCStrAttach(char *z, void *pAttach, void(*xFree)(void*)){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + assert( p->nRCRef>0 ); + assert( p->uMagic==SQLITE_RCSTR_MAGIC ); + if( p->xFree ) p->xFree(p->pAttach); + p->xFree = xFree; + p->pAttach = pAttach; +} + +/* +** Return the attachment associated with a string if the attachment +** has the destructure specified in the second argument. If the +** string has no attachment or if the destructor does not match, +** then return a NULL pointr. +*/ +void *sqlite3RCStrGetAttachment(char *z, void(*xFree)(void*)){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + assert( p->nRCRef>0 ); + assert( p->uMagic==SQLITE_RCSTR_MAGIC ); + return p->xFree==xFree ? p->pAttach : 0; +} diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f214862f74..adf4d34dec 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1281,6 +1281,7 @@ typedef struct Parse Parse; typedef struct ParseCleanup ParseCleanup; typedef struct PreUpdate PreUpdate; typedef struct PrintfArguments PrintfArguments; +typedef struct RCStr RCStr; typedef struct RenameToken RenameToken; typedef struct Returning Returning; typedef struct RowSet RowSet; @@ -4061,6 +4062,47 @@ struct sqlite3_str { #define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0) +/* +** The following object is the header for an "RCStr" or "reference-counted +** string". An RCStr is passed around and used like any other char* +** that has been dynamically allocated. The important interface +** difference is that it uses sqlite3RCStrUnref() as its destructor +** rather than sqlite3_free(). Other than that the two are interchangeable. +** +** Thus to return an RCStr object as the result of an SQL function use: +** +** sqlite3_result_text64(ctx,z,sz,sqlite3RCStrUnref,SQLITE_UTF8) +** ^^^^^^^^^^^^^^^^^ +** Instead of sqlite3_free() or similar +** +** An SQL function can check its arguments to see if they are RCStr +** strings using the sqlite3ValueIsOfClass() function: +** +** sqlite3ValueIsOfClass(argv[i], sqlite3RCStrUnref); +** +** An RCStr string might be better than an ordinary string in some cases +** because: +** +** (1) You can duplicate it using sqlite3RCStrRef(x). +** +** (2) You can also add an associated object to the string. For +** example, if the string is JSON, perhaps the associated object +** is a parse of that JSON. +** +** Methods for an RCStr string begin with "sqlite3RCStr...". +*/ +struct RCStr { + u32 nRCRef; /* Number of references */ +#ifdef SQLITE_DEBUG + u32 uMagic; /* Magic number for sanity checking */ +#endif + void *pAttach; /* Attachment to this string */ + void (*xFree)(void*); /* Destructor for the attachment */ +}; + +/* The Magic number used by RCStr for sanity checking. SQLITE_DEBUG only. */ +#define SQLITE_RCSTR_MAGIC 0x3dc05d54 + /* ** A pointer to this structure is used to communicate information @@ -5180,6 +5222,7 @@ void sqlite3FileSuffix3(const char*, char*); u8 sqlite3GetBoolean(const char *z,u8); const void *sqlite3ValueText(sqlite3_value*, u8); +int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*)); int sqlite3ValueBytes(sqlite3_value*, u8); void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); @@ -5287,6 +5330,15 @@ void sqlite3OomClear(sqlite3*); int sqlite3ApiExit(sqlite3 *db, int); int sqlite3OpenTempDatabase(Parse *); +char *sqlite3RCStrRef(char*); +void sqlite3RCStrUnref(char*); +char *sqlite3RCStrNew(u64); +u64 sqlite3RCStrSize(char*); +char *sqlite3RCStrResize(char*,u64); +int sqlite3RCStrIsWriteable(char*); +void sqlite3RCStrAttach(char*, void*, void(*)(void*)); +void *sqlite3RCStrGetAttachment(char*,void(*)(void*)); + void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); int sqlite3StrAccumEnlarge(StrAccum*, i64); char *sqlite3StrAccumFinish(StrAccum*); diff --git a/src/vdbemem.c b/src/vdbemem.c index b5a794ae8f..87dfbbebd8 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -333,6 +333,11 @@ void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ pMem->flags |= MEM_Term; return; } + if( pMem->xDel==(void(*)(void*))sqlite3RCStrUnref ){ + /* Blindly assume that all RCStr objects are zero-terminated */ + pMem->flags |= MEM_Term; + return; + } }else if( pMem->szMalloc>0 && pMem->szMalloc >= pMem->n+1 ){ pMem->z[pMem->n] = 0; pMem->flags |= MEM_Term; @@ -1363,6 +1368,24 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ return valueToText(pVal, enc); } +/* Return true if sqlit3_value object pVal is a string or blob value +** that uses the destructor specified in the second argument. +** +** TODO: Maybe someday promote this interface into a published API so +** that third-party extensions can get access to it? +*/ +int sqlite3ValueIsOfClass(const sqlite3_value *pVal, void(*xFree)(void*)){ + if( ALWAYS(pVal!=0) + && (pVal->flags & (MEM_Str|MEM_Blob))!=0 + && (pVal->flags & MEM_Dyn)!=0 + && pVal->xDel==xFree + ){ + return 1; + }else{ + return 0; + } +} + /* ** Create a new sqlite3_value object. */ From 4bca1248455c6b8c4c79ac953461f4726429ed02 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 25 Jul 2023 15:43:01 +0000 Subject: [PATCH 2/6] Fix a minor problem with error reporting in JSON. FossilOrigin-Name: c456e4a8999066cd96246327101b3cca78294511a71a2ac07939bb702bfcb5f4 --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index b4a115361a..f3393df05d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Create\sthe\snew\sRCStr\sclass\sof\sstrings\sand\stry\sto\suse\sthem\sfor\sJSON\sstorage. -D 2023-07-25T15:08:18.543 +C Fix\sa\sminor\sproblem\swith\serror\sreporting\sin\sJSON. +D 2023-07-25T15:43:01.908 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 3add12eb29f09a99dc260bcde427ea925f600cf0bb0d9abb0618a03d7e50eead +F src/json.c 8f44f1aadfe1967af70dc9e6390823806f5a42cc301f11df435a1c3d4950308d F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465 F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002 @@ -2044,11 +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 28c6e964b1e2257527df02b352f02e135f7c4b764b8b41eda6461f9538f5d042 -R 4c5edeff0b59e42b46536f2b483d14f6 -T *branch * json-opt-rcstr -T *sym-json-opt-rcstr * -T -sym-json-opt * +P c1b8725089bb3d006ec69add28f4fcb3f4e79412c7f438b5b1067c2227e77b9c +R 625da2283c68c7b3c0e2eb949802be15 U drh -Z 3f6f4e5856b228a13fb14e603baeb559 +Z 238deba72513ff58266287df7bec917f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 59c5808d17..70df72c921 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c1b8725089bb3d006ec69add28f4fcb3f4e79412c7f438b5b1067c2227e77b9c \ No newline at end of file +c456e4a8999066cd96246327101b3cca78294511a71a2ac07939bb702bfcb5f4 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3bfa0c7484..18c4721455 100644 --- a/src/json.c +++ b/src/json.c @@ -530,7 +530,7 @@ static void jsonResult(JsonString *p){ (void(*)(void*))sqlite3RCStrUnref, SQLITE_UTF8); } - }else{ + }else if( p->bErr==1 ){ sqlite3_result_error_nomem(p->pCtx); } jsonReset(p); From 440e696d5e0b31a2542a01b3fba9eb356fea52ad Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 25 Jul 2023 18:28:03 +0000 Subject: [PATCH 3/6] Incremental improvements to JSON parsing - trying to fold in the RCStr object. FossilOrigin-Name: 4cb15d934a85ebc290fe6dd8cd3bd47b159561ca75d72bbffef30b9ea4623b09 --- manifest | 12 ++-- manifest.uuid | 2 +- src/json.c | 184 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 166 insertions(+), 32 deletions(-) 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); From 59b8e666f669a97e7d62fd1a6e24daed446f027e Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 25 Jul 2023 20:26:47 +0000 Subject: [PATCH 4/6] Clarify ownership of the various objects involved in parsing JSON. FossilOrigin-Name: afe02a398a16d51bd7482b6fbe2fbd15d9ac4fd9cdbc9d2bf81f38b3391fc567 --- manifest | 14 ++-- manifest.uuid | 2 +- src/json.c | 205 ++++++++++++++++---------------------------------- src/printf.c | 2 +- 4 files changed, 75 insertions(+), 148 deletions(-) diff --git a/manifest b/manifest index 09e5a7cfff..437038d380 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Incremental\simprovements\sto\sJSON\sparsing\s-\strying\sto\sfold\sin\sthe\sRCStr\sobject. -D 2023-07-25T18:28:03.341 +C Clarify\sownership\sof\sthe\svarious\sobjects\sinvolved\sin\sparsing\sJSON. +D 2023-07-25T20:26:47.054 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 28fe8ed9e63293fd5708d129d4edd4a634887aabb0364685195974e35ee10762 +F src/json.c 97f8c20c1cfefbc54e2b8b2143a24aae4993c12de6e74764af443e033b01b013 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465 F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002 @@ -634,7 +634,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c 21e410b0a3904dddb39ef1b245cfb991302022a6f0fc16f0a8a13539d6d2f713 +F src/printf.c 1406ade1451adfa4374d8e9bbb8606109742c1216f5dbc95e011bf721fd91365 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -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 c456e4a8999066cd96246327101b3cca78294511a71a2ac07939bb702bfcb5f4 -R 2b5df60a4958d2a70ce11e3569ae2bab +P 4cb15d934a85ebc290fe6dd8cd3bd47b159561ca75d72bbffef30b9ea4623b09 +R cbb913300d23d0635674a5fded1ff5ab U drh -Z d10e127820cbbcdc4a84e1b86eb42adb +Z f943de02bc9e737c29b8eb0aa52b28c4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a811037656..f222cbf323 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4cb15d934a85ebc290fe6dd8cd3bd47b159561ca75d72bbffef30b9ea4623b09 \ No newline at end of file +afe02a398a16d51bd7482b6fbe2fbd15d9ac4fd9cdbc9d2bf81f38b3391fc567 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 194d0421eb..6cb429f407 100644 --- a/src/json.c +++ b/src/json.c @@ -171,6 +171,17 @@ struct JsonParse { ** Utility routines for dealing with JsonString objects **************************************************************************/ +#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 jsonStringClass(void *p){ + sqlite3RCStrUnref((char*)p); +} +#endif + /* Set the JsonString object to an empty string */ static void jsonZero(JsonString *p){ @@ -1700,16 +1711,27 @@ json_parse_restart: ** pParse. ** ** 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 to bTakeJson. If bTakeJson is 1, the newly initialized +** JsonParse object will become the own the zJson input string. If bTakeJson +** is 0, then the caller is responsible for preserving zJson for the lifetime +** of the JsonParse object. */ 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 */ + char *zJson, /* Input JSON text to be parsed */ + int bTakeJson /* Assume ownership of zJson if true */ ){ int i; memset(pParse, 0, sizeof(*pParse)); if( zJson==0 ) return 1; pParse->zJson = zJson; + pParse->bOwnsJson = bTakeJson; + pParse->nJPRef = 1; i = jsonParseValue(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ @@ -1738,120 +1760,6 @@ 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. @@ -1905,18 +1813,28 @@ static int jsonParseFindParents(JsonParse *pParse){ ** 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 +** is no longer valid, parse the JSON again and return the new parse. +** Also register the new parse so that it will be available for ** future sqlite3_get_auxdata() calls. ** ** If an error occurs and pErrCtx!=0 then report the error on pErrCtx ** and return NULL. ** -** If an error occurs and pErrCtx==0 then return the Parse object with -** JsonParse.nErr non-zero. If the caller invokes this routine with -** pErrCtx==0 and it gets back a JsonParse with nErr!=0, then the caller -** is responsible for invoking jsonParseFree() on the returned value. -** But the caller may invoke jsonParseFree() *only* if pParse->nErr!=0. +** The returned pointer (if it is not NULL) is owned by the cache in +** most cases, not the caller. The caller does NOT need to invoke +** jsonParseFree(), in most cases. +** +** Except, if an error occurs and pErrCtx==0 then return the JsonParse +** object with JsonParse.nErr non-zero and the caller will own the JsonParse +** object. In that case, it will be the responsibility of the caller to +** invoke jsonParseFree(). To summarize: +** +** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the +** cache. Call does not need to +** free it. +** +** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller +** and so the caller must free it. */ static JsonParse *jsonParseCached( sqlite3_context *pCtx, @@ -1931,6 +1849,7 @@ static JsonParse *jsonParseCached( int iMinKey = 0; u32 iMinHold = 0xffffffff; u32 iMaxHold = 0; + if( zJson==0 ) return 0; for(iKey=0; iKeynErr = 0; pMatch->iHold = iMaxHold+1; + assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */ return pMatch; } - p = sqlite3_malloc64( sizeof(*p) + nJson + 1 ); + p = sqlite3_malloc64( sizeof(*p) ); 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, pErrCtx, p->zJson) ){ - if( pErrCtx==0 ){ - p->nErr = 1; - return p; - } + p->zJson = sqlite3RCStrNew( nJson ); + if( p->zJson==0 ){ sqlite3_free(p); + sqlite3_result_error_nomem(pCtx); + return 0; + } + memcpy(p->zJson, zJson, nJson); + p->zJson[nJson] = 0; + if( jsonParse(p, pErrCtx, p->zJson, 1) ){ + if( pErrCtx==0 ){ + p->nErr = 1; + assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */ + return p; + } + jsonParseFree(p); return 0; } - p->nJPRef = 1; p->nJson = nJson; p->iHold = iMaxHold+1; + /* Transfer ownership of the new JsonParse to the cache */ sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, (void(*)(void*))jsonParseFree); return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); @@ -2357,7 +2284,7 @@ static void jsonParseFunc( u32 i; assert( argc==1 ); - if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0])) ) return; + if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0]), 0) ) return; jsonParseFindParents(&x); jsonInit(&s, ctx); for(i=0; izJson; + const char *z = (const char*)sqlite3_value_text(argv[0]); for(i=0; iiErr && ALWAYS(z[i]); i++){ if( (z[i]&0xc0)!=0x80 ) n++; } @@ -3639,7 +3566,7 @@ static int jsonEachFilter( 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) ){ + if( jsonParse(&p->sParse, 0, p->zJson, 0) ){ int rc = SQLITE_NOMEM; if( p->sParse.oom==0 ){ sqlite3_free(cur->pVtab->zErrMsg); diff --git a/src/printf.c b/src/printf.c index 7b84288168..222e70be29 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1430,7 +1430,7 @@ int sqlite3RCStrIsWriteable(char *z){ ** This routine returns 0 on an OOM. */ char *sqlite3RCStrNew(u64 N){ - RCStr *p = sqlite3_malloc64( N + sizeof(*p) ); + RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 ); if( p==0 ) return 0; p->nRCRef = 1; p->xFree = 0; From 0f200bc580eda625f48866d0311ce6f57bfa531a Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 26 Jul 2023 00:48:45 +0000 Subject: [PATCH 5/6] Add the JsonParse.zAlt field to old revised JSON text after a change. Demonstrate that this elminates the need for reparsing after a change by using it in the json_remove() function. This is an incremental check-in containing lots of cruft. FossilOrigin-Name: f930b139d6db0ee799bc90397b225175103c4bf22923d1c9cbcd32509adc1738 --- manifest | 18 +++--- manifest.uuid | 2 +- src/json.c | 146 +++++++++++++++++++++++++++++++++++------------- src/printf.c | 10 +++- src/sqliteInt.h | 10 ++-- src/vdbemem.c | 2 + 6 files changed, 134 insertions(+), 54 deletions(-) diff --git a/manifest b/manifest index 437038d380..875c317328 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Clarify\sownership\sof\sthe\svarious\sobjects\sinvolved\sin\sparsing\sJSON. -D 2023-07-25T20:26:47.054 +C Add\sthe\sJsonParse.zAlt\sfield\sto\sold\srevised\sJSON\stext\safter\sa\schange.\nDemonstrate\sthat\sthis\selminates\sthe\sneed\sfor\sreparsing\safter\sa\schange\nby\susing\sit\sin\sthe\sjson_remove()\sfunction.\s\sThis\sis\san\sincremental\scheck-in\ncontaining\slots\sof\scruft. +D 2023-07-26T00:48:45.768 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 97f8c20c1cfefbc54e2b8b2143a24aae4993c12de6e74764af443e033b01b013 +F src/json.c a7823b0593eac42a608f8439e6b1f28022ac5fce9aa81a82856e48dd07ab601d F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465 F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002 @@ -634,7 +634,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c 1406ade1451adfa4374d8e9bbb8606109742c1216f5dbc95e011bf721fd91365 +F src/printf.c 56e362bbefbe61b5b211f1886e3cdb9b25d8ba817c12df3a15f11b9bdc152efd F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -643,7 +643,7 @@ F src/shell.c.in d320d8a13636de06d777cc1eab981caca304e175464e98183cf4ea68d93db81 F src/sqlite.h.in f999ef3642f381d69679b2516b430dbcb6c5a2a951b7f5e43dc4751b474a5774 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h 59b755dec944aa3b068e962ef53264de6fde3d6b6df2d5869ea3afdb7facdf60 +F src/sqliteInt.h 29c8e6d0b6dfa9c2c356f23ab8349d7fac3679d5f7cfc01d4ee45c9171db2960 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -714,7 +714,7 @@ F src/vdbeInt.h 401813862f9d75af01bdb2ab99253ad019e9d6ddcc8058e4fa61a43e9a60d1f7 F src/vdbeapi.c dde6c4d0f87486f056b9db4d1ea185bb1d84a6839102b86e76316ba590d07cc7 F src/vdbeaux.c b5e3f7e158518b4eca6f166ac43900640a3fe9735c710e12bfa119af21059339 F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce -F src/vdbemem.c aee9ac636666616494d9a395d29efc3fe9e1404a9f043db81c82560b43b78f35 +F src/vdbemem.c 46ea371b10c3573cf9287d385411fbcc6cd42099ad3505d20e33f2f3ed91537d F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015 F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c aae4bd769410eb7e1d02c42613eec961d514459b1c3c1c63cfc84e92a137daac @@ -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 4cb15d934a85ebc290fe6dd8cd3bd47b159561ca75d72bbffef30b9ea4623b09 -R cbb913300d23d0635674a5fded1ff5ab +P afe02a398a16d51bd7482b6fbe2fbd15d9ac4fd9cdbc9d2bf81f38b3391fc567 +R 33c717c250039ffbe53dc8d7b2094929 U drh -Z f943de02bc9e737c29b8eb0aa52b28c4 +Z 96ad60bdf5cbde0ec2b901493a583f6e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f222cbf323..dce1119850 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -afe02a398a16d51bd7482b6fbe2fbd15d9ac4fd9cdbc9d2bf81f38b3391fc567 \ No newline at end of file +f930b139d6db0ee799bc90397b225175103c4bf22923d1c9cbcd32509adc1738 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6cb429f407..d7d464442b 100644 --- a/src/json.c +++ b/src/json.c @@ -142,7 +142,8 @@ 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 */ - char *zJson; /* Original JSON string */ + char *zJson; /* Original JSON string (before edits) */ + char *zAlt; /* Revised JSON after edits applied. Maybe NULL */ u32 *aUp; /* Index of parent of each node */ JsonTask *pClean; /* Cleanup operations prior to freeing this object */ u16 iDepth; /* Nesting depth */ @@ -151,8 +152,10 @@ struct JsonParse { 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 */ + u8 useMod; /* Actually use the edits contain inside aNode */ + u8 hasMod; /* aNode contains edits from the original zJson */ int nJson; /* Length of the zJson string in bytes */ + int nAlt; /* Length of alternative JSON string zAlt, in bytes */ u32 iErr; /* Error location in zJson[] */ u32 iSubst; /* Last known JSON_SUBST node */ u32 iHold; /* Replace cache line with the lowest iHold value */ @@ -245,6 +248,16 @@ static int jsonGrow(JsonString *p, u32 N){ return SQLITE_OK; } +/* Try to force the string to be an RCStr string, rather than a +** static string. Return true on success. The only reason this +** might fail is due to an OOM fault. +*/ +static int jsonForceRCStr(JsonString *p){ + if( p->bStatic==0 ) return 1; + jsonGrow(p, p->nAlloc+1); + return p->bStatic==0; +} + /* Append N bytes from zIn onto the end of the JsonString string. */ static SQLITE_NOINLINE void jsonAppendExpand( @@ -588,6 +601,11 @@ static void jsonParseReset(JsonParse *pParse){ sqlite3_free(pParse->aUp); pParse->aUp = 0; } + if( pParse->zAlt ){ + char *z = pParse->zAlt; + pParse->zAlt = 0; + sqlite3RCStrUnref(z); + } if( pParse->bOwnsJson ){ /* Order operations so that if the destructor for pParse->zJson ** invokes jsonParseFree(), the recursion will terminate harmlessly */ @@ -650,7 +668,7 @@ static void jsonRenderNode( JsonString *pOut /* Write JSON here */ ){ assert( pNode!=0 ); - while( (pNode->jnFlags & JNODE_REPLACE)!=0 ){ + while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){ u32 idx = (u32)(pNode - pParse->aNode); u32 i = pParse->iSubst; while( 1 /*exit-by-break*/ ){ @@ -722,13 +740,14 @@ static void jsonRenderNode( jsonAppendChar(pOut, '['); for(;;){ while( j<=pNode->n ){ - if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){ + if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); jsonRenderNode(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; @@ -741,7 +760,7 @@ static void jsonRenderNode( jsonAppendChar(pOut, '{'); for(;;){ while( j<=pNode->n ){ - if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){ + if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); jsonRenderNode(pParse, &pNode[j], pOut); jsonAppendChar(pOut, ':'); @@ -750,6 +769,7 @@ static void jsonRenderNode( 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; @@ -766,7 +786,8 @@ static void jsonRenderNode( static void jsonReturnJson( JsonParse *pParse, /* The complete JSON */ JsonNode *pNode, /* Node to return */ - sqlite3_context *pCtx /* Return value for this function */ + sqlite3_context *pCtx, /* Return value for this function */ + int bGenerateAlt /* Also store the rendered text in zAlt */ ){ JsonString s; if( pParse->oom ){ @@ -776,6 +797,12 @@ static void jsonReturnJson( if( pParse->nErr==0 ){ jsonInit(&s, pCtx); jsonRenderNode(pParse, pNode, &s); + if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){ + jsonAppendChar(&s, 0); + s.nUsed--; + pParse->zAlt = sqlite3RCStrRef(s.zBuf); + pParse->nAlt = s.nUsed; + } jsonResult(&s); sqlite3_result_subtype(pCtx, JSON_SUBTYPE); } @@ -965,7 +992,7 @@ static void jsonReturn( } case JSON_ARRAY: case JSON_OBJECT: { - jsonReturnJson(pParse, pNode, pCtx); + jsonReturnJson(pParse, pNode, pCtx, 0); break; } } @@ -1079,6 +1106,8 @@ static int jsonParseAddSubstNode( pParse->aNode[idx].eU = 4; pParse->aNode[idx].u.iPrev = pParse->iSubst; pParse->iSubst = idx; + pParse->hasMod = 1; + pParse->useMod = 1; return idx; } @@ -1729,6 +1758,7 @@ static int jsonParse( int i; memset(pParse, 0, sizeof(*pParse)); if( zJson==0 ) return 1; +//printf("PARSE %s\n", zJson); pParse->zJson = zJson; pParse->bOwnsJson = bTakeJson; pParse->nJPRef = 1; @@ -1837,9 +1867,10 @@ static int jsonParseFindParents(JsonParse *pParse){ ** and so the caller must free it. */ static JsonParse *jsonParseCached( - sqlite3_context *pCtx, - sqlite3_value *pJson, - sqlite3_context *pErrCtx + sqlite3_context *pCtx, /* Context to use for cache search */ + sqlite3_value *pJson, /* Function param containing JSON text */ + sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */ + int bUnedited /* No prior edits allowed */ ){ char *zJson = (char*)sqlite3_value_text(pJson); int nJson = sqlite3_value_bytes(pJson); @@ -1859,10 +1890,24 @@ static JsonParse *jsonParseCached( } if( pMatch==0 && p->nJson==nJson + && (p->hasMod==0 || bUnedited==0) && memcmp(p->zJson,zJson,nJson)==0 ){ p->nErr = 0; + p->useMod = 0; pMatch = p; +//printf("HIT %s at %d\n", zJson, iKey); + }else + if( pMatch==0 + && p->zAlt!=0 + && bUnedited==0 + && p->nAlt==nJson + && memcmp(p->zAlt, zJson, nJson)==0 + ){ + p->nErr = 0; + p->useMod = 1; + pMatch = p; +//printf("HIT %s at %d-alt\n", zJson, iKey); }else if( p->iHoldiHold; iMinKey = iKey; @@ -1872,11 +1917,18 @@ static JsonParse *jsonParseCached( } } if( pMatch ){ + /* The input JSON text was found in the cache. Use the preexisting + ** parse of this JSON */ pMatch->nErr = 0; pMatch->iHold = iMaxHold+1; assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */ return pMatch; } + + /* The input JSON was not found anywhere in the cache. We will need + ** to parse it ourselves and generate a new JsonParse object. + */ +//printf("MISS %s\n", zJson); p = sqlite3_malloc64( sizeof(*p) ); if( p==0 ){ sqlite3_result_error_nomem(pCtx); @@ -1954,7 +2006,7 @@ static JsonNode *jsonLookupStep( u32 i, j, nKey; const char *zKey; JsonNode *pRoot = &pParse->aNode[iRoot]; - while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){ + while( (pRoot->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){ u32 idx = (u32)(pRoot - pParse->aNode); i = pParse->iSubst; while( 1 /*exit-by-break*/ ){ @@ -2004,6 +2056,7 @@ static JsonNode *jsonLookupStep( j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; assert( pRoot->eU==2 ); iRoot = pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; @@ -2012,6 +2065,7 @@ static JsonNode *jsonLookupStep( if( pApnd ){ u32 iStart, iLabel; JsonNode *pNode; + assert( pParse->useMod ); iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); zPath += i; @@ -2041,10 +2095,11 @@ static JsonNode *jsonLookupStep( if( pRoot->eType!=JSON_ARRAY ) return 0; for(;;){ while( j<=pBase->n ){ - if( (pBase[j].jnFlags & JNODE_REMOVE)==0 ) i++; + if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++; j += jsonNodeSize(&pBase[j]); } if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; assert( pBase->eU==2 ); iBase = pBase->u.iAppend; pBase = &pParse->aNode[iBase]; @@ -2074,11 +2129,14 @@ static JsonNode *jsonLookupStep( zPath += j + 1; j = 1; for(;;){ - while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){ - if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--; + while( j<=pRoot->n + && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod)) + ){ + if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--; j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; assert( pRoot->eU==2 ); iRoot = pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; @@ -2090,6 +2148,7 @@ static JsonNode *jsonLookupStep( if( i==0 && pApnd ){ u32 iStart; JsonNode *pNode; + assert( pParse->useMod ); iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); if( pParse->oom ) return 0; @@ -2392,7 +2451,7 @@ static void jsonArrayLengthFunc( u32 i; JsonNode *pNode; - p = jsonParseCached(ctx, argv[0], ctx); + p = jsonParseCached(ctx, argv[0], ctx, 0); if( p==0 ) return; assert( p->nNode ); if( argc==2 ){ @@ -2405,9 +2464,14 @@ static void jsonArrayLengthFunc( return; } if( pNode->eType==JSON_ARRAY ){ - assert( (pNode->jnFlags & JNODE_APPEND)==0 ); - for(i=1; i<=pNode->n; n++){ - i += jsonNodeSize(&pNode[i]); + while( 1 /*exit-by-break*/ ){ + for(i=1; i<=pNode->n; n++){ + i += jsonNodeSize(&pNode[i]); + } + if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( p->useMod==0 ) break; + assert( pNode->eU==2 ); + pNode = &p->aNode[pNode->u.iAppend]; } } sqlite3_result_int64(ctx, n); @@ -2454,7 +2518,7 @@ static void jsonExtractFunc( JsonString jx; if( argc<2 ) return; - p = jsonParseCached(ctx, argv[0], ctx); + p = jsonParseCached(ctx, argv[0], ctx, 0); if( p==0 ) return; if( argc==2 ){ /* With a single PATH argument */ @@ -2487,7 +2551,7 @@ static void jsonExtractFunc( } if( pNode ){ if( flags & JSON_JSON ){ - jsonReturnJson(p, pNode, ctx); + jsonReturnJson(p, pNode, ctx, 0); }else{ jsonReturn(p, pNode, ctx); sqlite3_result_subtype(ctx, 0); @@ -2613,12 +2677,14 @@ static void jsonPatchFunc( 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); jsonDebugPrintNode(pResult); - jsonReturnJson(&x, pResult, ctx); + jsonReturnJson(&x, pResult, ctx, 0); }else{ sqlite3_result_error_nomem(ctx); } @@ -2679,27 +2745,30 @@ static void jsonRemoveFunc( int argc, sqlite3_value **argv ){ - JsonParse x; /* The parse */ + JsonParse *pParse; /* The parse */ JsonNode *pNode; const char *zPath; u32 i; if( argc<1 ) return; - if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0]), 0) ) return; - assert( x.nNode ); + pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); + if( pParse==0 ) return; for(i=1; i<(u32)argc; i++){ zPath = (const char*)sqlite3_value_text(argv[i]); if( zPath==0 ) goto remove_done; - pNode = jsonLookup(&x, zPath, 0, ctx); - if( x.nErr ) goto remove_done; - if( pNode ) pNode->jnFlags |= JNODE_REMOVE; + pNode = jsonLookup(pParse, zPath, 0, ctx); + if( pParse->nErr ) goto remove_done; + if( pNode ){ + pNode->jnFlags |= JNODE_REMOVE; + pParse->hasMod = 1; + pParse->useMod = 1; + } } - if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){ - jsonReturnJson(&x, x.aNode, ctx); + if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){ + jsonReturnJson(pParse, pParse->aNode, ctx, 1); } remove_done: - jsonDebugPrintParse(&x); - jsonParseReset(&x); + jsonDebugPrintParse(p); } /* @@ -2763,7 +2832,7 @@ static void jsonReplaceNode( sqlite3_result_error_nomem(pCtx); } }else{ - JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx); + JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1); if( pPatch==0 ){ p->oom = 1; break; @@ -2817,7 +2886,7 @@ static void jsonReplaceFunc( jsonReplaceNode(ctx, &x, (u32)(pNode - x.aNode), argv[i+1]); } } - jsonReturnJson(&x, x.aNode, ctx); + jsonReturnJson(&x, x.aNode, ctx, 0); replace_err: jsonDebugPrintParse(&x); jsonParseReset(&x); @@ -2858,6 +2927,7 @@ static void jsonSetFunc( for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); bApnd = 0; + x.useMod = 1; pNode = jsonLookup(&x, zPath, &bApnd, ctx); if( x.oom ){ sqlite3_result_error_nomem(ctx); @@ -2869,7 +2939,7 @@ static void jsonSetFunc( } } jsonDebugPrintParse(&x); - jsonReturnJson(&x, x.aNode, ctx); + jsonReturnJson(&x, x.aNode, ctx, 0); jsonSetDone: jsonParseReset(&x); @@ -2891,7 +2961,7 @@ static void jsonTypeFunc( const char *zPath; JsonNode *pNode; - p = jsonParseCached(ctx, argv[0], ctx); + p = jsonParseCached(ctx, argv[0], ctx, 0); if( p==0 ) return; if( argc==2 ){ zPath = (const char*)sqlite3_value_text(argv[1]); @@ -2918,12 +2988,12 @@ static void jsonValidFunc( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - p = jsonParseCached(ctx, argv[0], 0); + p = jsonParseCached(ctx, argv[0], 0, 0); if( p==0 || p->oom ){ sqlite3_result_error_nomem(ctx); sqlite3_free(p); }else{ - sqlite3_result_int(ctx, p->nErr==0 && p->hasNonstd==0); + sqlite3_result_int(ctx, p->nErr==0 && (p->hasNonstd==0 || p->useMod)); if( p->nErr ) jsonParseFree(p); } } @@ -2964,7 +3034,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], 0); + p = jsonParseCached(ctx, argv[0], 0, 0); if( p==0 || p->oom ){ sqlite3_result_error_nomem(ctx); sqlite3_free(p); diff --git a/src/printf.c b/src/printf.c index 222e70be29..a55bd2b85b 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1406,6 +1406,7 @@ void sqlite3RCStrUnref(char *z){ } } +#if 0 /* ** Return true if the reference count on the string is exactly one, meaning ** that the string can be modified. Return false if the reference count @@ -1419,6 +1420,7 @@ int sqlite3RCStrIsWriteable(char *z){ assert( p->uMagic==SQLITE_RCSTR_MAGIC ); return p->nRCRef==1; } +#endif /* ** Create a new string that is capable of holding N bytes of text, not counting @@ -1441,6 +1443,7 @@ char *sqlite3RCStrNew(u64 N){ return (char*)&p[1]; } +#if 0 /* ** Return the number of bytes allocated to the string. The value returned ** does not include the space for the zero-terminator at the end. @@ -1456,6 +1459,7 @@ u64 sqlite3RCStrSize(char *z){ N -= sizeof(p) + 1; return N; } +#endif /* ** Change the size of the string so that it is able to hold N bytes. @@ -1477,6 +1481,7 @@ char *sqlite3RCStrResize(char *z, u64 N){ } } +#if 0 /* ** Add a new attachment to the string. ** @@ -1494,7 +1499,9 @@ void sqlite3RCStrAttach(char *z, void *pAttach, void(*xFree)(void*)){ p->xFree = xFree; p->pAttach = pAttach; } - +#endif + +#if 0 /* ** Return the attachment associated with a string if the attachment ** has the destructure specified in the second argument. If the @@ -1509,3 +1516,4 @@ void *sqlite3RCStrGetAttachment(char *z, void(*xFree)(void*)){ assert( p->uMagic==SQLITE_RCSTR_MAGIC ); return p->xFree==xFree ? p->pAttach : 0; } +#endif diff --git a/src/sqliteInt.h b/src/sqliteInt.h index adf4d34dec..358063534f 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -5222,7 +5222,7 @@ void sqlite3FileSuffix3(const char*, char*); u8 sqlite3GetBoolean(const char *z,u8); const void *sqlite3ValueText(sqlite3_value*, u8); -int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*)); +//int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*)); int sqlite3ValueBytes(sqlite3_value*, u8); void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); @@ -5333,11 +5333,11 @@ int sqlite3OpenTempDatabase(Parse *); char *sqlite3RCStrRef(char*); void sqlite3RCStrUnref(char*); char *sqlite3RCStrNew(u64); -u64 sqlite3RCStrSize(char*); +//u64 sqlite3RCStrSize(char*); char *sqlite3RCStrResize(char*,u64); -int sqlite3RCStrIsWriteable(char*); -void sqlite3RCStrAttach(char*, void*, void(*)(void*)); -void *sqlite3RCStrGetAttachment(char*,void(*)(void*)); +//int sqlite3RCStrIsWriteable(char*); +//void sqlite3RCStrAttach(char*, void*, void(*)(void*)); +//void *sqlite3RCStrGetAttachment(char*,void(*)(void*)); void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); int sqlite3StrAccumEnlarge(StrAccum*, i64); diff --git a/src/vdbemem.c b/src/vdbemem.c index 87dfbbebd8..d44a41716f 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1368,6 +1368,7 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ return valueToText(pVal, enc); } +#if 0 /* Return true if sqlit3_value object pVal is a string or blob value ** that uses the destructor specified in the second argument. ** @@ -1385,6 +1386,7 @@ int sqlite3ValueIsOfClass(const sqlite3_value *pVal, void(*xFree)(void*)){ return 0; } } +#endif /* ** Create a new sqlite3_value object. From 44f53b96472a660e42f4c4f33e01f0fc9c691440 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 26 Jul 2023 01:05:08 +0000 Subject: [PATCH 6/6] Extend the enhancement to json_set() and json_replace(). Clean up cruft. FossilOrigin-Name: 2dbb22c75e86f2e3ced38ac14b4943570d5c2f86cd5e37e875bf0c863be28836 --- manifest | 18 +++++------ manifest.uuid | 2 +- src/json.c | 44 ++++++++++++-------------- src/printf.c | 82 ------------------------------------------------- src/sqliteInt.h | 45 ++++++--------------------- src/vdbemem.c | 20 ------------ 6 files changed, 39 insertions(+), 172 deletions(-) diff --git a/manifest b/manifest index 875c317328..b712bbf97f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sJsonParse.zAlt\sfield\sto\sold\srevised\sJSON\stext\safter\sa\schange.\nDemonstrate\sthat\sthis\selminates\sthe\sneed\sfor\sreparsing\safter\sa\schange\nby\susing\sit\sin\sthe\sjson_remove()\sfunction.\s\sThis\sis\san\sincremental\scheck-in\ncontaining\slots\sof\scruft. -D 2023-07-26T00:48:45.768 +C Extend\sthe\senhancement\sto\sjson_set()\sand\sjson_replace().\s\sClean\sup\scruft. +D 2023-07-26T01:05:08.003 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 a7823b0593eac42a608f8439e6b1f28022ac5fce9aa81a82856e48dd07ab601d +F src/json.c 790ca70b1f8884610fe5401ac6f260d955d47d9c14c9f3d2aa6541fd881faf07 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465 F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002 @@ -634,7 +634,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c 56e362bbefbe61b5b211f1886e3cdb9b25d8ba817c12df3a15f11b9bdc152efd +F src/printf.c e3ba080e2f409f9bfcc8d34724e6fc160e9c718dc92d0548f6b71b8b6f860ce2 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -643,7 +643,7 @@ F src/shell.c.in d320d8a13636de06d777cc1eab981caca304e175464e98183cf4ea68d93db81 F src/sqlite.h.in f999ef3642f381d69679b2516b430dbcb6c5a2a951b7f5e43dc4751b474a5774 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h 29c8e6d0b6dfa9c2c356f23ab8349d7fac3679d5f7cfc01d4ee45c9171db2960 +F src/sqliteInt.h 30d7b0d586a4d03a384dcb60088c81b6fc6f74ce85cc3a0b3242eedc3cc24dbd F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -714,7 +714,7 @@ F src/vdbeInt.h 401813862f9d75af01bdb2ab99253ad019e9d6ddcc8058e4fa61a43e9a60d1f7 F src/vdbeapi.c dde6c4d0f87486f056b9db4d1ea185bb1d84a6839102b86e76316ba590d07cc7 F src/vdbeaux.c b5e3f7e158518b4eca6f166ac43900640a3fe9735c710e12bfa119af21059339 F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce -F src/vdbemem.c 46ea371b10c3573cf9287d385411fbcc6cd42099ad3505d20e33f2f3ed91537d +F src/vdbemem.c 33da4f30ddba2670bc1e617c3262b66aef2a8039043d4ff93e5c97974991089d F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015 F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c aae4bd769410eb7e1d02c42613eec961d514459b1c3c1c63cfc84e92a137daac @@ -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 afe02a398a16d51bd7482b6fbe2fbd15d9ac4fd9cdbc9d2bf81f38b3391fc567 -R 33c717c250039ffbe53dc8d7b2094929 +P f930b139d6db0ee799bc90397b225175103c4bf22923d1c9cbcd32509adc1738 +R 80113afdab5484e027b5180529a678b7 U drh -Z 96ad60bdf5cbde0ec2b901493a583f6e +Z dc859019c8db0487fd3e03bdb875d59d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index dce1119850..4bbeaca0db 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f930b139d6db0ee799bc90397b225175103c4bf22923d1c9cbcd32509adc1738 \ No newline at end of file +2dbb22c75e86f2e3ced38ac14b4943570d5c2f86cd5e37e875bf0c863be28836 \ No newline at end of file diff --git a/src/json.c b/src/json.c index d7d464442b..a4a3e65c92 100644 --- a/src/json.c +++ b/src/json.c @@ -1758,7 +1758,6 @@ static int jsonParse( int i; memset(pParse, 0, sizeof(*pParse)); if( zJson==0 ) return 1; -//printf("PARSE %s\n", zJson); pParse->zJson = zJson; pParse->bOwnsJson = bTakeJson; pParse->nJPRef = 1; @@ -1896,7 +1895,6 @@ static JsonParse *jsonParseCached( p->nErr = 0; p->useMod = 0; pMatch = p; -//printf("HIT %s at %d\n", zJson, iKey); }else if( pMatch==0 && p->zAlt!=0 @@ -1907,7 +1905,6 @@ static JsonParse *jsonParseCached( p->nErr = 0; p->useMod = 1; pMatch = p; -//printf("HIT %s at %d-alt\n", zJson, iKey); }else if( p->iHoldiHold; iMinKey = iKey; @@ -1928,7 +1925,6 @@ 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. */ -//printf("MISS %s\n", zJson); p = sqlite3_malloc64( sizeof(*p) ); if( p==0 ){ sqlite3_result_error_nomem(pCtx); @@ -2866,7 +2862,7 @@ static void jsonReplaceFunc( int argc, sqlite3_value **argv ){ - JsonParse x; /* The parse */ + JsonParse *pParse; /* The parse */ JsonNode *pNode; const char *zPath; u32 i; @@ -2876,20 +2872,20 @@ static void jsonReplaceFunc( jsonWrongNumArgs(ctx, "replace"); return; } - if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0]), 0) ) return; - assert( x.nNode ); + pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); + if( pParse==0 ) return; for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); - pNode = jsonLookup(&x, zPath, 0, ctx); - if( x.nErr ) goto replace_err; + pParse->useMod = 1; + pNode = jsonLookup(pParse, zPath, 0, ctx); + if( pParse->nErr ) goto replace_err; if( pNode ){ - jsonReplaceNode(ctx, &x, (u32)(pNode - x.aNode), argv[i+1]); + jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); } } - jsonReturnJson(&x, x.aNode, ctx, 0); + jsonReturnJson(pParse, pParse->aNode, ctx, 1); replace_err: - jsonDebugPrintParse(&x); - jsonParseReset(&x); + jsonDebugPrintParse(pParse); } @@ -2910,7 +2906,7 @@ static void jsonSetFunc( int argc, sqlite3_value **argv ){ - JsonParse x; /* The parse */ + JsonParse *pParse; /* The parse */ JsonNode *pNode; const char *zPath; u32 i; @@ -2922,27 +2918,27 @@ static void jsonSetFunc( jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); return; } - if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0]), 0) ) return; - assert( x.nNode ); + pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); + if( pParse==0 ) return; for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); bApnd = 0; - x.useMod = 1; - pNode = jsonLookup(&x, zPath, &bApnd, ctx); - if( x.oom ){ + pParse->useMod = 1; + pNode = jsonLookup(pParse, zPath, &bApnd, ctx); + if( pParse->oom ){ sqlite3_result_error_nomem(ctx); goto jsonSetDone; - }else if( x.nErr ){ + }else if( pParse->nErr ){ goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ - jsonReplaceNode(ctx, &x, (u32)(pNode - x.aNode), argv[i+1]); + jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); } } - jsonDebugPrintParse(&x); - jsonReturnJson(&x, x.aNode, ctx, 0); + jsonDebugPrintParse(pParse); + jsonReturnJson(pParse, pParse->aNode, ctx, 1); jsonSetDone: - jsonParseReset(&x); + /* no cleanup required */; } /* diff --git a/src/printf.c b/src/printf.c index a55bd2b85b..87ad91f795 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1394,34 +1394,13 @@ void sqlite3RCStrUnref(char *z){ assert( p!=0 ); p--; assert( p->nRCRef>0 ); - assert( p->uMagic==SQLITE_RCSTR_MAGIC ); if( p->nRCRef>=2 ){ p->nRCRef--; }else{ - if( p->xFree ) p->xFree(p->pAttach); -#ifdef SQLITE_DEBUG - p->uMagic = 0; -#endif sqlite3_free(p); } } -#if 0 -/* -** Return true if the reference count on the string is exactly one, meaning -** that the string can be modified. Return false if the reference count -** is greater than one. -*/ -int sqlite3RCStrIsWriteable(char *z){ - RCStr *p = (RCStr*)z; - assert( p!=0 ); - p--; - assert( p->nRCRef>0 ); - assert( p->uMagic==SQLITE_RCSTR_MAGIC ); - return p->nRCRef==1; -} -#endif - /* ** Create a new string that is capable of holding N bytes of text, not counting ** the zero byte at the end. The string is uninitialized. @@ -1435,32 +1414,9 @@ char *sqlite3RCStrNew(u64 N){ RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 ); if( p==0 ) return 0; p->nRCRef = 1; - p->xFree = 0; - p->pAttach = 0; -#ifdef SQLITE_DEBUG - p->uMagic = SQLITE_RCSTR_MAGIC; -#endif return (char*)&p[1]; } -#if 0 -/* -** Return the number of bytes allocated to the string. The value returned -** does not include the space for the zero-terminator at the end. -*/ -u64 sqlite3RCStrSize(char *z){ - RCStr *p = (RCStr*)z; - u64 N; - assert( p!=0 ); - p--; - assert( p->nRCRef>0 ); - assert( p->uMagic==SQLITE_RCSTR_MAGIC ); - N = sqlite3_msize(p); - N -= sizeof(p) + 1; - return N; -} -#endif - /* ** Change the size of the string so that it is able to hold N bytes. ** The string might be reallocated, so return the new allocation. @@ -1471,7 +1427,6 @@ char *sqlite3RCStrResize(char *z, u64 N){ assert( p!=0 ); p--; assert( p->nRCRef==1 ); - assert( p->uMagic==SQLITE_RCSTR_MAGIC ); pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1); if( pNew==0 ){ sqlite3_free(p); @@ -1480,40 +1435,3 @@ char *sqlite3RCStrResize(char *z, u64 N){ return (char*)&pNew[1]; } } - -#if 0 -/* -** Add a new attachment to the string. -** -** A string may have no more than one attachment. When a new attachment -** is added, any prior attachment is destroyed. Remove an attachment -** by adding a zero-attachment. -*/ -void sqlite3RCStrAttach(char *z, void *pAttach, void(*xFree)(void*)){ - RCStr *p = (RCStr*)z; - assert( p!=0 ); - p--; - assert( p->nRCRef>0 ); - assert( p->uMagic==SQLITE_RCSTR_MAGIC ); - if( p->xFree ) p->xFree(p->pAttach); - p->xFree = xFree; - p->pAttach = pAttach; -} -#endif - -#if 0 -/* -** Return the attachment associated with a string if the attachment -** has the destructure specified in the second argument. If the -** string has no attachment or if the destructor does not match, -** then return a NULL pointr. -*/ -void *sqlite3RCStrGetAttachment(char *z, void(*xFree)(void*)){ - RCStr *p = (RCStr*)z; - assert( p!=0 ); - p--; - assert( p->nRCRef>0 ); - assert( p->uMagic==SQLITE_RCSTR_MAGIC ); - return p->xFree==xFree ? p->pAttach : 0; -} -#endif diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 358063534f..a19a16d50d 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4066,44 +4066,22 @@ struct sqlite3_str { ** The following object is the header for an "RCStr" or "reference-counted ** string". An RCStr is passed around and used like any other char* ** that has been dynamically allocated. The important interface -** difference is that it uses sqlite3RCStrUnref() as its destructor -** rather than sqlite3_free(). Other than that the two are interchangeable. +** differences: ** -** Thus to return an RCStr object as the result of an SQL function use: -** -** sqlite3_result_text64(ctx,z,sz,sqlite3RCStrUnref,SQLITE_UTF8) -** ^^^^^^^^^^^^^^^^^ -** Instead of sqlite3_free() or similar +** 1. RCStr strings are reference counted. They are deallocated +** when the reference count reaches zero. ** -** An SQL function can check its arguments to see if they are RCStr -** strings using the sqlite3ValueIsOfClass() function: +** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than +** sqlite3_free() ** -** sqlite3ValueIsOfClass(argv[i], sqlite3RCStrUnref); -** -** An RCStr string might be better than an ordinary string in some cases -** because: -** -** (1) You can duplicate it using sqlite3RCStrRef(x). -** -** (2) You can also add an associated object to the string. For -** example, if the string is JSON, perhaps the associated object -** is a parse of that JSON. -** -** Methods for an RCStr string begin with "sqlite3RCStr...". +** 3. Make a (read-only) copy of a read-only RCStr string using +** sqlite3RCStrRef(). */ struct RCStr { - u32 nRCRef; /* Number of references */ -#ifdef SQLITE_DEBUG - u32 uMagic; /* Magic number for sanity checking */ -#endif - void *pAttach; /* Attachment to this string */ - void (*xFree)(void*); /* Destructor for the attachment */ + u64 nRCRef; /* Number of references */ + /* Total structure size should be a multiple of 8 bytes for alignment */ }; -/* The Magic number used by RCStr for sanity checking. SQLITE_DEBUG only. */ -#define SQLITE_RCSTR_MAGIC 0x3dc05d54 - - /* ** A pointer to this structure is used to communicate information ** from sqlite3Init and OP_ParseSchema into the sqlite3InitCallback. @@ -5222,7 +5200,6 @@ void sqlite3FileSuffix3(const char*, char*); u8 sqlite3GetBoolean(const char *z,u8); const void *sqlite3ValueText(sqlite3_value*, u8); -//int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*)); int sqlite3ValueBytes(sqlite3_value*, u8); void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); @@ -5333,11 +5310,7 @@ int sqlite3OpenTempDatabase(Parse *); char *sqlite3RCStrRef(char*); void sqlite3RCStrUnref(char*); char *sqlite3RCStrNew(u64); -//u64 sqlite3RCStrSize(char*); char *sqlite3RCStrResize(char*,u64); -//int sqlite3RCStrIsWriteable(char*); -//void sqlite3RCStrAttach(char*, void*, void(*)(void*)); -//void *sqlite3RCStrGetAttachment(char*,void(*)(void*)); void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); int sqlite3StrAccumEnlarge(StrAccum*, i64); diff --git a/src/vdbemem.c b/src/vdbemem.c index d44a41716f..c73e59c362 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1368,26 +1368,6 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ return valueToText(pVal, enc); } -#if 0 -/* Return true if sqlit3_value object pVal is a string or blob value -** that uses the destructor specified in the second argument. -** -** TODO: Maybe someday promote this interface into a published API so -** that third-party extensions can get access to it? -*/ -int sqlite3ValueIsOfClass(const sqlite3_value *pVal, void(*xFree)(void*)){ - if( ALWAYS(pVal!=0) - && (pVal->flags & (MEM_Str|MEM_Blob))!=0 - && (pVal->flags & MEM_Dyn)!=0 - && pVal->xDel==xFree - ){ - return 1; - }else{ - return 0; - } -} -#endif - /* ** Create a new sqlite3_value object. */