]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add an initial implementation for json_remove().
authordrh <drh@noemail.net>
Mon, 17 Aug 2015 20:14:19 +0000 (20:14 +0000)
committerdrh <drh@noemail.net>
Mon, 17 Aug 2015 20:14:19 +0000 (20:14 +0000)
FossilOrigin-Name: 2a8267209dbda36a37c1b5f65000b2f763c62341

ext/misc/json.c
manifest
manifest.uuid

index 1b2b6ff5c29f6ee24dc2a99869efcc82989176be..dc27349c0f61ec0e0830d4b49cff1adfa64a9ab6 100644 (file)
@@ -64,14 +64,19 @@ static const char * const jsonType[] = {
   "null", "true", "false", "integer", "real", "text", "array", "object"
 };
 
+/* Bit values for the JsonNode.jnFlag field
+*/
+#define JNODE_RAW     0x01         /* Content is raw, not JSON encoded */
+#define JNODE_ESCAPE  0x02         /* Content is text with \ escapes */
+#define JNODE_REMOVE  0x04         /* Do not output */
+
 
 /* A single node of parsed JSON
 */
 typedef struct JsonNode JsonNode;
 struct JsonNode {
   u8 eType;              /* One of the JSON_ type values */
-  u8 bRaw;               /* Content is raw, rather than JSON encoded */
-  u8 bBackslash;         /* Formatted JSON_STRING contains \ escapes */
+  u8 jnFlags;            /* JNODE flags */
   u32 n;                 /* Bytes of content, or number of sub-nodes */
   const char *zJContent; /* JSON content */
 };
