]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the json_each(JSON,PATH) table-valued-function.
authordrh <drh@noemail.net>
Wed, 19 Aug 2015 22:47:17 +0000 (22:47 +0000)
committerdrh <drh@noemail.net>
Wed, 19 Aug 2015 22:47:17 +0000 (22:47 +0000)
FossilOrigin-Name: 3335ac17bbcb09dc915173d69bf42048f84ad563

ext/misc/json.c
manifest
manifest.uuid

index 274749726d3c6d68a086a014bc0959c1f34ffe41..7e1d210e3701acd6cf34f70f5a099ef58b2a1f80 100644 (file)
@@ -17,7 +17,9 @@
 **
 ** For the time being, all JSON is stored as pure text.  (We might add
 ** a JSONB type in the future which stores a binary encoding of JSON in
-** a BLOB, but there is no support for JSONB in the current implementation.)
+** a BLOB, but there is no support for JSONB in the current implementation.
+** This implementation parses JSON text at 250 MB/s, so it is hard to see
+** how JSONB might improve on that.)
 */
 #include "sqlite3ext.h"
 SQLITE_EXTENSION_INIT1
@@ -1156,6 +1158,276 @@ static void jsonTypeFunc(
   sqlite3_free(x.aNode);
 }
 
+/****************************************************************************
+** The json_each virtual table
+****************************************************************************/
+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 */
+};
+
+/* Constructor for the json_each virtual table */
+static int jsonEachConnect(
+  sqlite3 *db,
+  void *pAux,
+  int argc, const char *const*argv,
+  sqlite3_vtab **ppVtab,
+  char **pzErr
+){
+  sqlite3_vtab *pNew;
+  pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+  if( pNew==0 ) return SQLITE_NOMEM;
+
+/* 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;
+}
+
+/* destructor for json_each virtual table */
+static int jsonEachDisconnect(sqlite3_vtab *pVtab){
+  sqlite3_free(pVtab);
+  return SQLITE_OK;
+}
+
+/* constructor for a JsonEachCursor object. */
+static int jsonEachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+  JsonEachCursor *pCur;
+  pCur = sqlite3_malloc( sizeof(*pCur) );
+  if( pCur==0 ) return SQLITE_NOMEM;
+  memset(pCur, 0, sizeof(*pCur));
+  *ppCursor = &pCur->base;
+  return SQLITE_OK;
+}
+
+/* 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);
+  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;
+}
+
+/* Destructor for a jsonEachCursor object */
+static int jsonEachClose(sqlite3_vtab_cursor *cur){
+  JsonEachCursor *p = (JsonEachCursor*)cur;
+  jsonEachCursorReset(p);
+  sqlite3_free(cur);
+  return SQLITE_OK;
+}
+
+/* Return TRUE if the jsonEachCursor object has been advanced off the end
+** of the JSON object */
+static int jsonEachEof(sqlite3_vtab_cursor *cur){
+  JsonEachCursor *p = (JsonEachCursor*)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){
+  JsonEachCursor *p = (JsonEachCursor*)cur;
+  switch( p->eType ){
+    case JSON_ARRAY: {
+      p->i += jsonSize(&p->sParse.aNode[p->i]);
+      p->iRowid++;
+      break;
+    }
+    case JSON_OBJECT: {
+      p->i += 1 + jsonSize(&p->sParse.aNode[p->i+1]);
+      p->iRowid++;
+      break;
+    }
+    default: {
+      p->i = p->iEnd;
+      break;
+    }
+  }
+  return SQLITE_OK;
+}
+
+/* Return the value of a column */
+static int jsonEachColumn(
+  sqlite3_vtab_cursor *cur,   /* The cursor */
+  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
+  int i                       /* Which column to return */
+){
+  JsonEachCursor *p = (JsonEachCursor*)cur;
+  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);
+      }
+      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);
+      }
+      break;
+    }
+    case JEACH_PATH: {
+      const char *zPath = p->zPath;
+      if( zPath==0 ) zPath = "$";
+      sqlite3_result_text(ctx, zPath, -1, SQLITE_STATIC);
+      break;
+    }
+    default: {
+      sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+      break;
+    }
+  }
+  return SQLITE_OK;
+}
+
+/* Return the current rowid value */
+static int jsonEachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+  JsonEachCursor *p = (JsonEachCursor*)cur;
+  *pRowid = p->iRowid;
+  return SQLITE_OK;
+}
+
+/* The query strategy is to look for an equality constraint on the json
+** column.  Without such a constraint, the table cannot operate.  idxNum is
+** 1 if the constraint is found, 3 if the constraint and zPath are found,
+** and 0 otherwise.
+*/
+static int jsonEachBestIndex(
+  sqlite3_vtab *tab,
+  sqlite3_index_info *pIdxInfo
+){
+  int i;
+  int jsonIdx = -1;
+  int pathIdx = -1;
+  const struct sqlite3_index_constraint *pConstraint;
+  pConstraint = pIdxInfo->aConstraint;
+  for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+    if( pConstraint->usable==0 ) continue;
+    if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+    switch( pConstraint->iColumn ){
+      case JEACH_JSON:   jsonIdx = i;    break;
+      case JEACH_PATH:   pathIdx = i;    break;
+      default:           /* no-op */     break;
+    }
+  }
+  if( jsonIdx<0 ){
+    pIdxInfo->idxNum = 0;
+    pIdxInfo->estimatedCost = (double)2000000000;
+  }else{
+    pIdxInfo->estimatedCost = (double)1;
+    pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1;
+    pIdxInfo->aConstraintUsage[jsonIdx].omit = 1;
+    if( pathIdx<0 ){
+      pIdxInfo->idxNum = 1;
+    }else{
+      pIdxInfo->aConstraintUsage[pathIdx].argvIndex = 2;
+      pIdxInfo->aConstraintUsage[pathIdx].omit = 1;
+      pIdxInfo->idxNum = 3;
+    }
+  }
+  return SQLITE_OK;
+}
+
+/* Start a search on a new JSON string */
+static int jsonEachFilter(
+  sqlite3_vtab_cursor *cur,
+  int idxNum, const char *idxStr,
+  int argc, sqlite3_value **argv
+){
+  JsonEachCursor *p = (JsonEachCursor*)cur;
+  const char *z;
+  const char *zPath;
+  sqlite3_int64 n;
+
+  jsonEachCursorReset(p);
+  if( idxNum==0 ) return SQLITE_OK;
+  z = (const char*)sqlite3_value_text(argv[0]);
+  if( z==0 ) return SQLITE_OK;
+  if( idxNum&2 ){
+    zPath = (const char*)sqlite3_value_text(argv[1]);
+    if( zPath==0 || zPath[0]!='$' ) return SQLITE_OK;
+  }
+  n = sqlite3_value_bytes(argv[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) ){
+    jsonEachCursorReset(p);
+  }else{
+    JsonNode *pNode;
+    if( idxNum==3 ){
+      n = sqlite3_value_bytes(argv[1]);
+      p->zPath = sqlite3_malloc( n+1 );
+      if( p->zPath==0 ) return SQLITE_NOMEM;
+      memcpy(p->zPath, zPath, n+1);
+      pNode = jsonLookup(&p->sParse, 0, p->zPath+1, 0);
+      if( pNode==0 ){
+        jsonEachCursorReset(p);
+        return SQLITE_OK;
+      }
+    }else{
+      pNode = p->sParse.aNode;
+    }
+    p->i = (int)(pNode - p->sParse.aNode);
+    p->eType = pNode->eType;
+    if( p->eType>=JSON_ARRAY ){
+      p->i++;
+      p->iEnd = p->i + pNode->n;
+    }else{
+      p->iEnd = p->i+1;
+    }
+  }
+  return SQLITE_OK;
+}
+
+/* The methods of the json_each virtual table */
+static sqlite3_module jsonEachModule = {
+  0,                         /* iVersion */
+  0,                         /* xCreate */
+  jsonEachConnect,           /* xConnect */
+  jsonEachBestIndex,         /* xBestIndex */
+  jsonEachDisconnect,        /* xDisconnect */
+  0,                         /* xDestroy */
+  jsonEachOpen,              /* 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 */
+};
+
+
 #ifdef _WIN32
 __declspec(dllexport)
 #endif
