]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add in many jsonb_xxxx() interfaces. Still uses the internal JsonNode
authordrh <>
Tue, 26 Sep 2023 19:30:46 +0000 (19:30 +0000)
committerdrh <>
Tue, 26 Sep 2023 19:30:46 +0000 (19:30 +0000)
representation for transformations and search, but it does at least conform
to the desired API design.  Largely untested.

FossilOrigin-Name: e6045b4e1bf3a8e33926fc12b3c039f5e1002eaecbe277ffa82b0ec271a29d17

manifest
manifest.uuid
src/json.c

index 06d1240d09514905e9be68ac2e73e49a1be1cf28..80c8adf66ba6b235c162b8307459ed139855e53d 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sjsonb\sbranch.
-D 2023-09-26T15:13:04.116
+C Add\sin\smany\sjsonb_xxxx()\sinterfaces.\s\sStill\suses\sthe\sinternal\sJsonNode\nrepresentation\sfor\stransformations\sand\ssearch,\sbut\sit\sdoes\sat\sleast\sconform\nto\sthe\sdesired\sAPI\sdesign.\s\sLargely\suntested.
+D 2023-09-26T19:30:46.857
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276
-F src/json.c ee823133ed6f56ffb42c99fa78814667be70cd1b8106714cff459ee35f5a063e
+F src/json.c 6fb31345d252dc6992c707ec7e826ec9f2283b026092e5105f9905664a2210c5
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0
 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40
@@ -2122,8 +2122,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a82ebbac3c542ec7f86d1e8414d7fd166db48450115ee3b26d12b5bb445f5896 7ad38254c37153efa72291d09800693ca60894359548eda877d59defa8c70d49
-R 2f21564c602d3b30115c3421d174134f
+P ac242c4d47ec36aab1c2fa5e65e7b595e686f49473b75bd63708d05c59ce3f0f
+R 3743d1571a08e262e44d6bfa0fe7cd5d
 U drh
-Z bdae2aab159970d0c164367006c6960b
+Z 4bd7fe1646cada0525f1a26ea76a3776
 # Remove this line to create a well-formed Fossil manifest.
