typedef unsigned char u8;
/* Objects */
-typedef struct Json Json;
+typedef struct JsonString JsonString;
typedef struct JsonNode JsonNode;
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[] */
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;
};
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);
}
/* 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);
/* 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 = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
char *zNew;
if( p->bStatic ){
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;
#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;
}
/* 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++] = '"';
** 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) ){
/* 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,
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
*/
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 ){
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];
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];
}
case JSON_ARRAY:
case JSON_OBJECT: {
- Json s;
+ JsonString s;
jsonInit(&s, pCtx);
jsonRenderNode(pNode, &s, aReplace);
jsonResult(&s);
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*);
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;
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;
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];
jsonAppendRaw(&s, "\n", 1);
}
}
- sqlite3_free(x.aNode);
+ jsonParseReset(&x);
jsonResult(&s);
}
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);
}
/*
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 */
sqlite3_value **argv
){
int i;
- Json jx;
+ JsonString jx;
jsonInit(&jx, context);
jsonAppendChar(&jx, '[');
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);
}
if( pNode ){
jsonReturn(pNode, context, 0);
}
- sqlite3_free(x.aNode);
+ jsonParseReset(&x);
}
/*
sqlite3_value **argv
){
int i;
- Json jx;
+ JsonString jx;
const char *z;
u32 n;
jsonReturn(x.aNode, context, 0);
}
}
- sqlite3_free(x.aNode);
+ jsonParseReset(&x);
}
/*
jsonReturn(x.aNode, context, argv);
}
}
- sqlite3_free(x.aNode);
+ jsonParseReset(&x);
}
+
/*
** json_set(JSON, PATH, VALUE, ...)
**
jsonReturn(x.aNode, context, argv);
}
}
- sqlite3_free(x.aNode);
+ jsonParseReset(&x);
}
/*
if( zPath ) pNode = jsonLookup(&x, 0, zPath, 0);
sqlite3_result_text(context, jsonType[pNode->eType], -1, SQLITE_STATIC);
}
- sqlite3_free(x.aNode);
+ jsonParseReset(&x);
}
/****************************************************************************
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 */
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 */
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;
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;
}
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->i<p->sParse.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;
}
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;
}
break;
}
default: {
+ assert( i==JEACH_JSON );
sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
break;
}
}
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 ){
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;
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 */
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)
{ "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<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
(void*)&aFunc[i].flag,
aFunc[i].xFunc, 0, 0);
}
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_module(db, "json_each", &jsonEachModule, 0);
+ for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
}
return rc;
}