]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
The json_patch() function now operates exclusively on JSONB. This patch
authordrh <>
Wed, 29 Nov 2023 17:36:54 +0000 (17:36 +0000)
committerdrh <>
Wed, 29 Nov 2023 17:36:54 +0000 (17:36 +0000)
also includes improvements to JSONB debug printing routines.

FossilOrigin-Name: fee19d0098242110d2c44ec7b9620c1210ef3f87913305f66ec85d277dd96ab6

manifest
manifest.uuid
src/json.c

index 275eb80263a72d07dbf06af9dfdaebff4b8efa7d..a779c2b472bca4e1db253f5063bc7ac54da1b9dc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sthe\sjsonb\sbranch.
-D 2023-11-29T12:18:02.265
+C The\sjson_patch()\sfunction\snow\soperates\sexclusively\son\sJSONB.\s\sThis\spatch\nalso\sincludes\simprovements\sto\sJSONB\sdebug\sprinting\sroutines.
+D 2023-11-29T17:36:54.076
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -688,7 +688,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 aebeb6c1beea6a143223139e9b168b4187aff02c60a49daa6bcafadafcb6ee15
+F src/json.c 00b55c9e54047e1412fb7303e954e996c84bb8b06d3e376b12f49178c4fd7b69
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9
@@ -2145,8 +2145,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 5ab790736d943e08f097efcee5cfbf0d83c65b0a53f273060330ba719affa5e5 cad269d5e274443c39203a56603b991accc0399135d436996fc039d1d28ec9db
-R e74b99bb550123c4a0166b07666ffd15
+P 1a59fcab2179cc3b52ecd3de7d2018db96ac149aaff521959773a517b8d9ac3e
+R 883e1938b9afc8e6538b46e875ce6c0e
 U drh
-Z 2ea38ce368a7d99793b6d4f302fe78c3
+Z 19b3c18296db473321d95b26a8cafb5e
 # Remove this line to create a well-formed Fossil manifest.
index 2b290251230f8aa33099ee34e095392d023e63e1..f95e15f1a1e3d1d11bae2f7c8ae72f2b6e78abd0 100644 (file)
@@ -1 +1 @@
-1a59fcab2179cc3b52ecd3de7d2018db96ac149aaff521959773a517b8d9ac3e
\ No newline at end of file
+fee19d0098242110d2c44ec7b9620c1210ef3f87913305f66ec85d277dd96ab6
\ No newline at end of file
index d210e5e98687bfc20026d2dfe4a53a94312afbaa..c0fca583a4e321c8b2ea09ef01c6f7e442caeab1 100644 (file)
@@ -2033,15 +2033,6 @@ static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
   if( pNode->n!=nKey ) return 0;
   return strncmp(pNode->u.zJContent, zKey, nKey)==0;
 }
-static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){
-  if( p1->jnFlags & JNODE_RAW ){
-    return jsonLabelCompare(p2, p1->u.zJContent, p1->n);
-  }else if( p2->jnFlags & JNODE_RAW ){
-    return jsonLabelCompare(p1, p2->u.zJContent, p2->n);
-  }else{
-    return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0;
-  }
-}
 
 /*
 ** Search along zPath to find the node specified.  Return a pointer
@@ -2327,25 +2318,6 @@ static void jsonWrongNumArgs(
   sqlite3_free(zMsg);
 }
 
-/*
-** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
-*/
-static void jsonRemoveAllNulls(JsonNode *pNode){
-  int i, n;
-  assert( pNode->eType==JSON_OBJECT );
-  n = pNode->n;
-  for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
-    switch( pNode[i].eType ){
-      case JSON_NULL:
-        pNode[i].jnFlags |= JNODE_REMOVE;
-        break;
-      case JSON_OBJECT:
-        jsonRemoveAllNulls(&pNode[i]);
-        break;
-    }
-  }
-}
-
 /****************************************************************************
 ** Utility routines for dealing with the binary BLOB representation of JSON
 ****************************************************************************/
@@ -3111,7 +3083,9 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
          (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4];
     n = 5;
   }