index 4de851b60c78499320b25cccd03edb578b0badcd..a8e9449ee7b84eab9629d85bdae3eb54869e5fd1 100644 (file)
@@ -1 +1 @@
-ac242c4d47ec36aab1c2fa5e65e7b595e686f49473b75bd63708d05c59ce3f0f
\ No newline at end of file
+e6045b4e1bf3a8e33926fc12b3c039f5e1002eaecbe277ffa82b0ec271a29d17
\ No newline at end of file
index cfd37754f884d334a2fb8bd3a47b186acc92796f..0b5068a9f05fb78bb338615fbdf704b3e5e1d6a2 100644 (file)
@@ -159,6 +159,16 @@ static const char * const jsonType[] = {
 #define JNODE_LABEL   0x20  /* Is a label of an object */
 #define JNODE_JSON5   0x40  /* Node contains JSON5 enhancements */
 
+/*
+** Bit values for the flags passed into jsonExtractFunc() or
+** jsonSetFunc() via the user-data value.
+*/
+#define JSON_JSON      0x01        /* Result is always JSON */
+#define JSON_SQL       0x02        /* Result is always SQL */
+#define JSON_ABPATH    0x03        /* Allow abbreviated JSON path specs */
+#define JSON_ISSET     0x04        /* json_set(), not json_insert() */
+#define JSON_BLOB      0x08        /* Use the BLOB output format */
+
 
 /* A single node of parsed JSON.  An array of these nodes describes
 ** a parse of JSON + edits.
@@ -847,6 +857,13 @@ static void jsonRenderNode(
   }
 }
 
+/* Forward reference */
+static void jsonRenderNodeAsBlob(
+  JsonParse *pParse,             /* the complete parse of the JSON */
+  JsonNode *pNode,               /* The node to render */
+  JsonParse *pOut                /* Write the BLOB rendering of JSON here */
+);
+
 /*
 ** Return a JsonNode and all its descendants as a JSON string.
 */
@@ -856,12 +873,22 @@ static void jsonReturnJson(
   sqlite3_context *pCtx,      /* Return value for this function */
   int bGenerateAlt            /* Also store the rendered text in zAlt */
 ){
+  int flags;
   JsonString s;
   if( pParse->oom ){
     sqlite3_result_error_nomem(pCtx);
     return;
   }
-  if( pParse->nErr==0 ){
+  if( pParse->nErr ){
+    return;
+  }
+  flags = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
+  if( flags & JSON_BLOB ){
+    JsonParse x;
+    memset(&x, 0, sizeof(x));
+    jsonRenderNodeAsBlob(pParse, pNode, &x);
+    sqlite3_result_blob(pCtx, x.aBlob, x.nBlob, sqlite3_free);
+  }else{
     jsonInit(&s, pCtx);
     jsonRenderNode(pParse, pNode, &s);
     if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){
@@ -1811,7 +1838,7 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
 }
 
 /* Forward reference */
-static int jsonParseValueFromBinary(JsonParse *pParse, u32 i);
+static int jsonParseValueFromBlob(JsonParse *pParse, u32 i);
 
 /*
 ** Parse a complete JSON string.  Return 0 on success or non-zero if there
@@ -1830,7 +1857,7 @@ static int jsonParse(
   if( pParse->isBinary ){
     pParse->aBlob = (u8*)pParse->zJson;
     pParse->nBlob = pParse->nJson;
-    i = jsonParseValueFromBinary(pParse, 0);
+    i = jsonParseValueFromBlob(pParse, 0);
   }else{
     i = jsonParseValue(pParse, 0);
   }
@@ -3247,7 +3274,7 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
 ** is still in use.  It will be removed after all processing translates
 ** to the new BLOB encoding.
 */
-static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){
+static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){
   u8 t;     /* Node type */
   u32 sz;   /* Node size */
   u32 x;    /* Index of payload start */
@@ -3310,7 +3337,7 @@ static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){
       int iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
       u32 j = i+x;
       while( j<i+x+sz ){
-        int r = jsonParseValueFromBinary(pParse, j);
+        int r = jsonParseValueFromBlob(pParse, j);
         if( r<=0 ) return -1;
         j = (u32)r;
       }
@@ -3321,7 +3348,7 @@ static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){
       int iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
       u32 j = i+x, k = 0;
       while( j<i+x+sz ){
-        int r = jsonParseValueFromBinary(pParse, j);
+        int r = jsonParseValueFromBlob(pParse, j);
         if( r<=0 ) return -1;
         if( (k++&1)==0 ){
           pParse->aNode[pParse->nNode-1].jnFlags |= JNODE_LABEL;
@@ -3335,6 +3362,137 @@ static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){
   return i+x+sz;
 }
 
+/*
+** Convert pNode that belongs to pParse into the BLOB representation.
+** The BLOB representation is added to the pOut->aBlob[] array.
+*/
+static void jsonRenderNodeAsBlob(
+  JsonParse *pParse,             /* the complete parse of the JSON */
+  JsonNode *pNode,               /* The node to render */
+  JsonParse *pOut                /* Write the BLOB rendering of JSON here */
+){
+  assert( pNode!=0 );
+  while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){
+    u32 idx = (u32)(pNode - pParse->aNode);
+    u32 i = pParse->iSubst;
+    while( 1 /*exit-by-break*/ ){
+      assert( i<pParse->nNode );
+      assert( pParse->aNode[i].eType==JSON_SUBST );
+      assert( pParse->aNode[i].eU==4 );
+      assert( pParse->aNode[i].u.iPrev<i );
+      if( pParse->aNode[i].n==idx ){
+        pNode = &pParse->aNode[i+1];
+        break;
+      }
+      i = pParse->aNode[i].u.iPrev;
+    }
+  }
+  switch( pNode->eType ){
+    default: {
+      assert( pNode->eType==JSON_NULL );
+      jsonBlobAppendNodeType(pOut, JSONB_NULL, 0);
+      break;
+    }
+    case JSON_TRUE: {
+      jsonBlobAppendNodeType(pOut, JSONB_TRUE, 0);
+      break;
+    }
+    case JSON_FALSE: {
+      jsonBlobAppendNodeType(pOut, JSONB_FALSE, 0);
+      break;
+    }
+    case JSON_STRING: {
+      int op;
+      assert( pNode->eU==1 );
+      if( pNode->jnFlags & JNODE_RAW ){
+        if( memchr(pNode->u.zJContent, '"', pNode->n)==0
+         && memchr(pNode->u.zJContent, '\\', pNode->n)==0
+        ){
+          op = JSONB_TEXT;
+        }else{
+          op = JSONB_TEXTRAW;
+        }
+      }else if( pNode->jnFlags & JNODE_JSON5 ){
+        op = JSONB_TEXT5;
+      }else{
+        op = JSONB_TEXTJ;
+      }
+      jsonBlobAppendNodeType(pOut, op, pNode->n);
+      jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n);
+      break;
+    }
+    case JSON_REAL: {
+      int op;
+      assert( pNode->eU==1 );
+      if( pNode->jnFlags & JNODE_JSON5 ){
+        op = JSONB_FLOAT5;
+      }else{
+        assert( pNode->n>0 );
+        op = JSONB_FLOAT;
+      }
+      jsonBlobAppendNodeType(pOut, op, pNode->n);
+      jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n);
+      break;
+    }
+    case JSON_INT: {
+      int op;
+      assert( pNode->eU==1 );
+      if( pNode->jnFlags & JNODE_JSON5 ){
+        op = JSONB_INT5;
+      }else{
+        assert( pNode->n>0 );
+        op = JSONB_INT;
+      }
+      jsonBlobAppendNodeType(pOut, op, pNode->n);
+      jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n);
+      break;
+    }
+    case JSON_ARRAY: {
+      u32 j = 1;
+      u32 iStart, iThis = pOut->nBlob;
+      jsonBlobAppendNodeType(pOut, JSONB_ARRAY, 0x7fffffff);
+      iStart = pOut->nBlob;
+      for(;;){
+        while( j<=pNode->n ){
+          if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
+            jsonRenderNodeAsBlob(pParse, &pNode[j], pOut);
+          }
+          j += jsonNodeSize(&pNode[j]);
+        }
+        if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
+        if( pParse->useMod==0 ) break;
+        assert( pNode->eU==2 );
+        pNode = &pParse->aNode[pNode->u.iAppend];
+        j = 1;
+      }
+      jsonBlobChangePayloadSize(pOut, iThis, pOut->nBlob - iStart);
+      break;
+    }
+    case JSON_OBJECT: {
+      u32 j = 1;
+      u32 iStart, iThis = pOut->nBlob;
+      jsonBlobAppendNodeType(pOut, JSONB_OBJECT, 0x7fffffff);
+      iStart = pOut->nBlob;
+      for(;;){
+        while( j<=pNode->n ){
+          if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
+            jsonRenderNodeAsBlob(pParse, &pNode[j], pOut);
+            jsonRenderNodeAsBlob(pParse, &pNode[j+1], pOut);
+          }
+          j += 1 + jsonNodeSize(&pNode[j+1]);
+        }
+        if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
+        if( pParse->useMod==0 ) break;
+        assert( pNode->eU==2 );
+        pNode = &pParse->aNode[pNode->u.iAppend];
+        j = 1;
+      }
+      jsonBlobChangePayloadSize(pOut, iThis, pOut->nBlob - iStart);
+      break;
+    }
+  }
+}
+
 /****************************************************************************
 ** SQL functions used for testing and debugging
 ****************************************************************************/