@@ -1199,5 +1471,8 @@ int sqlite3_json_init(
                                  (void*)&aFunc[i].flag,
                                  aFunc[i].xFunc, 0, 0);
   }
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_module(db, "json_each", &jsonEachModule, 0);
+  }
   return rc;
 }
index fa1a720a0076b25673e8d2c31eeb43a968a44f39..d5dc07e75eb7950fdbbda8e6bfc5625acf994a63 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sthe\stable-valued-function\srowid\sfix.
-D 2015-08-19T19:26:13.526
+C Add\sthe\sjson_each(JSON,PATH)\stable-valued-function.
+D 2015-08-19T22:47:17.476
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 90f3097efb9a53f5fc59a4f8a08be07cf9f52c02
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -192,7 +192,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
 F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767
 F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e
-F ext/misc/json.c 3b265167a9f8f006042bf0226a838c773a098c03
+F ext/misc/json.c 57a9f747b2813edc5f481eb808889d304a1c5f79
 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
@@ -1378,7 +1378,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 96a5d44d9fcb7b159c04630ad2d956fe27df5a43 a325a08599759471047e234ef9cfcc3cb110aafd
-R dcfe96af8a1b65c426e62b553bd60db8
+P a06a6392bd48baa8b9bac2624869c0cc7da7e779
+R 43d8c9f353202a0c5a0252660436f9ac
 U drh
-Z 00eb910bc67976561bafd5a76ff4be3d
+Z 41b78b71d0425818407424da18ee9720
index ab65dd6ddb97663fda6c5ca0196d5def5d17435f..a1d2dfbb42d022766e46e9596fceb9667b5c623b 100644 (file)
@@ -1 +1 @@
-a06a6392bd48baa8b9bac2624869c0cc7da7e779
\ No newline at end of file
+3335ac17bbcb09dc915173d69bf42048f84ad563
\ No newline at end of file