-  if( i+sz+n>pParse->nBlob ){
+  if( i+sz+n > pParse->nBlob
+   && i+sz+n > pParse->nBlob-pParse->delta
+  ){
     sz = 0;
     n = 0;
   }
@@ -4314,18 +4288,33 @@ static void jsonDebugPrintBlob(
     u32 i, n, nn, sz = 0;
     int showContent = 1;
     u8 x = pParse->aBlob[iStart] & 0x0f;
+    u32 savedNBlob = pParse->nBlob;
     printf("%5d:%*s", iStart, nIndent, "");
+    if( pParse->nBlobAlloc>pParse->nBlob ){
+      pParse->nBlob = pParse->nBlobAlloc;
+    }
     nn = n = jsonbPayloadSize(pParse, iStart, &sz);
     if( nn==0 ) nn = 1;
     if( sz>0 && x<JSONB_ARRAY ){
       nn += sz;
     }
     for(i=0; i<nn; i++) printf(" %02x", pParse->aBlob[iStart+i]);
-    if( n==0 || iStart+n+sz>iEnd ){
+    if( n==0 ){
       printf("   ERROR invalid node size\n");
       iStart = n==0 ? iStart+1 : iEnd;
       continue;
     }
+    pParse->nBlob = savedNBlob;
+    if( iStart+n+sz>iEnd ){
+      iEnd = iStart+n+sz;
+      if( iEnd>pParse->nBlob ){
+        if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){
+          iEnd = pParse->nBlobAlloc;
+        }else{
+          iEnd = pParse->nBlob;
+        }
+      }
+    }
     printf("  <-- ");
     switch( x ){
       case JSONB_NULL:     printf("null"); break;
@@ -4379,7 +4368,11 @@ static void jsonShowParse(JsonParse *pParse){
     printf("NULL pointer\n");
     return;
   }else{
-    printf("%u byte JSONB:\n", pParse->nBlob);
+    printf("nBlobAlloc = %u\n", pParse->nBlobAlloc);
+    printf("nBlob = %u\n", pParse->nBlob);
+    printf("delta = %d\n", pParse->delta);
+    if( pParse->nBlob==0 ) return;
+    printf("content (bytes 0..%u):\n", pParse->nBlob-1);
   }
   jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0);
 }
@@ -4735,79 +4728,6 @@ json_extract_error:
   return;
 }
 
-/* This is the RFC 7396 MergePatch algorithm.
-*/
-static JsonNode *jsonMergePatch(
-  JsonParse *pParse,   /* The JSON parser that contains the TARGET */
-  u32 iTarget,         /* Node of the TARGET in pParse */
-  JsonNode *pPatch     /* The PATCH */
-){
-  u32 i, j;
-  u32 iRoot;
-  JsonNode *pTarget;
-  if( pPatch->eType!=JSON_OBJECT ){
-    return pPatch;
-  }
-  assert( iTarget<pParse->nNode );
-  pTarget = &pParse->aNode[iTarget];
-  assert( (pPatch->jnFlags & JNODE_APPEND)==0 );
-  if( pTarget->eType!=JSON_OBJECT ){
-    jsonRemoveAllNulls(pPatch);
-    return pPatch;
-  }
-  iRoot = iTarget;
-  for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){
-    u32 nKey;
-    const char *zKey;
-    if( pPatch[i].eType!=JSON_STRING ){
-      pParse->nErr = 1;
-      return 0;
-    }
-    assert( pPatch[i].eU==1 );
-    nKey = pPatch[i].n;
-    zKey = pPatch[i].u.zJContent;
-    for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
-      assert( pTarget[j].eType==JSON_STRING );
-      assert( pTarget[j].jnFlags & JNODE_LABEL );
-      if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){
-        if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ) break;
-        if( pPatch[i+1].eType==JSON_NULL ){
-          pTarget[j+1].jnFlags |= JNODE_REMOVE;
-        }else{
-          JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
-          if( pNew==0 ) return 0;
-          if( pNew!=&pParse->aNode[iTarget+j+1] ){
-            jsonParseAddSubstNode(pParse, iTarget+j+1);
-            jsonParseAddNodeArray(pParse, pNew, jsonNodeSize(pNew));
-          }
-          pTarget = &pParse->aNode[iTarget];
-        }
-        break;
-      }
-    }
-    if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
-      int iStart;
-      JsonNode *pApnd;
-      u32 nApnd;
-      iStart = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
-      jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
-      pApnd = &pPatch[i+1];
-      if( pApnd->eType==JSON_OBJECT ) jsonRemoveAllNulls(pApnd);
-      nApnd = jsonNodeSize(pApnd);
-      jsonParseAddNodeArray(pParse, pApnd, jsonNodeSize(pApnd));
-      if( pParse->oom ) return 0;
-      pParse->aNode[iStart].n = 1+nApnd;
-      pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
-      pParse->aNode[iRoot].u.iAppend = iStart;
-      JSON_VVA( pParse->aNode[iRoot].eU = 2 );
-      iRoot = iStart;
-      pTarget = &pParse->aNode[iTarget];
-    }
-  }
-  return pTarget;
-}
-
-
 /*
 ** Return codes for jsonMergePatchBlob()
 */