@@ -3614,15 +3772,6 @@ static void jsonArrayLengthFunc(
   sqlite3_result_int64(ctx, n);
 }
 
-/*
-** Bit values for the flags passed into jsonExtractFunc() or
-** jsonSetFunc() via the user-data value.
-*/
-#define JSON_JSON      0x01        /* Result is always JSON */
-#define JSON_SQL       0x02        /* Result is always SQL */
-#define JSON_ABPATH    0x03        /* Allow abbreviated JSON path specs */
-#define JSON_ISSET     0x04        /* json_set(), not json_insert() */
-
 /*
 ** json_extract(JSON, PATH, ...)
 ** "->"(JSON,PATH)
@@ -4913,26 +5062,32 @@ static sqlite3_module jsonTreeModule = {
 void sqlite3RegisterJsonFunctions(void){
 #ifndef SQLITE_OMIT_JSON
   static FuncDef aJsonFunc[] = {
-    JFUNCTION(json,               1, 0,  jsonRemoveFunc),
-    JFUNCTION(json_array,        -1, 0,  jsonArrayFunc),
-    JFUNCTION(json_array_length,  1, 0,  jsonArrayLengthFunc),
-    JFUNCTION(json_array_length,  2, 0,  jsonArrayLengthFunc),
-    JFUNCTION(json_error_position,1, 0,  jsonErrorFunc),
-    JFUNCTION(json_extract,      -1, 0,  jsonExtractFunc),
-    JFUNCTION(->,                 2, JSON_JSON, jsonExtractFunc),
-    JFUNCTION(->>,                2, JSON_SQL, jsonExtractFunc),
-    JFUNCTION(json_insert,       -1, 0,  jsonSetFunc),
-    JFUNCTION(json_object,       -1, 0,  jsonObjectFunc),
-    JFUNCTION(json_patch,         2, 0,  jsonPatchFunc),
-    JFUNCTION(json_quote,         1, 0,  jsonQuoteFunc),
-    JFUNCTION(json_remove,       -1, 0,  jsonRemoveFunc),
-    JFUNCTION(json_replace,      -1, 0,  jsonReplaceFunc),
-    JFUNCTION(json_set,          -1, JSON_ISSET,  jsonSetFunc),
-    JFUNCTION(json_type,          1, 0,  jsonTypeFunc),
-    JFUNCTION(json_type,          2, 0,  jsonTypeFunc),
-    JFUNCTION(json_valid,         1, 0,  jsonValidFunc),
-    JFUNCTION(jsonb,              1, 0,  jsonbFunc),
-    JFUNCTION(jsonb_test2,        1, 0,  jsonbTest2),
+    JFUNCTION(json,               1, 0,                    jsonRemoveFunc),
+    JFUNCTION(json_array,        -1, 0,                    jsonArrayFunc),
+    JFUNCTION(json_array_length,  1, 0,                    jsonArrayLengthFunc),
+    JFUNCTION(json_array_length,  2, 0,                    jsonArrayLengthFunc),
+    JFUNCTION(json_error_position,1, 0,                    jsonErrorFunc),
+    JFUNCTION(json_extract,      -1, 0,                    jsonExtractFunc),
+    JFUNCTION(->,                 2, JSON_JSON,            jsonExtractFunc),
+    JFUNCTION(->>,                2, JSON_SQL,             jsonExtractFunc),
+    JFUNCTION(json_insert,       -1, 0,                    jsonSetFunc),
+    JFUNCTION(json_object,       -1, 0,                    jsonObjectFunc),
+    JFUNCTION(json_patch,         2, 0,                    jsonPatchFunc),
+    JFUNCTION(json_quote,         1, 0,                    jsonQuoteFunc),
+    JFUNCTION(json_remove,       -1, 0,                    jsonRemoveFunc),
+    JFUNCTION(json_replace,      -1, 0,                    jsonReplaceFunc),
+    JFUNCTION(json_set,          -1, JSON_ISSET,           jsonSetFunc),
+    JFUNCTION(json_type,          1, 0,                    jsonTypeFunc),
+    JFUNCTION(json_type,          2, 0,                    jsonTypeFunc),
+    JFUNCTION(json_valid,         1, 0,                    jsonValidFunc),
+    JFUNCTION(jsonb,              1, JSON_BLOB,            jsonbFunc),
+    JFUNCTION(jsonb_test2,        1, 0,                    jsonbTest2),
+    JFUNCTION(jsonb_insert,      -1, JSON_BLOB,            jsonSetFunc),
+    JFUNCTION(jsonb_patch,        2, JSON_BLOB,            jsonPatchFunc),
+    JFUNCTION(jsonb_quote,        1, JSON_BLOB,            jsonQuoteFunc),
+    JFUNCTION(jsonb_remove,      -1, JSON_BLOB,            jsonRemoveFunc),
+    JFUNCTION(jsonb_replace,     -1, JSON_BLOB,            jsonReplaceFunc),
+    JFUNCTION(jsonb_set,         -1, JSON_ISSET|JSON_BLOB, jsonSetFunc),
 #if SQLITE_DEBUG
     JFUNCTION(json_parse,         1, 0,  jsonParseFunc),
     JFUNCTION(json_test1,         1, 0,  jsonTest1Func),