@@ -87,6 +92,15 @@ struct JsonParse {
   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.
+*/
+static u32 jsonSizeof(JsonNode *pNode){
+  return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
+}
+
 /* Set the Json object to an empty string
 */
 static void jsonZero(Json *p){
@@ -126,7 +140,7 @@ static void jsonOom(Json *p){
 ** Return zero on success.  Return non-zero on an OOM error
 */
 static int jsonGrow(Json *p, u32 N){
-  u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+100;
+  u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
   char *zNew;
   if( p->bStatic ){
     if( p->oom ) return SQLITE_NOMEM;
@@ -171,6 +185,16 @@ static void jsonAppendChar(Json *p, char c){
   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){
+  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
 ** under construction.  Enclose the string in "..." and escape
 ** any double-quotes or backslash characters contained within the
@@ -209,7 +233,7 @@ static void jsonResult(Json *p){
 ** the number of JsonNode objects that are encoded.
 */
 static int jsonRenderNode(JsonNode *pNode, Json *pOut){
-  u32 j = 0;
+  u32 j = 1;
   switch( pNode->eType ){
     case JSON_NULL: {
       jsonAppendRaw(pOut, "null", 4);
@@ -224,7 +248,7 @@ static int jsonRenderNode(JsonNode *pNode, Json *pOut){
       break;
     }
     case JSON_STRING: {
-      if( pNode->bRaw ){
+      if( pNode->jnFlags & JNODE_RAW ){
         jsonAppendString(pOut, pNode->zJContent, pNode->n);
         break;
       }
@@ -237,28 +261,34 @@ static int jsonRenderNode(JsonNode *pNode, Json *pOut){
     }
     case JSON_ARRAY: {
       jsonAppendChar(pOut, '[');
-      j = 0;
-      while( j<pNode->n ){
-        if( j>0 ) jsonAppendChar(pOut, ',');
-        j += jsonRenderNode(&pNode[j+1], pOut);
+      while( j<=pNode->n ){
+        if( pNode[j].jnFlags & JNODE_REMOVE ){
+          j += jsonSizeof(&pNode[j]);
+        }else{
+          jsonAppendSeparator(pOut);
+          j += jsonRenderNode(&pNode[j], pOut);
+        }
       }
       jsonAppendChar(pOut, ']');
       break;
     }
     case JSON_OBJECT: {
       jsonAppendChar(pOut, '{');
-      j = 0;
-      while( j<pNode->n ){
-        if( j>0 ) jsonAppendChar(pOut, ',');
-        j += jsonRenderNode(&pNode[j+1], pOut);
-        jsonAppendChar(pOut, ':');
-        j += jsonRenderNode(&pNode[j+1], pOut);
+      while( j<=pNode->n ){
+        if( pNode[j+1].jnFlags & JNODE_REMOVE ){
+          j += 1 + jsonSizeof(&pNode[j+1]);
+        }else{
+          jsonAppendSeparator(pOut);
+          jsonRenderNode(&pNode[j], pOut);
+          jsonAppendChar(pOut, ':');
+          j += 1 + jsonRenderNode(&pNode[j+1], pOut);
+        }
       }
       jsonAppendChar(pOut, '}');
       break;
     }
   }
-  return j+1;
+  return j;
 }
 
 /*
@@ -293,9 +323,9 @@ static void jsonReturn(JsonNode *pNode, sqlite3_context *pCtx){
       break;
     }
     case JSON_STRING: {
-      if( pNode->bRaw ){
+      if( pNode->jnFlags & JNODE_RAW ){
         sqlite3_result_text(pCtx, pNode->zJContent, pNode->n, SQLITE_TRANSIENT);
-      }else if( !pNode->bBackslash ){
+      }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
         /* JSON formatted without any backslash-escapes */
         sqlite3_result_text(pCtx, pNode->zJContent+1, pNode->n-2,
                             SQLITE_TRANSIENT);
@@ -406,8 +436,7 @@ static int jsonParseAddNode(
   }
   p = &pParse->aNode[pParse->nNode];
   p->eType = (u8)eType;
-  p->bRaw = 0;
-  p->bBackslash = 0;
+  p->jnFlags = 0;
   p->n = n;
   p->zJContent = zContent;
   return pParse->nNode++;
@@ -477,7 +506,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
     return j+1;
   }else if( c=='"' ){
     /* Parse string */
-    u8 bBackslash = 0;
+    u8 jnFlags = 0;
     j = i+1;
     for(;;){
       c = pParse->zJson[j];
@@ -485,14 +514,14 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
       if( c=='\\' ){
         c = pParse->zJson[++j];
         if( c==0 ) return -1;
-        bBackslash = 1;
+        jnFlags = JNODE_ESCAPE;
       }else if( c=='"' ){
         break;
       }
       j++;
     }
     jsonParseAddNode(pParse, JSON_STRING, j+1-i, &pParse->zJson[i]);
-    if( bBackslash ) pParse->aNode[pParse->nNode-1].bBackslash = 1;
+    pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
     return j+1;
   }else if( c=='n'
          && strncmp(pParse->zJson+i,"null",4)==0
@@ -572,6 +601,7 @@ static int jsonParse(JsonParse *pParse, const char *zJson){
   }
   return 0;
 }
+
 /*
 ** Search along zPath to find the node specified.  Return a pointer
 ** to that node, or NULL if zPath is malformed or if there is no such
@@ -593,10 +623,7 @@ static JsonNode *jsonLookup(JsonNode *pRoot, const char *zPath){
         return jsonLookup(&pRoot[j+1], &zPath[i]);
       }
       j++;
-      if( pRoot[j].eType==JSON_ARRAY || pRoot[j].eType==JSON_OBJECT ){
-        j += pRoot[j].n;
-      }
-      j++;
+      j += jsonSizeof(&pRoot[j]);
     }
   }else if( zPath[0]=='[' && isdigit(zPath[1]) ){
     if( pRoot->eType!=JSON_ARRAY ) return 0;
@@ -610,10 +637,7 @@ static JsonNode *jsonLookup(JsonNode *pRoot, const char *zPath){
     zPath++;
     j = 1;
     while( i>0 && j<=pRoot->n ){
-      if( pRoot[j].eType==JSON_ARRAY || pRoot[j].eType==JSON_OBJECT ){
-        j += pRoot[j].n;
-      }
-      j++;
+      j += jsonSizeof(&pRoot[j]);
       i--;
     }
     if( j<=pRoot->n ){
@@ -627,6 +651,7 @@ static JsonNode *jsonLookup(JsonNode *pRoot, const char *zPath){
 ** SQL functions used for testing and debugging
 ****************************************************************************/
 
+#ifdef SQLITE_DEBUG
 /*
 ** The json_parse(JSON) function returns a string which describes
 ** a parse of the JSON provided.  Or it returns NULL if JSON is not
@@ -640,26 +665,17 @@ static void jsonParseFunc(
   Json s;       /* Output string - not real JSON */
   JsonParse x;  /* The parse */
   u32 i;
-  char zBuf[50];
+  char zBuf[100];
 
   assert( argc==1 );
   if( jsonParse(&x, (const char*)sqlite3_value_text(argv[0])) ) return;
   jsonInit(&s, context);
   for(i=0; i<x.nNode; i++){
-    sqlite3_snprintf(sizeof(zBuf), zBuf, "node %u:\n", i);
-    jsonAppend(&s, zBuf);
-    sqlite3_snprintf(sizeof(zBuf), zBuf, "  type: %s\n",
-                     jsonType[x.aNode[i].eType]);
+    sqlite3_snprintf(sizeof(zBuf), zBuf, "node %3u: %7s n=%d\n",
+                     i, jsonType[x.aNode[i].eType], x.aNode[i].n);
     jsonAppend(&s, zBuf);
-    if( x.aNode[i].eType>=JSON_INT ){
-      sqlite3_snprintf(sizeof(zBuf), zBuf, "     n: %u\n", x.aNode[i].n);
-      jsonAppend(&s, zBuf);
-    }
     if( x.aNode[i].zJContent!=0 ){
-      sqlite3_snprintf(sizeof(zBuf), zBuf, "  ofst: %u\n",
-                       (u32)(x.aNode[i].zJContent - x.zJson));
-      jsonAppend(&s, zBuf);
-      jsonAppendRaw(&s, "  text: ", 8);
+      jsonAppendRaw(&s, "    text: ", 10);
       jsonAppendRaw(&s, x.aNode[i].zJContent, x.aNode[i].n);
       jsonAppendRaw(&s, "\n", 1);
     }
@@ -696,6 +712,7 @@ static void jsonNodeCountFunc(
   sqlite3_result_int64(context, x.nNode);
   sqlite3_free(x.aNode);
 }
+#endif /* SQLITE_DEBUG */
 
 /****************************************************************************
 ** SQL function implementations
@@ -779,10 +796,8 @@ static void jsonArrayLengthFunc(
       JsonNode *pNode = x.aNode;
       if( zPath ) pNode = jsonLookup(pNode, zPath);
       if( pNode->eType==JSON_ARRAY ){
-        for(i=1; i<=pNode->n; i++, n++){
-          if( pNode[i].eType==JSON_ARRAY || pNode[i].eType==JSON_OBJECT ){
-            i += pNode[i].n;
-          }
+        for(i=1; i<=pNode->n; n++){
+          i += jsonSizeof(&pNode[i]);
         }
       }
     }
@@ -882,6 +897,40 @@ static void jsonObjectFunc(
 }
 
 
+/*
+** json_remove(JSON, PATH, ...)
+**
+** Remove the named elements from JSON and return the result.  Ill-formed
+** PATH arguments are silently ignored.  If JSON is ill-formed, then NULL
+** is returned.
+*/
+static void jsonRemoveFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  JsonParse x;          /* The parse */
+  JsonNode *pNode;
+  const char *zPath;
+  u32 i;
+
+  if( argc<1 ) return;
+  if( jsonParse(&x, (const char*)sqlite3_value_text(argv[0])) ) return;
+  if( x.nNode ){
+    for(i=1; i<argc; i++){
+      zPath = (const char*)sqlite3_value_text(argv[i]);
+      if( zPath==0 ) continue;
+      if( zPath[0]!='$' ) continue;
+      pNode = jsonLookup(x.aNode, &zPath[1]);
+      if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
+    }
+    if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
+      jsonReturn(x.aNode, context);
+    }
+  }
+  sqlite3_free(x.aNode);
+}
+
 /*
 ** json_type(JSON)
 ** json_type(JSON, PATH)
@@ -934,13 +983,16 @@ int sqlite3_json_init(
     { "json_array_length",    2,    jsonArrayLengthFunc   },
     { "json_extract",         2,    jsonExtractFunc       },
     { "json_object",         -1,    jsonObjectFunc        },
+    { "json_remove",         -1,    jsonRemoveFunc        },
     { "json_type",            1,    jsonTypeFunc          },
     { "json_type",            2,    jsonTypeFunc          },
 
+#if SQLITE_DEBUG
     /* DEBUG and TESTING functions */
     { "json_parse",           1,    jsonParseFunc     },
     { "json_test1",           1,    jsonTest1Func     },
     { "json_nodecount",       1,    jsonNodeCountFunc },
+#endif
   };
   SQLITE_EXTENSION_INIT2(pApi);
   (void)pzErrMsg;  /* Unused parameter */
index 67b31769fb283c852b6d58da216d2f77748d1752..4fc2dcacd64cd141ccf2167d1b3d6b103fac74de 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Initial\simplementation\sfor\sjson_array_length(),\sjson_extract(),\sand\njson_type().
-D 2015-08-17T15:17:37.780
+C Add\san\sinitial\simplementation\sfor\sjson_remove().
+D 2015-08-17T20:14:19.276
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 7669f34c487f5b328de6b508f374ee1e56558bb0
 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 f26cbaa8ba1e396b3bf1e29aa116abed2a27ef95
+F ext/misc/json.c 30fd85ea1fba24031952aa0c635156cfd8ca02ea
 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
@@ -1374,7 +1374,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 9703c0aa18ae43375af876474b818e504e1c10a5
-R cfeb418e7f67bb21a131fd01c852e6d6
+P 39983204515837e7bd574cf47918e493acc03d1f
+R 9ed2e57314d456e476aa0e6edd9d8c55
 U drh
-Z 903f29a5c61013c958dfe3ed9e5ebef0
+Z 25ffed392c0fb3616f3b027e2b1bb1db
index be625468c7607d87d96aee4f4ae8342ed0dc40a6..3cd093bc94b9dd42af4322c48defab9e8432063f 100644 (file)
@@ -1 +1 @@
-39983204515837e7bd574cf47918e493acc03d1f
\ No newline at end of file
+2a8267209dbda36a37c1b5f65000b2f763c62341
\ No newline at end of file