@@ -4908,9 +4828,10 @@ static int jsonMergePatchBlob(
   }
   x = pTarget->aBlob[iTarget] & 0x0f;
   if( x!=JSONB_OBJECT ){  /* Algorithm line 05 */
-    static const u8 emptyObject[] = { JSONB_OBJECT };
     n = jsonbPayloadSize(pTarget, iTarget, &sz);
-    jsonBlobEdit(pTarget, iTarget, sz, emptyObject, 1); /* Line 06 */
+    jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0);
+    x = pTarget->aBlob[iTarget];
+    pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT;
   }
   n = jsonbPayloadSize(pPatch, iPatch, &sz);
   if( n==0 ) return JSON_MERGE_BADPATCH;
@@ -5019,7 +4940,7 @@ static int jsonMergePatchBlob(
       }
     }
   }
-  jsonAfterEditSizeAdjust(pTarget, iTarget);
+  if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget);
   return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK;
 }
 
@@ -5034,65 +4955,27 @@ static void jsonPatchFunc(
   int argc,
   sqlite3_value **argv
 ){
-  JsonParse *pX;     /* The JSON that is being patched */
-  JsonParse *pY;     /* The patch */
-  JsonNode *pResult;   /* The result of the merge */
+  JsonParse *pTarget;    /* The TARGET */
+  JsonParse *pPatch;     /* The PATCH */
+  int rc;                /* Result code */
 
   UNUSED_PARAMETER(argc);
-  if( jsonFuncArgMightBeBinary(argv[0])
-   && jsonFuncArgMightBeBinary(argv[1])
-  ){
-    JsonParse target, patch;
-    sqlite3 *db;
-    int rc;
-    memset(&target, 0, sizeof(target));
-    memset(&patch, 0, sizeof(patch));
-    target.aBlob = (u8*)sqlite3_value_blob(argv[0]);
-    target.nBlob = sqlite3_value_bytes(argv[0]);
-    patch.aBlob = (u8*)sqlite3_value_blob(argv[1]);
-    patch.nBlob = sqlite3_value_bytes(argv[1]);
-    db = sqlite3_context_db_handle(ctx);
-    if( db->mallocFailed ) return;
-    if( !jsonBlobMakeEditable(&target, patch.nBlob) ) return;
-    rc = jsonMergePatchBlob(&target, 0, &patch, 0);
-    if( rc ){
-      if( rc==JSON_MERGE_OOM ){
-        sqlite3_result_error_nomem(ctx);
-      }else{
-        sqlite3_result_error(ctx, "malformed JSON", -1);
-      }
-      jsonParseReset(&target);
+  assert( argc==2 );
+  pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE);
+  if( pTarget==0 ) return;
+  pPatch = jsonParseFuncArg(ctx, argv[1], 0);
+  if( pPatch ){
+    rc = jsonMergePatchBlob(pTarget, 0, pPatch, 0);
+    if( rc==JSON_MERGE_OK ){
+      jsonReturnParse(ctx, pTarget);
+    }else if( rc==JSON_MERGE_OOM ){
+      sqlite3_result_error_nomem(ctx);
     }else{
-      int flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
-      if( flgs & JSON_BLOB ){
-        sqlite3_result_blob(ctx, target.aBlob, target.nBlob, SQLITE_DYNAMIC);
-      }else{
-        JsonString s;
-        jsonStringInit(&s, ctx);
-        jsonXlateBlobToText(&target, 0, &s);
-        jsonReturnString(&s);
-        jsonParseReset(&target);
-      }
+      sqlite3_result_error(ctx, "malformed JSON", -1);
     }
-    return;
-  }
-  pX = jsonParseCached(ctx, argv[0], ctx, 1);
-  if( pX==0 ) return;
-  assert( pX->hasMod==0 );
-  pX->hasMod = 1;
-  pY = jsonParseCached(ctx, argv[1], ctx, 1);
-  if( pY==0 ) return;
-  pX->useMod = 1;
-  pY->useMod = 1;
-  pResult = jsonMergePatch(pX, 0, pY->aNode);
-  assert( pResult!=0 || pX->oom || pX->nErr );
-  if( pX->oom ){
-    sqlite3_result_error_nomem(ctx);
-  }else if( pX->nErr ){
-    sqlite3_result_error(ctx, "malformed JSON", -1);
-  }else if( pResult ){
-    jsonReturnNodeAsJson(pX, pResult, ctx, 0, 0);
+    jsonParseFree(pPatch);
   }
+  jsonParseFree(pTarget);
 }