From 3574ec40a6273ce2057e5242e1929ef9cfb663dd Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 25 Sep 2025 23:32:23 +0000 Subject: [PATCH 1/3] Add JSON table-valued functions jsonb_each() and jsonb_tree(). FossilOrigin-Name: 2f8b461c636ce2c0e7970c00d1e6ea62cdef72273150fea6aabe4e9292724977 --- manifest | 17 ++++++---- manifest.tags | 4 +-- manifest.uuid | 2 +- src/json.c | 92 ++++++++++++++++++++------------------------------- 4 files changed, 48 insertions(+), 67 deletions(-) diff --git a/manifest b/manifest index 01280c386f..315b8ddc82 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm\sdist:\sadd\s--snapshot\sflag,\sadd\s-64\sflag\sto\sinclude\s64-bit\sbuilds,\sand\sadd\smodule-symbols.html\sto\sthe\sdist. -D 2025-09-25T18:53:28.163 +C Add\sJSON\stable-valued\sfunctions\sjsonb_each()\sand\sjsonb_tree(). +D 2025-09-25T23:32:23.992 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -698,7 +698,7 @@ F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd -F src/json.c eb5aa0f1fd049058352250b81810c9ab63ab8d57264e19c86c3060b4ea076511 +F src/json.c 5338c81c3d0a1472c10a2bca2bc8c89ace613d1ce6b76ef0ef5905dfbc762187 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 3326993a09553c6b38cc52d4f9cc2e47dcfc8736ffd853fcb0cb49bc9e3d523c F src/main.c 4315e426f5898d994f47a5a17169b48120cd440307ab6d07850ff3d1eabf3726 @@ -2170,8 +2170,11 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P ab6eb4d3b723107da488770b1849b9f1d1c491348f1eaa544e0e07356b8fac7d -R 5604f99b6885eee7b3387abf567432cd -U stephan -Z 54eb9cf8d86f8180d0b259e57237e3ca +P e9a6391b377b6964620285192262a4dc9fe1712799f7aa8a8b37c5f718544ed2 +R 124e90bed91fdad6bd23f672ef76cd75 +T *branch * jsonb_each +T *sym-jsonb_each * +T -sym-trunk * +U drh +Z 64aeb0af1a54113541748e99c409d44e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.tags b/manifest.tags index bec971799f..ba78dcfe1b 100644 --- a/manifest.tags +++ b/manifest.tags @@ -1,2 +1,2 @@ -branch trunk -tag trunk +branch jsonb_each +tag jsonb_each diff --git a/manifest.uuid b/manifest.uuid index 2683a199a5..9d1b40265d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e9a6391b377b6964620285192262a4dc9fe1712799f7aa8a8b37c5f718544ed2 +2f8b461c636ce2c0e7970c00d1e6ea62cdef72273150fea6aabe4e9292724977 diff --git a/src/json.c b/src/json.c index 3df33a1109..267aaac93c 100644 --- a/src/json.c +++ b/src/json.c @@ -3153,19 +3153,27 @@ static void jsonReturnTextJsonFromBlob( ** ** If the value is a primitive, return it as an SQL value. ** If the value is an array or object, return it as either -** JSON text or the BLOB encoding, depending on the JSON_B flag -** on the userdata. +** JSON text or the BLOB encoding, depending on the eMode flag +** as follows: +** +** eMode==0 JSONB if the JSON_B flag is set in userdata or +** text if the JSON_B flag is omitted from userdata. +** +** eMode==1 Text +** +** eMode==2 JSONB */ static void jsonReturnFromBlob( JsonParse *pParse, /* Complete JSON parse tree */ u32 i, /* Index of the node */ sqlite3_context *pCtx, /* Return value for this function */ - int textOnly /* return text JSON. Disregard user-data */ + int eMode /* Format of return: text of JSONB */ ){ u32 n, sz; int rc; sqlite3 *db = sqlite3_context_db_handle(pCtx); + assert( eMode>=0 && eMode<=2 ); n = jsonbPayloadSize(pParse, i, &sz); if( n==0 ){ sqlite3_result_error(pCtx, "malformed JSON", -1); @@ -3284,8 +3292,14 @@ static void jsonReturnFromBlob( } case JSONB_ARRAY: case JSONB_OBJECT: { - int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); - if( flags & JSON_BLOB ){ + if( eMode==0 ){ + if( (SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)) & JSON_BLOB)!=0 ){ + eMode = 2; + }else{ + eMode = 1; + } + } + if( eMode==2 ){ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT); }else{ jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n); @@ -4932,6 +4946,7 @@ struct JsonEachCursor { u32 nRoot; /* Size of the root path in bytes */ u8 eType; /* Type of the container for element i */ u8 bRecursive; /* True for json_tree(). False for json_each() */ + u8 eMode; /* 1 for json_each(). 2 for jsonb_each() */ u32 nParent; /* Current nesting depth */ u32 nParentAlloc; /* Space allocated for aParent[] */ JsonParent *aParent; /* Parent elements of i */ @@ -4943,6 +4958,8 @@ typedef struct JsonEachConnection JsonEachConnection; struct JsonEachConnection { sqlite3_vtab base; /* Base class - must be first */ sqlite3 *db; /* Database connection */ + u8 eMode; /* 1 for json_each(). 2 for jsonb_each() */ + u8 bRecursive; /* True for json_tree(). False for json_each() */ }; @@ -4985,6 +5002,8 @@ static int jsonEachConnect( if( pNew==0 ) return SQLITE_NOMEM; sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); pNew->db = db; + pNew->eMode = argv[0][4]=='b' ? 2 : 1; + pNew->bRecursive = argv[0][4+pNew->eMode]=='t'; } return rc; } @@ -4996,8 +5015,8 @@ static int jsonEachDisconnect(sqlite3_vtab *pVtab){ return SQLITE_OK; } -/* constructor for a JsonEachCursor object for json_each(). */ -static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ +/* constructor for a JsonEachCursor object for json_each()/json_tree(). */ +static int jsonEachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ JsonEachConnection *pVtab = (JsonEachConnection*)p; JsonEachCursor *pCur; @@ -5005,21 +5024,13 @@ static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur)); if( pCur==0 ) return SQLITE_NOMEM; pCur->db = pVtab->db; + pCur->eMode = pVtab->eMode; + pCur->bRecursive = pVtab->bRecursive; jsonStringZero(&pCur->path); *ppCursor = &pCur->base; return SQLITE_OK; } -/* constructor for a JsonEachCursor object for json_tree(). */ -static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ - int rc = jsonEachOpenEach(p, ppCursor); - if( rc==SQLITE_OK ){ - JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor; - pCur->bRecursive = 1; - } - return rc; -} - /* Reset a JsonEachCursor back to its original state. Free any memory ** held. */ static void jsonEachCursorReset(JsonEachCursor *p){ @@ -5224,7 +5235,7 @@ static int jsonEachColumn( } case JEACH_VALUE: { u32 i = jsonSkipLabel(p); - jsonReturnFromBlob(&p->sParse, i, ctx, 1); + jsonReturnFromBlob(&p->sParse, i, ctx, p->eMode); if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){ sqlite3_result_subtype(ctx, JSON_SUBTYPE); } @@ -5468,36 +5479,7 @@ static sqlite3_module jsonEachModule = { jsonEachBestIndex, /* xBestIndex */ jsonEachDisconnect, /* xDisconnect */ 0, /* xDestroy */ - jsonEachOpenEach, /* xOpen - open a cursor */ - jsonEachClose, /* xClose - close a cursor */ - jsonEachFilter, /* xFilter - configure scan constraints */ - jsonEachNext, /* xNext - advance a cursor */ - jsonEachEof, /* xEof - check for end of scan */ - jsonEachColumn, /* xColumn - read data */ - jsonEachRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0, /* xShadowName */ - 0 /* xIntegrity */ -}; - -/* The methods of the json_tree virtual table. */ -static sqlite3_module jsonTreeModule = { - 0, /* iVersion */ - 0, /* xCreate */ - jsonEachConnect, /* xConnect */ - jsonEachBestIndex, /* xBestIndex */ - jsonEachDisconnect, /* xDisconnect */ - 0, /* xDestroy */ - jsonEachOpenTree, /* xOpen - open a cursor */ + jsonEachOpen, /* xOpen - open a cursor */ jsonEachClose, /* xClose - close a cursor */ jsonEachFilter, /* xFilter - configure scan constraints */ jsonEachNext, /* xNext - advance a cursor */ @@ -5590,16 +5572,12 @@ void sqlite3RegisterJsonFunctions(void){ */ int sqlite3JsonTableFunctions(sqlite3 *db){ int rc = SQLITE_OK; - static const struct { - const char *zName; - sqlite3_module *pModule; - } aMod[] = { - { "json_each", &jsonEachModule }, - { "json_tree", &jsonTreeModule }, + int i; + static const char *azModule[] = { + "json_each", "json_tree", "jsonb_each", "jsonb_tree" }; - unsigned int i; - for(i=0; i Date: Fri, 26 Sep 2025 10:29:07 +0000 Subject: [PATCH 2/3] On-demand loading of JSON virtual tables. FossilOrigin-Name: 118f97ddcd8f888715825a623c02d42b92a82737a316f7bee4cd66f570945346 --- manifest | 21 +++++++++------------ manifest.uuid | 2 +- src/build.c | 5 +++++ src/json.c | 15 +++++++++------ src/main.c | 3 --- src/sqliteInt.h | 2 +- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/manifest b/manifest index 315b8ddc82..ebb44acedd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sJSON\stable-valued\sfunctions\sjsonb_each()\sand\sjsonb_tree(). -D 2025-09-25T23:32:23.992 +C On-demand\sloading\sof\sJSON\svirtual\stables. +D 2025-09-26T10:29:07.156 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -681,7 +681,7 @@ F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea F src/btree.c cb5b8ceb9baa02a63a2f83dec09c4153e1cfbdf9c2adef5c62c26d2160eeb067 F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0 F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886 -F src/build.c 1ba7a4f00f0e7281131eb0a83218b2a6a952840303e48ac8059ae1a6271c7ab2 +F src/build.c f3c5f5b6b4b6654d3680c15b31a1a2ba4fdf0c3b1e8bea10cb20233c5423972f F src/callback.c acae8c8dddda41ee85cfdf19b926eefe830f371069f8aadca3aa39adf5b1c859 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/date.c b6f92001f4b1f73f21774927488661d28f4dac9cd9701ed96486d96b44f5b058 @@ -698,10 +698,10 @@ F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd -F src/json.c 5338c81c3d0a1472c10a2bca2bc8c89ace613d1ce6b76ef0ef5905dfbc762187 +F src/json.c 9f20fe0c5340e576607036ad2ffa95981300294210dfb1356d7f1ad47bec4d1e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 3326993a09553c6b38cc52d4f9cc2e47dcfc8736ffd853fcb0cb49bc9e3d523c -F src/main.c 4315e426f5898d994f47a5a17169b48120cd440307ab6d07850ff3d1eabf3726 +F src/main.c ce69a2650e3d359ed6a8a2867ccafb27ac62ce1d39f3120a84ff513320952a6c F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 @@ -742,7 +742,7 @@ F src/shell.c.in 175630658a5fce0277cddf4991c56931ed061b3af36061be3e56ef113588452 F src/sqlite.h.in 5732519a2acb09066032ceac21f25996eb3f28f807a4468e30633c7c70faae1c F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 3f0c4ed6934e7309a61c6f3c30f70a30a5b869f785bb3d9f721a36c5e4359126 -F src/sqliteInt.h 01ab337beb08582828b8db542034eaf832778aa670ae6ade7e70f1d998ebdf17 +F src/sqliteInt.h 673c7c5d7e77552452b21499717233329809f252458f77a798977737f51b31b8 F src/sqliteLimit.h fe70bd8983e5d317a264f2ea97473b359faf3ebb0827877a76813f5cf0cdc364 F src/status.c 0e72e4f6be6ccfde2488eb63210297e75f569f3ce9920f6c3d77590ec6ce5ffd F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -2170,11 +2170,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P e9a6391b377b6964620285192262a4dc9fe1712799f7aa8a8b37c5f718544ed2 -R 124e90bed91fdad6bd23f672ef76cd75 -T *branch * jsonb_each -T *sym-jsonb_each * -T -sym-trunk * +P 2f8b461c636ce2c0e7970c00d1e6ea62cdef72273150fea6aabe4e9292724977 +R 4c4b4754e5e560616cf33dd688d0812a U drh -Z 64aeb0af1a54113541748e99c409d44e +Z a66b270f99e73bb4ae16db5d49fcd519 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9d1b40265d..94fcaadf66 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2f8b461c636ce2c0e7970c00d1e6ea62cdef72273150fea6aabe4e9292724977 +118f97ddcd8f888715825a623c02d42b92a82737a316f7bee4cd66f570945346 diff --git a/src/build.c b/src/build.c index 13bb3014d7..e130ce9027 100644 --- a/src/build.c +++ b/src/build.c @@ -433,6 +433,11 @@ Table *sqlite3LocateTable( if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ pMod = sqlite3PragmaVtabRegister(db, zName); } +#ifndef SQLITE_OMIT_JSON + if( pMod==0 && sqlite3_strnicmp(zName, "json", 4)==0 ){ + pMod = sqlite3JsonVtabRegister(db, zName); + } +#endif if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ testcase( pMod->pEpoTab==0 ); return pMod->pEpoTab; diff --git a/src/json.c b/src/json.c index 267aaac93c..2393518e19 100644 --- a/src/json.c +++ b/src/json.c @@ -5568,17 +5568,20 @@ void sqlite3RegisterJsonFunctions(void){ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) /* -** Register the JSON table-valued functions +** Register the JSON table-valued function named zName and return a +** pointer to its Module object. Return NULL if something goes wrong. */ -int sqlite3JsonTableFunctions(sqlite3 *db){ - int rc = SQLITE_OK; +Module *sqlite3JsonVtabRegister(sqlite3 *db, const char *zName){ int i; static const char *azModule[] = { "json_each", "json_tree", "jsonb_each", "jsonb_tree" }; - for(i=0; iaModule, zName)==0 ); + for(i=0; i Date: Fri, 26 Sep 2025 11:28:01 +0000 Subject: [PATCH 3/3] Fix a harmless compiler warning. FossilOrigin-Name: c50a3c45a20e5b9d48e749818bea06dfa99b729535e0617347c6ece1d277a447 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index ebb44acedd..5c09156b97 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C On-demand\sloading\sof\sJSON\svirtual\stables. -D 2025-09-26T10:29:07.156 +C Fix\sa\sharmless\scompiler\swarning. +D 2025-09-26T11:28:01.506 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -698,7 +698,7 @@ F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd -F src/json.c 9f20fe0c5340e576607036ad2ffa95981300294210dfb1356d7f1ad47bec4d1e +F src/json.c 0f11267c9dbe83e4cdca56d213f021b0e9516d31317e485e3508687cfb65bf36 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 3326993a09553c6b38cc52d4f9cc2e47dcfc8736ffd853fcb0cb49bc9e3d523c F src/main.c ce69a2650e3d359ed6a8a2867ccafb27ac62ce1d39f3120a84ff513320952a6c @@ -2170,8 +2170,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 2f8b461c636ce2c0e7970c00d1e6ea62cdef72273150fea6aabe4e9292724977 -R 4c4b4754e5e560616cf33dd688d0812a +P 118f97ddcd8f888715825a623c02d42b92a82737a316f7bee4cd66f570945346 +R e1531d0f131fb3867141bb1e24c14f34 U drh -Z a66b270f99e73bb4ae16db5d49fcd519 +Z 93dadbf666221c7bdbecb74355913b33 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 94fcaadf66..d7e11a0c63 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -118f97ddcd8f888715825a623c02d42b92a82737a316f7bee4cd66f570945346 +c50a3c45a20e5b9d48e749818bea06dfa99b729535e0617347c6ece1d277a447 diff --git a/src/json.c b/src/json.c index 2393518e19..db65a3fe4d 100644 --- a/src/json.c +++ b/src/json.c @@ -5572,7 +5572,7 @@ void sqlite3RegisterJsonFunctions(void){ ** pointer to its Module object. Return NULL if something goes wrong. */ Module *sqlite3JsonVtabRegister(sqlite3 *db, const char *zName){ - int i; + unsigned int i; static const char *azModule[] = { "json_each", "json_tree", "jsonb_each", "jsonb_tree" };