From 505ad2ca41b1b7026ab74680d11179824d69c653 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 21 Aug 2015 17:33:11 +0000 Subject: [PATCH] Add the json_tree() virtual table. FossilOrigin-Name: 08c36e45f0d3a7b89caf823652d7543b76ac802a --- ext/misc/json.c | 350 +++++++++++++++++++++++++++++++++++------------- manifest | 12 +- manifest.uuid | 2 +- 3 files changed, 264 insertions(+), 100 deletions(-) diff --git a/ext/misc/json.c b/ext/misc/json.c index 77bc6d6794..26cde61056 100644 --- a/ext/misc/json.c +++ b/ext/misc/json.c @@ -34,7 +34,7 @@ typedef unsigned int u32; typedef unsigned char u8; /* Objects */ -typedef struct Json Json; +typedef struct JsonString JsonString; typedef struct JsonNode JsonNode; typedef struct JsonParse JsonParse; @@ -42,7 +42,7 @@ typedef struct JsonParse JsonParse; ** under construction. Really, this is a generic string accumulator ** that can be and is used to create strings other than JSON. */ -struct Json { +struct JsonString { sqlite3_context *pCtx; /* Function context - put error messages here */ char *zBuf; /* Append JSON content here */ u64 nAlloc; /* Bytes of storage available in zBuf[] */ @@ -89,6 +89,7 @@ struct JsonNode { union { const char *zJContent; /* Content for INT, REAL, and STRING */ u32 iAppend; /* More terms for ARRAY and OBJECT */ + u32 iKey; /* Key for ARRAY objects in json_tree() */ } u; }; @@ -99,44 +100,36 @@ struct JsonParse { u32 nAlloc; /* Number of slots of aNode[] allocated */ JsonNode *aNode; /* Array of nodes containing the parse */ const char *zJson; /* Original JSON string */ + u32 *aUp; /* Index of parent of each node */ u8 oom; /* Set to true if out of memory */ }; -/* -** Return the number of consecutive JsonNode slots need to represent -** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and -** OBJECT types, the number might be larger. -** -** Appended elements are not counted. The value returned is the number -** by which the JsonNode counter should increment in order to go to the -** next peer value. -*/ -static u32 jsonSize(JsonNode *pNode){ - return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1; -} +/************************************************************************** +** Utility routines for dealing with JsonString objects +**************************************************************************/ -/* Set the Json object to an empty string +/* Set the JsonString object to an empty string */ -static void jsonZero(Json *p){ +static void jsonZero(JsonString *p){ p->zBuf = p->zSpace; p->nAlloc = sizeof(p->zSpace); p->nUsed = 0; p->bStatic = 1; } -/* Initialize the Json object +/* Initialize the JsonString object */ -static void jsonInit(Json *p, sqlite3_context *pCtx){ +static void jsonInit(JsonString *p, sqlite3_context *pCtx){ p->pCtx = pCtx; p->bErr = 0; jsonZero(p); } -/* Free all allocated memory and reset the Json object back to its +/* Free all allocated memory and reset the JsonString object back to its ** initial state. */ -static void jsonReset(Json *p){ +static void jsonReset(JsonString *p){ if( !p->bStatic ) sqlite3_free(p->zBuf); jsonZero(p); } @@ -144,7 +137,7 @@ static void jsonReset(Json *p){ /* Report an out-of-memory (OOM) condition */ -static void jsonOom(Json *p){ +static void jsonOom(JsonString *p){ if( !p->bErr ){ p->bErr = 1; sqlite3_result_error_nomem(p->pCtx); @@ -155,7 +148,7 @@ static void jsonOom(Json *p){ /* Enlarge pJson->zBuf so that it can hold at least N more bytes. ** Return zero on success. Return non-zero on an OOM error */ -static int jsonGrow(Json *p, u32 N){ +static int jsonGrow(JsonString *p, u32 N){ u64 nTotal = NnAlloc ? p->nAlloc*2 : p->nAlloc+N+10; char *zNew; if( p->bStatic ){ @@ -180,9 +173,9 @@ static int jsonGrow(Json *p, u32 N){ return SQLITE_OK; } -/* Append N bytes from zIn onto the end of the Json string. +/* Append N bytes from zIn onto the end of the JsonString string. */ -static void jsonAppendRaw(Json *p, const char *zIn, u32 N){ +static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return; memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; @@ -191,14 +184,14 @@ static void jsonAppendRaw(Json *p, const char *zIn, u32 N){ #ifdef SQLITE_DEBUG /* Append the zero-terminated string zIn */ -static void jsonAppend(Json *p, const char *zIn){ +static void jsonAppend(JsonString *p, const char *zIn){ jsonAppendRaw(p, zIn, (u32)strlen(zIn)); } #endif /* Append a single character */ -static void jsonAppendChar(Json *p, char c){ +static void jsonAppendChar(JsonString *p, char c){ if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return; p->zBuf[p->nUsed++] = c; } @@ -206,19 +199,19 @@ static void jsonAppendChar(Json *p, char c){ /* Append a comma separator to the output buffer, if the previous ** character is not '[' or '{'. */ -static void jsonAppendSeparator(Json *p){ +static void jsonAppendSeparator(JsonString *p){ char c; if( p->nUsed==0 ) return; c = p->zBuf[p->nUsed-1]; if( c!='[' && c!='{' ) jsonAppendChar(p, ','); } -/* Append the N-byte string in zIn to the end of the Json string +/* Append the N-byte string in zIn to the end of the JsonString string ** under construction. Enclose the string in "..." and escape ** any double-quotes or backslash characters contained within the ** string. */ -static void jsonAppendString(Json *p, const char *zIn, u32 N){ +static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ u32 i; if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return; p->zBuf[p->nUsed++] = '"'; @@ -238,7 +231,7 @@ static void jsonAppendString(Json *p, const char *zIn, u32 N){ ** construction. */ static void jsonAppendValue( - Json *p, /* Append to this JSON string */ + JsonString *p, /* Append to this JSON string */ sqlite3_value *pValue /* Value to append */ ){ switch( sqlite3_value_type(pValue) ){ @@ -273,7 +266,7 @@ static void jsonAppendValue( /* Make the JSON in p the result of the SQL function. */ -static void jsonResult(Json *p){ +static void jsonResult(JsonString *p){ if( p->bErr==0 ){ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, p->bStatic ? SQLITE_TRANSIENT : sqlite3_free, @@ -283,6 +276,36 @@ static void jsonResult(Json *p){ assert( p->bStatic ); } +/************************************************************************** +** Utility routines for dealing with JsonNode and JsonParse objects +**************************************************************************/ + +/* +** Return the number of consecutive JsonNode slots need to represent +** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and +** OBJECT types, the number might be larger. +** +** Appended elements are not counted. The value returned is the number +** by which the JsonNode counter should increment in order to go to the +** next peer value. +*/ +static u32 jsonNodeSize(JsonNode *pNode){ + return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1; +} + +/* +** Reclaim all memory allocated by a JsonParse object. But do not +** delete the JsonParse object itself. +*/ +static void jsonParseReset(JsonParse *pParse){ + sqlite3_free(pParse->aNode); + pParse->aNode = 0; + pParse->nNode = 0; + pParse->nAlloc = 0; + sqlite3_free(pParse->aUp); + pParse->aUp = 0; +} + /* ** Convert the JsonNode pNode into a pure JSON string and ** append to pOut. Subsubstructure is also included. Return @@ -290,7 +313,7 @@ static void jsonResult(Json *p){ */ static void jsonRenderNode( JsonNode *pNode, /* The node to render */ - Json *pOut, /* Write JSON here */ + JsonString *pOut, /* Write JSON here */ sqlite3_value **aReplace /* Replacement values */ ){ switch( pNode->eType ){ @@ -332,7 +355,7 @@ static void jsonRenderNode( jsonAppendSeparator(pOut); jsonRenderNode(&pNode[j], pOut, aReplace); } - j += jsonSize(&pNode[j]); + j += jsonNodeSize(&pNode[j]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; pNode = &pNode[pNode->u.iAppend]; @@ -356,7 +379,7 @@ static void jsonRenderNode( jsonRenderNode(&pNode[j+1], pOut, aReplace); } } - j += 1 + jsonSize(&pNode[j+1]); + j += 1 + jsonNodeSize(&pNode[j+1]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; pNode = &pNode[pNode->u.iAppend]; @@ -478,7 +501,7 @@ static void jsonReturn( } case JSON_ARRAY: case JSON_OBJECT: { - Json s; + JsonString s; jsonInit(&s, pCtx); jsonRenderNode(pNode, &s, aReplace); jsonResult(&s); @@ -676,15 +699,51 @@ static int jsonParse(JsonParse *pParse, const char *zJson){ if( zJson[i] ) i = -1; } if( i<0 ){ - sqlite3_free(pParse->aNode); - pParse->aNode = 0; - pParse->nNode = 0; - pParse->nAlloc = 0; + jsonParseReset(pParse); return 1; } return 0; } +/* Mark node i of pParse as being a child of iParent. Call recursively +** to fill in all the descendants of node i. +*/ +static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ + JsonNode *pNode = &pParse->aNode[i]; + u32 j; + pParse->aUp[i] = iParent; + switch( pNode->eType ){ + case JSON_ARRAY: { + for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){ + jsonParseFillInParentage(pParse, i+j, i); + } + break; + } + case JSON_OBJECT: { + for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){ + pParse->aUp[i+j] = i; + jsonParseFillInParentage(pParse, i+j+1, i); + } + break; + } + default: { + break; + } + } +} + +/* +** Compute the parentage of all nodes in a completed parse. +*/ +static int jsonParseFindParents(JsonParse *pParse){ + u32 *aUp; + assert( pParse->aUp==0 ); + aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode ); + if( aUp==0 ) return SQLITE_NOMEM; + jsonParseFillInParentage(pParse, 0, 0); + return SQLITE_OK; +} + /* forward declaration */ static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*); @@ -730,7 +789,7 @@ static JsonNode *jsonLookup( return jsonLookup(pParse, iRoot+j+1, &zPath[i], pApnd); } j++; - j += jsonSize(&pRoot[j]); + j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; iRoot += pRoot->u.iAppend; @@ -759,7 +818,7 @@ static JsonNode *jsonLookup( j = 1; for(;;){ while( i>0 && j<=pRoot->n ){ - j += jsonSize(&pRoot[j]); + j += jsonNodeSize(&pRoot[j]); i--; } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; @@ -820,8 +879,8 @@ static void jsonParseFunc( int argc, sqlite3_value **argv ){ - Json s; /* Output string - not real JSON */ - JsonParse x; /* The parse */ + JsonString s; /* Output string - not real JSON */ + JsonParse x; /* The parse */ u32 i; char zBuf[100]; @@ -838,7 +897,7 @@ static void jsonParseFunc( jsonAppendRaw(&s, "\n", 1); } } - sqlite3_free(x.aNode); + jsonParseReset(&x); jsonResult(&s); } @@ -853,7 +912,7 @@ static void jsonTest1Func( JsonParse x; /* The parse */ if( jsonParse(&x, (const char*)sqlite3_value_text(argv[0])) ) return; jsonReturn(x.aNode, context, 0); - sqlite3_free(x.aNode); + jsonParseReset(&x); } /* @@ -868,7 +927,7 @@ static void jsonNodeCountFunc( JsonParse x; /* The parse */ if( jsonParse(&x, (const char*)sqlite3_value_text(argv[0])) ) return; sqlite3_result_int64(context, x.nNode); - sqlite3_free(x.aNode); + jsonParseReset(&x); } #endif /* SQLITE_DEBUG */ @@ -887,7 +946,7 @@ static void jsonArrayFunc( sqlite3_value **argv ){ int i; - Json jx; + JsonString jx; jsonInit(&jx, context); jsonAppendChar(&jx, '['); @@ -932,11 +991,11 @@ static void jsonArrayLengthFunc( if( pNode->eType==JSON_ARRAY ){ assert( (pNode->jnFlags & JNODE_APPEND)==0 ); for(i=1; i<=pNode->n; n++){ - i += jsonSize(&pNode[i]); + i += jsonNodeSize(&pNode[i]); } } } - sqlite3_free(x.aNode); + jsonParseReset(&x); } sqlite3_result_int64(context, n); } @@ -965,7 +1024,7 @@ static void jsonExtractFunc( if( pNode ){ jsonReturn(pNode, context, 0); } - sqlite3_free(x.aNode); + jsonParseReset(&x); } /* @@ -979,7 +1038,7 @@ static void jsonObjectFunc( sqlite3_value **argv ){ int i; - Json jx; + JsonString jx; const char *z; u32 n; @@ -1039,7 +1098,7 @@ static void jsonRemoveFunc( jsonReturn(x.aNode, context, 0); } } - sqlite3_free(x.aNode); + jsonParseReset(&x); } /* @@ -1082,8 +1141,9 @@ static void jsonReplaceFunc( jsonReturn(x.aNode, context, argv); } } - sqlite3_free(x.aNode); + jsonParseReset(&x); } + /* ** json_set(JSON, PATH, VALUE, ...) ** @@ -1133,7 +1193,7 @@ static void jsonSetFunc( jsonReturn(x.aNode, context, argv); } } - sqlite3_free(x.aNode); + jsonParseReset(&x); } /* @@ -1165,7 +1225,7 @@ static void jsonTypeFunc( if( zPath ) pNode = jsonLookup(&x, 0, zPath, 0); sqlite3_result_text(context, jsonType[pNode->eType], -1, SQLITE_STATIC); } - sqlite3_free(x.aNode); + jsonParseReset(&x); } /**************************************************************************** @@ -1174,13 +1234,14 @@ static void jsonTypeFunc( typedef struct JsonEachCursor JsonEachCursor; struct JsonEachCursor { sqlite3_vtab_cursor base; /* Base class - must be first */ - u32 iRowid; /* The rowid */ - u32 i; /* Index in sParse.aNode[] of current row */ - u32 iEnd; /* EOF when i equals or exceeds this value */ - u8 eType; /* Type of top-level element */ - char *zJson; /* Input json */ - char *zPath; /* Path by which to filter zJson */ - JsonParse sParse; /* The input json */ + u32 iRowid; /* The rowid */ + u32 i; /* Index in sParse.aNode[] of current row */ + u32 iEnd; /* EOF when i equals or exceeds this value */ + u8 eType; /* Type of top-level element */ + u8 bRecursive; /* True for json_tree(). False for json_each() */ + char *zJson; /* Input JSON */ + char *zPath; /* Path by which to filter zJson */ + JsonParse sParse; /* Parse of the input JSON */ }; /* Constructor for the json_each virtual table */ @@ -1192,18 +1253,26 @@ static int jsonEachConnect( char **pzErr ){ sqlite3_vtab *pNew; - pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); - if( pNew==0 ) return SQLITE_NOMEM; + int rc; /* Column numbers */ #define JEACH_KEY 0 #define JEACH_VALUE 1 -#define JEACH_JSON 2 -#define JEACH_PATH 3 - - sqlite3_declare_vtab(db, "CREATE TABLE x(key,value,json hidden,path hidden)"); - memset(pNew, 0, sizeof(*pNew)); - return SQLITE_OK; +#define JEACH_TYPE 2 +#define JEACH_ATOM 3 +#define JEACH_ID 4 +#define JEACH_PARENT 5 +#define JEACH_JSON 6 +#define JEACH_PATH 7 + + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(key,value,type,atom,id,parent,json hidden,path hidden)"); + if( rc==SQLITE_OK ){ + pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + } + return rc; } /* destructor for json_each virtual table */ @@ -1212,8 +1281,8 @@ static int jsonEachDisconnect(sqlite3_vtab *pVtab){ return SQLITE_OK; } -/* constructor for a JsonEachCursor object. */ -static int jsonEachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ +/* constructor for a JsonEachCursor object for json_each(). */ +static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ JsonEachCursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; @@ -1222,17 +1291,26 @@ static int jsonEachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ 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){ sqlite3_free(p->zJson); sqlite3_free(p->zPath); - sqlite3_free(p->sParse.aNode); + jsonParseReset(&p->sParse); p->iRowid = 0; p->i = 0; p->iEnd = 0; p->eType = 0; - memset(&p->sParse, 0, sizeof(p->sParse)); p->zJson = 0; p->zPath = 0; } @@ -1252,18 +1330,39 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){ return p->i >= p->iEnd; } -/* Advance the cursor to the next top-level element of the current -** JSON string */ -static int jsonEachNext(sqlite3_vtab_cursor *cur){ +/* Advance the cursor to the next element for json_tree() */ +static int jsonEachNextTree(sqlite3_vtab_cursor *cur){ + JsonEachCursor *p = (JsonEachCursor*)cur; + if( p->i==0 ){ + p->i = 1; + }else if( p->sParse.aNode[p->sParse.aUp[p->i]].eType==JSON_OBJECT ){ + p->i += 2; + }else{ + p->i++; + } + p->iRowid++; + if( p->isParse.nNode ){ + JsonNode *pUp = &p->sParse.aNode[p->sParse.aUp[p->i]]; + p->eType = pUp->eType; + if( pUp->eType==JSON_ARRAY ) pUp->u.iKey++; + if( p->sParse.aNode[p->i].eType==JSON_ARRAY ){ + p->sParse.aNode[p->i].u.iKey = 0; + } + } + return SQLITE_OK; +} + +/* Advance the cursor to the next element for json_each() */ +static int jsonEachNextEach(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; switch( p->eType ){ case JSON_ARRAY: { - p->i += jsonSize(&p->sParse.aNode[p->i]); + p->i += jsonNodeSize(&p->sParse.aNode[p->i]); p->iRowid++; break; } case JSON_OBJECT: { - p->i += 1 + jsonSize(&p->sParse.aNode[p->i+1]); + p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]); p->iRowid++; break; } @@ -1282,20 +1381,46 @@ static int jsonEachColumn( int i /* Which column to return */ ){ JsonEachCursor *p = (JsonEachCursor*)cur; + JsonNode *pThis = &p->sParse.aNode[p->i]; switch( i ){ case JEACH_KEY: { if( p->eType==JSON_OBJECT ){ - jsonReturn(&p->sParse.aNode[p->i], ctx, 0); - }else{ - sqlite3_result_int64(ctx, p->iRowid); + jsonReturn(pThis, ctx, 0); + }else if( p->eType==JSON_ARRAY ){ + u32 iKey; + if( p->bRecursive ){ + if( p->iRowid==0 ) break; + iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey - 1; + }else{ + iKey = p->iRowid; + } + sqlite3_result_int64(ctx, iKey); } break; } case JEACH_VALUE: { - if( p->eType==JSON_OBJECT ){ - jsonReturn(&p->sParse.aNode[p->i+1], ctx, 0); - }else{ - jsonReturn(&p->sParse.aNode[p->i], ctx, 0); + if( p->eType==JSON_OBJECT ) pThis++; + jsonReturn(pThis, ctx, 0); + break; + } + case JEACH_TYPE: { + if( p->eType==JSON_OBJECT ) pThis++; + sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC); + break; + } + case JEACH_ATOM: { + if( p->eType==JSON_OBJECT ) pThis++; + if( pThis->eType>=JSON_ARRAY ) break; + jsonReturn(pThis, ctx, 0); + break; + } + case JEACH_ID: { + sqlite3_result_int64(ctx, p->i + (p->eType==JSON_OBJECT)); + break; + } + case JEACH_PARENT: { + if( p->i>0 && p->bRecursive ){ + sqlite3_result_int64(ctx, p->sParse.aUp[p->i]); } break; } @@ -1306,6 +1431,7 @@ static int jsonEachColumn( break; } default: { + assert( i==JEACH_JSON ); sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); break; } @@ -1345,9 +1471,9 @@ static int jsonEachBestIndex( } if( jsonIdx<0 ){ pIdxInfo->idxNum = 0; - pIdxInfo->estimatedCost = (double)2000000000; + pIdxInfo->estimatedCost = 1e99; }else{ - pIdxInfo->estimatedCost = (double)1; + pIdxInfo->estimatedCost = 1.0; pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1; pIdxInfo->aConstraintUsage[jsonIdx].omit = 1; if( pathIdx<0 ){ @@ -1384,7 +1510,9 @@ static int jsonEachFilter( p->zJson = sqlite3_malloc( n+1 ); if( p->zJson==0 ) return SQLITE_NOMEM; memcpy(p->zJson, z, n+1); - if( jsonParse(&p->sParse, p->zJson) ){ + if( jsonParse(&p->sParse, p->zJson) + || (p->bRecursive && jsonParseFindParents(&p->sParse)) + ){ jsonEachCursorReset(p); }else{ JsonNode *pNode; @@ -1421,10 +1549,10 @@ static sqlite3_module jsonEachModule = { jsonEachBestIndex, /* xBestIndex */ jsonEachDisconnect, /* xDisconnect */ 0, /* xDestroy */ - jsonEachOpen, /* xOpen - open a cursor */ + jsonEachOpenEach, /* xOpen - open a cursor */ jsonEachClose, /* xClose - close a cursor */ jsonEachFilter, /* xFilter - configure scan constraints */ - jsonEachNext, /* xNext - advance a cursor */ + jsonEachNextEach, /* xNext - advance a cursor */ jsonEachEof, /* xEof - check for end of scan */ jsonEachColumn, /* xColumn - read data */ jsonEachRowid, /* xRowid - read data */ @@ -1437,6 +1565,35 @@ static sqlite3_module jsonEachModule = { 0, /* xRename */ }; +/* 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 */ + jsonEachClose, /* xClose - close a cursor */ + jsonEachFilter, /* xFilter - configure scan constraints */ + jsonEachNextTree, /* 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 */ +}; + +/**************************************************************************** +** The following routine is the only publically visible identifier in this +** file. Call the following routine in order to register the various SQL +** functions and the virtual table implemented by this file. +****************************************************************************/ #ifdef _WIN32 __declspec(dllexport) @@ -1473,6 +1630,13 @@ int sqlite3_json_init( { "json_nodecount", 1, 0, jsonNodeCountFunc }, #endif }; + static const struct { + const char *zName; + sqlite3_module *pModule; + } aMod[] = { + { "json_each", &jsonEachModule }, + { "json_tree", &jsonTreeModule }, + }; SQLITE_EXTENSION_INIT2(pApi); (void)pzErrMsg; /* Unused parameter */ for(i=0; i