]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merge recent trunk enhancements and fixes into the jsonb branch.
authordrh <>
Fri, 10 Nov 2023 18:59:23 +0000 (18:59 +0000)
committerdrh <>
Fri, 10 Nov 2023 18:59:23 +0000 (18:59 +0000)
FossilOrigin-Name: 091a5f058dfe2115fb9213655b34f00bcec80aebb299b571975cfe4ecd5ec206

1  2 
manifest
manifest.uuid
src/json.c
src/sqliteInt.h
src/vdbe.c
src/vdbeapi.c

diff --cc manifest
index 90388082d6c3871dd0ad4cf211544b10b0715718,fadd9147083f1f066c8f6be1882af71c90c87c0b..2d3eaed8b0b33f56c1b426a0ec88a461c76ecdf0
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Merge\srecent\strunk\senhancements\sinto\sthe\sjsonb\sbranch,\sand\sespecially\sthe\nfiner-grain\scharacterization\sof\sJSON\sfunction\sproperties.
- D 2023-11-08T17:11:13.302
 -C Ensure\s8-byte\salignment\sof\sdata\sstructues\sin\ssqlite3_database_file_object().\nThis\sshould\shave\sappeared\son\strunk\soriginally\sand\sthen\sbe\scherry-picked\sonto\nthe\sbranch.\s\sOh\swell....
 -D 2023-11-10T17:49:26.551
++C Merge\srecent\strunk\senhancements\sand\sfixes\sinto\sthe\sjsonb\sbranch.
++D 2023-11-10T18:59:23.439
  F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
  F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
  F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@@ -684,10 -684,10 +684,10 @@@ F src/hash.h 3340ab6e1d13e725571d7cee6d
  F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
  F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
  F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276
- F src/json.c e745701b9859a0940f8314eb59e913685a98a606506deef004675b43129da840
 -F src/json.c f93bf3df3651b1e01e2b57f7dc56f727e7b0e212d934eacf21c6fc8b31bf685e
++F src/json.c 01644dc0c33b331fd5f294eddb1e4e02a4057535193f5f38161ee75047dd7177
  F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
  F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
- F src/main.c e1bc8864834697503d370d94613be945d05ca1c5ebdda43e7d5c8ee8c48d433c
+ F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9
  F src/malloc.c f016922435dc7d1f1f5083a03338a3e91f8c67ce2c5bdcfa4cdef62e612f5fcc
  F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
  F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@@@ -724,12 -724,12 +724,12 @@@ F src/printf.c 9da63b9ae1c14789bcae1284
  F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
  F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916
  F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
- F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515
- F src/shell.c.in 7312c571ebf518fc8927bbb5aeb4fa67e5b0dfb2adae4258dcd1ccae42c11e1f
- F src/sqlite.h.in a0fce680a40fe81b13eae3749d001134d9fe0a43aecc09a8986520d5119acfcd
+ F src/select.c 47797c57c5ee2ad183b34a2e5d643ec7519366686bbe44a9a81df9fe304f28a7
+ F src/shell.c.in 297625a1ba6ea1c08bc2ea1b838b646cad309b62bf08df0e379355629404f140
+ F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35
  F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
  F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
- F src/sqliteInt.h 1412a692dfb5d615e416f3ddb9b33e5b6bd39f70432bb84046c50574fc604b51
 -F src/sqliteInt.h cd171cba32c7a553e7623fbd82b68b36a1b6c81079ab963260777ea9b3abe4d9
++F src/sqliteInt.h 6b82eb99a9d2887e873fb29e56befb7c50cf4624df615d23a28f071dc8abf5f6
  F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6
  F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
  F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@@ -794,10 -794,10 +794,10 @@@ F src/upsert.c fa125a8d3410ce9a97b02cb5
  F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
  F src/util.c b22cc9f203a8c0b9ee5338a67f8860347d14845864c10248bebe84518a781677
  F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
- F src/vdbe.c 5f7432b22b66a09503caab15e86f582f7b55299e1d366799896ae3e354192f09
 -F src/vdbe.c 7034cf3eec0c905df753368efbcdd96377fca0245584e66766ec47a29fe468c8
++F src/vdbe.c 44bccc33ff8864b4b937be4d67ebb244e4f4e5ebc6d44832298954df6b7d6a78
  F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0
  F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c
- F src/vdbeapi.c 22a2661a2886f6b142fce91e95533a1841135e8217f65297d7df353a0eddf650
 -F src/vdbeapi.c b07df805110dc6e81f2a3f9cd4e83f56ea523277a59bcec489a12b740c1079e7
++F src/vdbeapi.c 8f57d60c89da0b60e6d4e272358c511f6bae4e24330bdb11f8b42f986d1bf21b
  F src/vdbeaux.c f3997b5956c8d97bd2fc3392db42caecddfa6549e9df82e0a7e5804653ca475a
  F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5
  F src/vdbemem.c 0012d5f01cc866833847c2f3ae4c318ac53a1cb3d28acad9c35e688039464cf0
@@@ -2140,8 -2139,9 +2140,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9
  F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
  F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
  F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
- P b12110276fc15d1b6b0cc455f89747ace7a09650d5ba433d8bb431bb4e5bc951 b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f
- R d8efa19343b75dfdaa31ac930f849ac1
 -P a976b7208ff8603d7353ce9a0bdfba8e681cbb2ed3de6cfb5f0e8b07312ab86f
 -Q +3cfcaafaff181c7945cc659ff6d58a0d2232d49830a259f0510d833a7a5a824b
 -R 1028456d182afa02f9e623f04c8760e0
++P 72393b003f9f8675e4a124dddd09607b5b34ddefe56716b283c68c0982fb3d96 ac39800bb2685fa287c7d834faed75f0bc61320ef986de314392d6eadb574d30
++R 6d3d51033686bbc98309c106a216587b
  U drh
- Z 8a4148addede5d91729ce4e74e65d8c3
 -Z a0dd8db0b2162f7029aa06a0eb66b266
++Z 403b43107da0aaabcdd1c627364824ec
  # Remove this line to create a well-formed Fossil manifest.
diff --cc manifest.uuid
index 0529da599b76e442ff5a739ef14f0c900a9ee7d2,fc184b9086ec97749fab8d2b9a12dde59cd1b3de..8ef4377df0fa2c3a2be79e9b13ef9a4e10bb4bfc
@@@ -1,1 -1,1 +1,1 @@@
- 72393b003f9f8675e4a124dddd09607b5b34ddefe56716b283c68c0982fb3d96
 -ac39800bb2685fa287c7d834faed75f0bc61320ef986de314392d6eadb574d30
++091a5f058dfe2115fb9213655b34f00bcec80aebb299b571975cfe4ecd5ec206
diff --cc src/json.c
index 04349899c8a216dc5dac1f91db9915ad078fb7ff,0f11ace02bc34636516bbfc972e1b7faa6b6cbfc..3787167e90499f94be28ae01ac45518d16ad1575
@@@ -592,18 -429,22 +592,24 @@@ static void jsonAppendString(JsonStrin
  static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){
    u32 i;
    jsonAppendChar(p, '"');
 -  zIn++;
 -  N -= 2;
    while( N>0 ){
-     for(i=0; i<N && zIn[i]!='\\'; i++){}
+     for(i=0; i<N && zIn[i]!='\\' && zIn[i]!='"'; i++){}
      if( i>0 ){
        jsonAppendRawNZ(p, zIn, i);
 +      if( i>=N ) break;
        zIn += i;
        N -= i;
 -      if( N==0 ) break;
 +    }
 +    if( N<2 ){
 +      p->eErr |= JSTRING_MALFORMED;
 +      break;
      }
+     if( zIn[0]=='"' ){
+       jsonAppendRawNZ(p, "\\\"", 2);
+       zIn++;
+       N--;
+       continue;
+     }
      assert( zIn[0]=='\\' );
      switch( (u8)zIn[1] ){
        case '\'':
@@@ -1031,9 -839,9 +1037,10 @@@ static void jsonReturnNodeAsJson
    JsonParse *pParse,          /* The complete JSON */
    JsonNode *pNode,            /* Node to return */
    sqlite3_context *pCtx,      /* Return value for this function */
-   int bGenerateAlt            /* Also store the rendered text in zAlt */
+   int bGenerateAlt,           /* Also store the rendered text in zAlt */
+   int omitSubtype             /* Do not call sqlite3_result_subtype() */
  ){
 +  int flags;
    JsonString s;
    if( pParse->oom ){
      sqlite3_result_error_nomem(pCtx);
        pParse->zAlt = sqlite3RCStrRef(s.zBuf);
        pParse->nAlt = s.nUsed;
      }
 -    jsonResult(&s);
 +    jsonReturnString(&s);
-     sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
+     if( !omitSubtype ) sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
    }
  }
  
@@@ -1093,19 -891,13 +1100,20 @@@ static u32 jsonHexToInt4(const char *z)
  }
  
  /*
 -** Make the JsonNode the return value of the function.
 +** Make the return value from an SQL function be the SQL value of
 +** JsonNode pNode.
 +**
 +** If pNode is an atom (not an array or object) then the value returned
 +** is a pure SQL value - an SQLITE_INTEGER, SQLITE_REAL, SQLITE_TEXT, or
 +** SQLITE_NULL.  However, if pNode is a JSON array or object, then the
 +** value returned is either RFC-8259 JSON text or a BLOB in the JSONB
 +** format, depending on the JSON_BLOB flag of the function user-data.
  */
 -static void jsonReturn(
 +static void jsonReturnFromNode(
    JsonParse *pParse,          /* Complete JSON parse tree */
    JsonNode *pNode,            /* Node to return */
-   sqlite3_context *pCtx       /* Return value for this function */
+   sqlite3_context *pCtx,      /* Return value for this function */
+   int omitSubtype             /* Do not call sqlite3_result_subtype() */
  ){
    switch( pNode->eType ){
      default: {
      }
      case JSON_ARRAY:
      case JSON_OBJECT: {
-       jsonReturnNodeAsJson(pParse, pNode, pCtx, 0);
 -      jsonReturnJson(pParse, pNode, pCtx, 0, omitSubtype);
++      jsonReturnNodeAsJson(pParse, pNode, pCtx, 0, omitSubtype);
        break;
      }
    }
@@@ -2540,1791 -2307,101 +2548,1791 @@@ static void jsonRemoveAllNulls(JsonNod
    }
  }
  
 -
  /****************************************************************************
 -** SQL functions used for testing and debugging
 +** Utility routines for dealing with the binary BLOB representation of JSON
  ****************************************************************************/
  
 -#if SQLITE_DEBUG
 +
  /*
 -** Print N node entries.
 +** Expand pParse->aBlob so that it holds at least N bytes.
 +**
 +** Return the number of errors.
  */
 -static void jsonDebugPrintNodeEntries(
 -  JsonNode *aNode,  /* First node entry to print */
 -  int N             /* Number of node entries to print */
 -){
 -  int i;
 -  for(i=0; i<N; i++){
 -    const char *zType;
 -    if( aNode[i].jnFlags & JNODE_LABEL ){
 -      zType = "label";
 -    }else{
 -      zType = jsonType[aNode[i].eType];
 -    }
 -    printf("node %4u: %-7s n=%-5d", i, zType, aNode[i].n);
 -    if( (aNode[i].jnFlags & ~JNODE_LABEL)!=0 ){
 -      u8 f = aNode[i].jnFlags;
 -      if( f & JNODE_RAW )     printf(" RAW");
 -      if( f & JNODE_ESCAPE )  printf(" ESCAPE");
 -      if( f & JNODE_REMOVE )  printf(" REMOVE");
 -      if( f & JNODE_REPLACE ) printf(" REPLACE");
 -      if( f & JNODE_APPEND )  printf(" APPEND");
 -      if( f & JNODE_JSON5 )   printf(" JSON5");
 -    }
 -    switch( aNode[i].eU ){
 -      case 1:  printf(" zJContent=[%.*s]\n",
 -                      aNode[i].n, aNode[i].u.zJContent);           break;
 -      case 2:  printf(" iAppend=%u\n", aNode[i].u.iAppend);        break;
 -      case 3:  printf(" iKey=%u\n", aNode[i].u.iKey);              break;
 -      case 4:  printf(" iPrev=%u\n", aNode[i].u.iPrev);            break;
 -      default: printf("\n");
 -    }
 +static int jsonBlobExpand(JsonParse *pParse, u32 N){
 +  u8 *aNew;
 +  u32 t;
 +  assert( N>pParse->nBlobAlloc );
 +  if( pParse->nBlobAlloc==0 ){
 +    t = 100;
 +  }else{
 +    t = pParse->nBlobAlloc*2;
    }
 +  if( t<N ) t = N+100;
 +  aNew = sqlite3_realloc64( pParse->aBlob, t );
 +  if( aNew==0 ){ pParse->oom = 1; return 1; }
 +  pParse->aBlob = aNew;
 +  pParse->nBlobAlloc = t;
 +  return 0;
  }
 -#endif /* SQLITE_DEBUG */
 -
  
 -#if 0  /* 1 for debugging.  0 normally.  Requires -DSQLITE_DEBUG too */
 -static void jsonDebugPrintParse(JsonParse *p){
 -  jsonDebugPrintNodeEntries(p->aNode, p->nNode);
 -}
 -static void jsonDebugPrintNode(JsonNode *pNode){
 -  jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode));
 +/*
 +** If pParse->aBlob is not previously editable (because it is taken
 +** from sqlite3_value_blob(), as indicated by the fact that
 +** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable
 +** by making a copy into space obtained from malloc.
 +**
 +** Return true on success.  Return false on OOM.
 +*/
 +static int jsonBlobMakeEditable(JsonParse *pParse){
 +  u8 *aOld;
 +  u32 nSize;
 +  if( pParse->nBlobAlloc>0 ) return 1;
 +  aOld = pParse->aBlob;
 +  nSize = pParse->nBlob + pParse->nIns;
 +  if( nSize>100 ) nSize -= 100;
 +  pParse->aBlob = 0;
 +  if( jsonBlobExpand(pParse, nSize) ){
 +    return 0;
 +  }
 +  assert( pParse->nBlobAlloc >= pParse->nBlob + pParse->nIns );
 +  memcpy(pParse->aBlob, aOld, pParse->nBlob);
 +  return 1;
  }
 -#else
 -   /* The usual case */
 -# define jsonDebugPrintNode(X)
 -# define jsonDebugPrintParse(X)
 -#endif
  
 -#ifdef SQLITE_DEBUG
 -/*
 -** SQL function:   json_parse(JSON)
 +
 +/* Expand pParse->aBlob and append N bytes.
  **
 -** Parse JSON using jsonParseCached().  Then print a dump of that
 -** parse on standard output.  Return the mimified JSON result, just
 -** like the json() function.
 +** Return the number of errors.
  */
 -static void jsonParseFunc(
 -  sqlite3_context *ctx,
 -  int argc,
 -  sqlite3_value **argv
 +static SQLITE_NOINLINE int jsonBlobExpandAndAppend(
 +  JsonParse *pParse,
 +  const u8 *aData,
 +  u32 N
  ){
 -  JsonParse *p;        /* The parse */
 +  if( jsonBlobExpand(pParse, pParse->nBlob+N) ) return 1;
 +  memcpy(&pParse->aBlob[pParse->nBlob], aData, N);
 +  pParse->nBlob += N;
 +  return 0;
 +}
  
 -  assert( argc==1 );
 -  p = jsonParseCached(ctx, argv[0], ctx, 0);
 -  if( p==0 ) return;
 -  printf("nNode     = %u\n", p->nNode);
 -  printf("nAlloc    = %u\n", p->nAlloc);
 -  printf("nJson     = %d\n", p->nJson);
 -  printf("nAlt      = %d\n", p->nAlt);
 -  printf("nErr      = %u\n", p->nErr);
 -  printf("oom       = %u\n", p->oom);
 -  printf("hasNonstd = %u\n", p->hasNonstd);
 -  printf("useMod    = %u\n", p->useMod);
 -  printf("hasMod    = %u\n", p->hasMod);
 -  printf("nJPRef    = %u\n", p->nJPRef);
 -  printf("iSubst    = %u\n", p->iSubst);
 -  printf("iHold     = %u\n", p->iHold);
 -  jsonDebugPrintNodeEntries(p->aNode, p->nNode);
 -  jsonReturnJson(p, p->aNode, ctx, 1, 0);
 +/* Append a single character.  Return 1 if an error occurs.
 +*/
 +static int jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
 +  if( pParse->nBlob >= pParse->nBlobAlloc ){
 +    return jsonBlobExpandAndAppend(pParse, &c, 1);
 +  }
 +  pParse->aBlob[pParse->nBlob++] = c;
 +  return 0;
  }
  
 -/*
 -** The json_test1(JSON) function return true (1) if the input is JSON
 -** text generated by another json function.  It returns (0) if the input
 -** is not known to be JSON.
 +/* Append bytes.  Return 1 if an error occurs.
 +*/
 +static int jsonBlobAppendNBytes(JsonParse *pParse, const u8 *aData, u32 N){
 +  if( pParse->nBlob+N > pParse->nBlobAlloc ){
 +    return jsonBlobExpandAndAppend(pParse, aData, N);
 +  }
 +  memcpy(&pParse->aBlob[pParse->nBlob], aData, N);
 +  pParse->nBlob += N;
 +  return 0;
 +}
 +
 +/* Append an node type byte together with the payload size.
 +*/
 +static void jsonBlobAppendNodeType(
 +  JsonParse *pParse,
 +  u8 eType,
 +  u32 szPayload
 +){
 +  u8 a[5];
 +  if( szPayload<=11 ){
 +    jsonBlobAppendOneByte(pParse, eType | (szPayload<<4));
 +  }else if( szPayload<=0xff ){
 +    a[0] = eType | 0xc0;
 +    a[1] = szPayload & 0xff;
 +    jsonBlobAppendNBytes(pParse, a, 2);
 +  }else if( szPayload<=0xffff ){
 +    a[0] = eType | 0xd0;
 +    a[1] = (szPayload >> 8) & 0xff;
 +    a[2] = szPayload & 0xff;
 +    jsonBlobAppendNBytes(pParse, a, 3);
 +  }else{
 +    a[0] = eType | 0xe0;
 +    a[1] = (szPayload >> 24) & 0xff;
 +    a[2] = (szPayload >> 16) & 0xff;
 +    a[3] = (szPayload >> 8) & 0xff;
 +    a[4] = szPayload & 0xff;
 +    jsonBlobAppendNBytes(pParse, a, 5);
 +  }
 +}
 +
 +/* Change the payload size for the node at index i to be szPayload.
 +*/
 +static void jsonBlobChangePayloadSize(
 +  JsonParse *pParse,
 +  u32 i,
 +  u32 szPayload
 +){
 +  u8 *a;
 +  u8 szType;
 +  u8 nExtra;
 +  u8 nNeeded;
 +  i8 delta;
 +  if( pParse->oom ) return;
 +  a = &pParse->aBlob[i];
 +  szType = a[0]>>4;
 +  if( szType<=11 ){
 +    nExtra = 0;
 +  }else if( szType==12 ){
 +    nExtra = 1;
 +  }else if( szType==13 ){
 +    nExtra = 2;
 +  }else{
 +    nExtra = 4;
 +  }
 +  if( szPayload<=11 ){
 +    nNeeded = 0;
 +  }else if( szPayload<=0xff ){
 +    nNeeded = 1;
 +  }else if( szPayload<=0xffff ){
 +    nNeeded = 2;
 +  }else{
 +    nNeeded = 4;
 +  }
 +  delta = nNeeded - nExtra;
 +  if( delta ){
 +    u32 newSize = pParse->nBlob + delta;
 +    if( delta>0 ){
 +      if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){
 +        return;  /* OOM error.  Error state recorded in pParse->oom. */
 +      }
 +      a = &pParse->aBlob[i];
 +      memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1));
 +    }else{
 +      memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta));
 +    }
 +    pParse->nBlob = newSize;
 +  }
 +  if( nNeeded==0 ){
 +    a[0] = (a[0] & 0x0f) | (szPayload<<4);
 +  }else if( nNeeded==1 ){
 +    a[0] = (a[0] & 0x0f) | 0xc0;
 +    a[1] = szPayload & 0xff;
 +  }else if( nNeeded==2 ){
 +    a[0] = (a[0] & 0x0f) | 0xd0;
 +    a[1] = (szPayload >> 8) & 0xff;
 +    a[2] = szPayload & 0xff;
 +  }else{
 +    a[0] = (a[0] & 0x0f) | 0xe0;
 +    a[1] = (szPayload >> 24) & 0xff;
 +    a[2] = (szPayload >> 16) & 0xff;
 +    a[3] = (szPayload >> 8) & 0xff;
 +    a[4] = szPayload & 0xff;
 +  }
 +}
 +
 +/*
 +** If z[0] is 'u' and is followed by exactly 4 hexadecimal character,
 +** then set *pOp to JSONB_TEXTJ and return true.  If not, do not make
 +** any changes to *pOp and return false.
 +*/
 +static int jsonIs4HexB(const char *z, int *pOp){
 +  if( z[0]!='u' ) return 0;
 +  if( !sqlite3Isxdigit(z[1]) ) return 0;
 +  if( !sqlite3Isxdigit(z[2]) ) return 0;
 +  if( !sqlite3Isxdigit(z[3]) ) return 0;
 +  if( !sqlite3Isxdigit(z[4]) ) return 0;
 +  *pOp = JSONB_TEXTJ;
 +  return 1;
 +}
 +
 +/*
 +** Translate a single element of JSON text at pParse->zJson[i] into
 +** its equivalent binary JSONB representation.  Append the translation into
 +** pParse->aBlob[] beginning at pParse->nBlob.  The size of
 +** pParse->aBlob[] is increased as necessary.
 +**
 +** Return the index of the first character past the end of the element parsed,
 +** or one of the following special result codes:
 +**
 +**      0    End of input
 +**     -1    Syntax error
 +**     -2    '}' seen   \
 +**     -3    ']' seen    \___  For these returns, pParse->iErr is set to
 +**     -4    ',' seen    /     the index in zJson[] of the seen character
 +**     -5    ':' seen   /
 +*/
 +static int jsonXlateTextToBlob(JsonParse *pParse, u32 i){
 +  char c;
 +  u32 j;
 +  u32 iThis, iStart;
 +  int x;
 +  u8 t;
 +  const char *z = pParse->zJson;
 +json_parse_restart:
 +  switch( (u8)z[i] ){
 +  case '{': {
 +    /* Parse object */
 +    iThis = pParse->nBlob;
 +    jsonBlobAppendNodeType(pParse, JSONB_OBJECT, (pParse->nJson-i)*2);
 +    if( ++pParse->iDepth > JSON_MAX_DEPTH ){
 +      pParse->iErr = i;
 +      return -1;
 +    }
 +    iStart = pParse->nBlob;
 +    for(j=i+1;;j++){
 +      u32 iBlob = pParse->nBlob;
 +      x = jsonXlateTextToBlob(pParse, j);
 +      if( x<=0 ){
 +        int op;
 +        if( x==(-2) ){
 +          j = pParse->iErr;
 +          if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1;
 +          break;
 +        }
 +        j += json5Whitespace(&z[j]);
 +        op = JSONB_TEXT;
 +        if( sqlite3JsonId1(z[j]) 
 +         || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op))
 +        ){
 +          int k = j+1;
 +          while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
 +            || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op))
 +          ){
 +            k++;
 +          }
 +          assert( iBlob==pParse->nBlob );
 +          jsonBlobAppendNodeType(pParse, op, k-j);
 +          jsonBlobAppendNBytes(pParse, (const u8*)&z[j], k-j);
 +          pParse->hasNonstd = 1;
 +          x = k;
 +        }else{
 +          if( x!=-1 ) pParse->iErr = j;
 +          return -1;
 +        }
 +      }
 +      if( pParse->oom ) return -1;
 +      t = pParse->aBlob[iBlob] & 0x0f;
 +      if( t<JSONB_TEXT || t>JSONB_TEXTRAW ){
 +        pParse->iErr = j;
 +        return -1;
 +      }
 +      j = x;
 +      if( z[j]==':' ){
 +        j++;
 +      }else{
 +        if( fast_isspace(z[j]) ){
 +          do{ j++; }while( fast_isspace(z[j]) );
 +          if( z[j]==':' ){
 +            j++;
 +            goto parse_object_value;
 +          }
 +        }
 +        x = jsonXlateTextToBlob(pParse, j);
 +        if( x!=(-5) ){
 +          if( x!=(-1) ) pParse->iErr = j;
 +          return -1;
 +        }
 +        j = pParse->iErr+1;
 +      }
 +    parse_object_value:
 +      x = jsonXlateTextToBlob(pParse, j);
 +      if( x<=0 ){
 +        if( x!=(-1) ) pParse->iErr = j;
 +        return -1;
 +      }
 +      j = x;
 +      if( z[j]==',' ){
 +        continue;
 +      }else if( z[j]=='}' ){
 +        break;
 +      }else{
 +        if( fast_isspace(z[j]) ){
 +          do{ j++; }while( fast_isspace(z[j]) );
 +          if( z[j]==',' ){
 +            continue;
 +          }else if( z[j]=='}' ){
 +            break;
 +          }
 +        }
 +        x = jsonXlateTextToBlob(pParse, j);
 +        if( x==(-4) ){
 +          j = pParse->iErr;
 +          continue;
 +        }
 +        if( x==(-2) ){
 +          j = pParse->iErr;
 +          break;
 +        }
 +      }
 +      pParse->iErr = j;
 +      return -1;
 +    }
 +    jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
 +    pParse->iDepth--;
 +    return j+1;
 +  }
 +  case '[': {
 +    /* Parse array */
 +    iThis = pParse->nBlob;
 +    jsonBlobAppendNodeType(pParse, JSONB_ARRAY, pParse->nJson - i);
 +    iStart = pParse->nBlob;
 +    if( pParse->oom ) return -1;
 +    if( ++pParse->iDepth > JSON_MAX_DEPTH ){
 +      pParse->iErr = i;
 +      return -1;
 +    }
 +    for(j=i+1;;j++){
 +      x = jsonXlateTextToBlob(pParse, j);
 +      if( x<=0 ){
 +        if( x==(-3) ){
 +          j = pParse->iErr;
 +          if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1;
 +          break;
 +        }
 +        if( x!=(-1) ) pParse->iErr = j;
 +        return -1;
 +      }
 +      j = x;
 +      if( z[j]==',' ){
 +        continue;
 +      }else if( z[j]==']' ){
 +        break;
 +      }else{
 +        if( fast_isspace(z[j]) ){
 +          do{ j++; }while( fast_isspace(z[j]) );
 +          if( z[j]==',' ){
 +            continue;
 +          }else if( z[j]==']' ){
 +            break;
 +          }
 +        }
 +        x = jsonXlateTextToBlob(pParse, j);
 +        if( x==(-4) ){
 +          j = pParse->iErr;
 +          continue;
 +        }
 +        if( x==(-3) ){
 +          j = pParse->iErr;
 +          break;
 +        }
 +      }
 +      pParse->iErr = j;
 +      return -1;
 +    }
 +    jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
 +    pParse->iDepth--;
 +    return j+1;
 +  }
 +  case '\'': {
 +    u8 opcode;
 +    char cDelim;
 +    int nn;
 +    pParse->hasNonstd = 1;
 +    opcode = JSONB_TEXT;
 +    goto parse_string;
 +  case '"':
 +    /* Parse string */
 +    opcode = JSONB_TEXT;
 +  parse_string:
 +    cDelim = z[i];
 +    nn = pParse->nJson;
 +    for(j=i+1; j<nn; j++){
 +      if( jsonIsOk[(unsigned char)z[j]] ) continue;
 +      c = z[j];
 +      if( c==cDelim ){
 +        break;
 +      }else if( c=='\\' ){
 +        c = z[++j];
 +        if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
 +           || c=='n' || c=='r' || c=='t'
 +           || (c=='u' && jsonIs4Hex(&z[j+1])) ){
 +          if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
 +        }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
 +           || (0xe2==(u8)c && 0x80==(u8)z[j+1]
 +                && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
 +           || (c=='x' && jsonIs2Hex(&z[j+1])) ){
 +          opcode = JSONB_TEXT5;
 +          pParse->hasNonstd = 1;
 +        }else if( c=='\r' ){
 +          if( z[j+1]=='\n' ) j++;
 +          opcode = JSONB_TEXT5;
 +          pParse->hasNonstd = 1;
 +        }else{
 +          pParse->iErr = j;
 +          return -1;
 +        }
 +      }else if( c<=0x1f ){
 +        /* Control characters are not allowed in strings */
 +        pParse->iErr = j;
 +        return -1;
 +      }
 +    }
 +    jsonBlobAppendNodeType(pParse, opcode, j-1-i);
 +    jsonBlobAppendNBytes(pParse, (const u8*)&z[i+1], j-1-i);
 +    return j+1;
 +  }
 +  case 't': {
 +    if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
 +      jsonBlobAppendOneByte(pParse, JSONB_TRUE);
 +      return i+4;
 +    }
 +    pParse->iErr = i;
 +    return -1;
 +  }
 +  case 'f': {
 +    if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
 +      jsonBlobAppendOneByte(pParse, JSONB_FALSE);
 +      return i+5;
 +    }
 +    pParse->iErr = i;
 +    return -1;
 +  }
 +  case '+': {
 +    u8 seenE;
 +    pParse->hasNonstd = 1;
 +    t = 0x00;            /* Bit 0x01:  JSON5.   Bit 0x02:  FLOAT */
 +    goto parse_number;
 +  case '.':
 +    if( sqlite3Isdigit(z[i+1]) ){
 +      pParse->hasNonstd = 1;
 +      t = 0x03;          /* Bit 0x01:  JSON5.   Bit 0x02:  FLOAT */
 +      seenE = 0;
 +      goto parse_number_2;
 +    }
 +    pParse->iErr = i;
 +    return -1;
 +  case '-':
 +  case '0':
 +  case '1':
 +  case '2':
 +  case '3':
 +  case '4':
 +  case '5':
 +  case '6':
 +  case '7':
 +  case '8':
 +  case '9':
 +    /* Parse number */
 +    t = 0x00;            /* Bit 0x01:  JSON5.   Bit 0x02:  FLOAT */
 +  parse_number:
 +    seenE = 0;
 +    assert( '-' < '0' );
 +    assert( '+' < '0' );
 +    assert( '.' < '0' );
 +    c = z[i];
 +
 +    if( c<='0' ){
 +      if( c=='0' ){
 +        if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
 +          assert( t==0x00 );
 +          pParse->hasNonstd = 1;
 +          t = 0x01;
 +          for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
 +          goto parse_number_finish;
 +        }else if( sqlite3Isdigit(z[i+1]) ){
 +          pParse->iErr = i+1;
 +          return -1;
 +        }
 +      }else{
 +        if( !sqlite3Isdigit(z[i+1]) ){
 +          /* JSON5 allows for "+Infinity" and "-Infinity" using exactly
 +          ** that case.  SQLite also allows these in any case and it allows
 +          ** "+inf" and "-inf". */
 +          if( (z[i+1]=='I' || z[i+1]=='i')
 +           && sqlite3StrNICmp(&z[i+1], "inf",3)==0
 +          ){
 +            pParse->hasNonstd = 1;
 +            if( z[i]=='-' ){
 +              jsonBlobAppendNodeType(pParse, JSONB_FLOAT, 6);
 +              jsonBlobAppendNBytes(pParse, (const u8*)"-9e999", 6);
 +            }else{
 +              jsonBlobAppendNodeType(pParse, JSONB_FLOAT, 5);
 +              jsonBlobAppendNBytes(pParse, (const u8*)"9e999", 5);
 +            }
 +            return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
 +          }
 +          if( z[i+1]=='.' ){
 +            pParse->hasNonstd = 1;
 +            t |= 0x01;
 +            goto parse_number_2;
 +          }
 +          pParse->iErr = i;
 +          return -1;
 +        }
 +        if( z[i+1]=='0' ){
 +          if( sqlite3Isdigit(z[i+2]) ){
 +            pParse->iErr = i+1;
 +            return -1;
 +          }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
 +            pParse->hasNonstd = 1;
 +            t |= 0x01;
 +            for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
 +            goto parse_number_finish;
 +          }
 +        }
 +      }
 +    }
 +
 +  parse_number_2:
 +    for(j=i+1;; j++){
 +      c = z[j];
 +      if( sqlite3Isdigit(c) ) continue;
 +      if( c=='.' ){
 +        if( (t & 0x02)!=0 ){
 +          pParse->iErr = j;
 +          return -1;
 +        }
 +        t |= 0x02;
 +        continue;
 +      }
 +      if( c=='e' || c=='E' ){
 +        if( z[j-1]<'0' ){
 +          if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
 +            pParse->hasNonstd = 1;
 +            t |= 0x01;
 +          }else{
 +            pParse->iErr = j;
 +            return -1;
 +          }
 +        }
 +        if( seenE ){
 +          pParse->iErr = j;
 +          return -1;
 +        }
 +        t |= 0x02;
 +        seenE = 1;
 +        c = z[j+1];
 +        if( c=='+' || c=='-' ){
 +          j++;
 +          c = z[j+1];
 +        }
 +        if( c<'0' || c>'9' ){
 +          pParse->iErr = j;
 +          return -1;
 +        }
 +        continue;
 +      }
 +      break;
 +    }
 +    if( z[j-1]<'0' ){
 +      if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
 +        pParse->hasNonstd = 1;
 +        t |= 0x01;
 +      }else{
 +        pParse->iErr = j;
 +        return -1;
 +      }
 +    }
 +  parse_number_finish:
 +    assert( JSONB_INT+0x01==JSONB_INT5 );
 +    assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 );
 +    assert( JSONB_INT+0x02==JSONB_FLOAT );
 +    if( z[i]=='+' ) i++;
 +    jsonBlobAppendNodeType(pParse, JSONB_INT+t, j-i);
 +    jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j-i);
 +    return j;
 +  }
 +  case '}': {
 +    pParse->iErr = i;
 +    return -2;  /* End of {...} */
 +  }
 +  case ']': {
 +    pParse->iErr = i;
 +    return -3;  /* End of [...] */
 +  }
 +  case ',': {
 +    pParse->iErr = i;
 +    return -4;  /* List separator */
 +  }
 +  case ':': {
 +    pParse->iErr = i;
 +    return -5;  /* Object label/value separator */
 +  }
 +  case 0: {
 +    return 0;   /* End of file */
 +  }
 +  case 0x09:
 +  case 0x0a:
 +  case 0x0d:
 +  case 0x20: {
 +    do{
 +      i++;
 +    }while( fast_isspace(z[i]) );
 +    goto json_parse_restart;
 +  }
 +  case 0x0b:
 +  case 0x0c:
 +  case '/':
 +  case 0xc2:
 +  case 0xe1:
 +  case 0xe2:
 +  case 0xe3:
 +  case 0xef: {
 +    j = json5Whitespace(&z[i]);
 +    if( j>0 ){
 +      i += j;
 +      pParse->hasNonstd = 1;
 +      goto json_parse_restart;
 +    }
 +    pParse->iErr = i;
 +    return -1;
 +  }
 +  case 'n': {
 +    if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
 +      jsonBlobAppendOneByte(pParse, JSONB_NULL);
 +      return i+4;
 +    }
 +    /* fall-through into the default case that checks for NaN */
 +  }
 +  default: {
 +    u32 k;
 +    int nn;
 +    c = z[i];
 +    for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
 +      if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
 +      nn = aNanInfName[k].n;
 +      if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
 +        continue;
 +      }
 +      if( sqlite3Isalnum(z[i+nn]) ) continue;
 +      if( aNanInfName[k].eType==JSON_REAL ){
 +        jsonBlobAppendOneByte(pParse, JSONB_FLOAT | 0x50);
 +        jsonBlobAppendNBytes(pParse, (const u8*)"9e999", 5);
 +      }else{
 +        jsonBlobAppendOneByte(pParse, JSONB_NULL);
 +      }
 +      pParse->hasNonstd = 1;
 +      return i + nn;
 +    }
 +    pParse->iErr = i;
 +    return -1;  /* Syntax error */
 +  }
 +  } /* End switch(z[i]) */
 +}
 +
 +
 +/*
 +** Parse a complete JSON string.  Return 0 on success or non-zero if there
 +** are any errors.  If an error occurs, free all memory held by pParse,
 +** but not pParse itself.
 +**
 +** pParse must be initialized to an empty parse object prior to calling
 +** this routine.
 +*/
 +static int jsonConvertTextToBlob(
 +  JsonParse *pParse,           /* Initialize and fill this JsonParse object */
 +  sqlite3_context *pCtx        /* Report errors here */
 +){
 +  int i;
 +  const char *zJson = pParse->zJson;
 +  i = jsonXlateTextToBlob(pParse, 0);
 +  if( pParse->oom ) i = -1;
 +  if( i>0 ){
 +    assert( pParse->iDepth==0 );
 +    while( fast_isspace(zJson[i]) ) i++;
 +    if( zJson[i] ){
 +      i += json5Whitespace(&zJson[i]);
 +      if( zJson[i] ){
 +        jsonParseReset(pParse);
 +        return 1;
 +      }
 +      pParse->hasNonstd = 1;
 +    }
 +  }
 +  if( i<=0 ){
 +    if( ALWAYS(pCtx!=0) ){
 +      if( pParse->oom ){
 +        sqlite3_result_error_nomem(pCtx);
 +      }else{
 +        sqlite3_result_error(pCtx, "malformed JSON", -1);
 +      }
 +    }
 +    jsonParseReset(pParse);
 +    return 1;
 +  }
 +  return 0;
 +}
 +
 +/*
 +** The input string pStr is a well-formed JSON text string.  Convert
 +** this into the JSONB format and make it the return value of the
 +** SQL function.
 +*/
 +static void jsonReturnStringAsBlob(JsonString *pStr){
 +  JsonParse px;
 +  memset(&px, 0, sizeof(px));
 +  jsonStringTerminate(pStr);
 +  px.zJson = pStr->zBuf;
 +  px.nJson = pStr->nUsed;
 +  (void)jsonXlateTextToBlob(&px, 0);
 +  if( px.oom ){
 +    sqlite3_free(px.aBlob);
 +    sqlite3_result_error_nomem(pStr->pCtx);
 +  }else{
 +    sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, sqlite3_free);
 +  }
 +}
 +
 +/* The byte at index i is a node type-code.  This routine
 +** determines the payload size for that node and writes that
 +** payload size in to *pSz.  It returns the offset from i to the
 +** beginning of the payload.  Return 0 on error.
 +*/
 +static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){
 +  u8 x;
 +  u32 sz;
 +  u32 n;
 +  if( NEVER(i>pParse->nBlob) ){
 +    *pSz = 0;
 +    return 0;
 +  }
 +  x = pParse->aBlob[i]>>4;
 +  if( x<=11 ){
 +    sz = x;
 +    n = 1;
 +  }else if( x==12 ){
 +    if( i+1>=pParse->nBlob ){
 +      *pSz = 0;
 +      return 0;
 +    }
 +    sz = pParse->aBlob[i+1];
 +    n = 2;
 +  }else if( x==13 ){
 +    if( i+2>=pParse->nBlob ){
 +      *pSz = 0;
 +      return 0;
 +    }
 +    sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2];
 +    n = 3;
 +  }else{
 +    if( i+4>=pParse->nBlob ){
 +      *pSz = 0;
 +      return 0;
 +    }
 +    sz = (pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) +
 +         (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4];
 +    n = 5;
 +  }
 +  if( i+sz+n>pParse->nBlob ){
 +    sz = 0;
 +    n = 0;
 +  }
 +  *pSz = sz;
 +  return n;
 +}
 +
 +
 +/*
 +** Translate the binary JSONB representation of JSON beginning at
 +** pParse->aBlob[i] into a JSON text string.  Append the JSON
 +** text onto the end of pOut.  Return the index in pParse->aBlob[]
 +** of the first byte past the end of the element that is translated.
 +**
 +** If an error is detected in the BLOB input, the pOut->eErr flag
 +** might get set to JSTRING_MALFORMED.  But not all BLOB input errors
 +** are detected.  So a malformed JSONB input might either result
 +** in an error, or in incorrect JSON.
 +**
 +** The pOut->eErr JSTRING_OOM flag is set on a OOM.
 +*/
 +static u32 jsonXlateBlobToText(
 +  JsonParse *pParse,             /* the complete parse of the JSON */
 +  u32 i,                         /* Start rendering at this index */
 +  JsonString *pOut               /* Write JSON here */
 +){
 +  u32 sz, n, j, iEnd;
 +
 +  n = jsonbPayloadSize(pParse, i, &sz);
 +  if( n==0 ){
 +    pOut->eErr |= JSTRING_MALFORMED;
 +    return pParse->nBlob+1;
 +  }
 +  switch( pParse->aBlob[i] & 0x0f ){
 +    case JSONB_NULL: {
 +      jsonAppendRawNZ(pOut, "null", 4);
 +      return i+1;
 +    }
 +    case JSONB_TRUE: {
 +      jsonAppendRawNZ(pOut, "true", 4);
 +      return i+1;
 +    }
 +    case JSONB_FALSE: {
 +      jsonAppendRawNZ(pOut, "false", 5);
 +      return i+1;
 +    }
 +    case JSONB_INT:
 +    case JSONB_FLOAT: {
 +      jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
 +      break;
 +    }
 +    case JSONB_INT5: {  /* Integer literal in hexadecimal notation */
 +      u32 k = 2;
 +      sqlite3_uint64 u = 0;
 +      const char *zIn = (const char*)&pParse->aBlob[i+n];
 +      if( zIn[0]=='-' ){
 +        jsonAppendChar(pOut, '-');
 +        k++;
 +      }
 +      for(; k<sz; k++){
 +        if( !sqlite3Isxdigit(zIn[k]) ){
 +          pOut->eErr |= JSTRING_MALFORMED;
 +          break;
 +        }else{
 +          u = u*16 + sqlite3HexToInt(zIn[k]);
 +        }
 +      }
 +      jsonPrintf(100,pOut,"%llu",u);
 +      break;
 +    }
 +    case JSONB_FLOAT5: { /* Float literal missing digits beside "." */
 +      u32 k = 0;
 +      const char *zIn = (const char*)&pParse->aBlob[i+n];
 +      if( zIn[0]=='-' ){
 +        jsonAppendChar(pOut, '-');
 +        k++;
 +      }
 +      if( zIn[k]=='.' ){
 +        jsonAppendChar(pOut, '0');
 +      }
 +      for(; k<sz; k++){
 +        jsonAppendChar(pOut, zIn[k]);
 +        if( zIn[k]=='.' && (k+1==sz || !sqlite3Isdigit(zIn[k+1])) ){
 +          jsonAppendChar(pOut, '0');
 +        }
 +      }
 +      break;
 +    }
 +    case JSONB_TEXT:
 +    case JSONB_TEXTJ: {
 +      jsonAppendChar(pOut, '"');
 +      jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
 +      jsonAppendChar(pOut, '"');
 +      break;
 +    }
 +    case JSONB_TEXT5: {
 +      const char *zIn;
 +      u32 k;
 +      u32 sz2 = sz;
 +      zIn = (const char*)&pParse->aBlob[i+n];
 +      jsonAppendChar(pOut, '"');
 +      while( sz2>0 ){
 +        for(k=0; k<sz2 && zIn[k]!='\\'; k++){}
 +        if( k>0 ){
 +          jsonAppendRawNZ(pOut, zIn, k);
 +          if( k>=sz2 ){
 +            break;
 +          }
 +          zIn += k;
 +          sz2 -= k;
 +        }
 +        if( sz2<2 ){
 +          if( sz2>0 ) pOut->eErr |= JSTRING_MALFORMED;
 +          if( sz2==0 ) break;
 +        }
 +        assert( zIn[0]=='\\' );
 +        switch( (u8)zIn[1] ){
 +          case '\'':
 +            jsonAppendChar(pOut, '\'');
 +            break;
 +          case 'v':
 +            jsonAppendRawNZ(pOut, "\\u0009", 6);
 +            break;
 +          case 'x':
 +            if( sz2<2 ){
 +              pOut->eErr |= JSTRING_MALFORMED;
 +              sz2 = 0;
 +              break;
 +            }
 +            jsonAppendRawNZ(pOut, "\\u00", 4);
 +            jsonAppendRawNZ(pOut, &zIn[2], 2);
 +            zIn += 2;
 +            sz2 -= 2;
 +            break;
 +          case '0':
 +            jsonAppendRawNZ(pOut, "\\u0000", 6);
 +            break;
 +          case '\r':
 +            if( sz2>2 && zIn[2]=='\n' ){
 +              zIn++;
 +              sz2--;
 +            }
 +            break;
 +          case '\n':
 +            break;
 +          case 0xe2:
 +            /* '\' followed by either U+2028 or U+2029 is ignored as
 +            ** whitespace.  Not that in UTF8, U+2028 is 0xe2 0x80 0x29.
 +            ** U+2029 is the same except for the last byte */
 +            if( sz2<4
 +             || 0x80!=(u8)zIn[2]
 +             || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3])
 +            ){
 +              pOut->eErr |= JSTRING_MALFORMED;
 +              k = sz2;
 +              break;
 +            }
 +            zIn += 2;
 +            sz2 -= 2;
 +            break;
 +          default:
 +            jsonAppendRawNZ(pOut, zIn, 2);
 +            break;
 +        }
 +        if( sz2<2 ){
 +          sz2 = 0;
 +          pOut->eErr |= JSTRING_MALFORMED;
 +          break;
 +        }
 +        zIn += 2;
 +        sz2 -= 2;
 +      }
 +      jsonAppendChar(pOut, '"');
 +      break;
 +    }
 +    case JSONB_TEXTRAW: {
 +      jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz);
 +      break;
 +    }
 +    case JSONB_ARRAY: {
 +      jsonAppendChar(pOut, '[');
 +      j = i+n;
 +      iEnd = j+sz;
 +      while( j<iEnd ){
 +        j = jsonXlateBlobToText(pParse, j, pOut);
 +        jsonAppendChar(pOut, ',');
 +      }
 +      if( sz>0 ) pOut->nUsed--;
 +      jsonAppendChar(pOut, ']');
 +      break;
 +    }
 +    case JSONB_OBJECT: {
 +      int x = 0;
 +      jsonAppendChar(pOut, '{');
 +      j = i+n;
 +      iEnd = j+sz;
 +      while( j<iEnd ){
 +        j = jsonXlateBlobToText(pParse, j, pOut);
 +        jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
 +      }
 +      if( sz>0 ) pOut->nUsed--;
 +      jsonAppendChar(pOut, '}');
 +      break;
 +    }
 +
 +    default: {
 +      pOut->eErr |= JSTRING_MALFORMED;
 +      break;
 +    }
 +  }
 +  return i+n+sz;
 +}
 +
 +/* Return true if the input pJson
 +**
 +** For performance reasons, this routine does not do a detailed check of the
 +** input BLOB to ensure that it is well-formed.  Hence, false positives are
 +** possible.  False negatives should never occur, however.
 +*/
 +static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
 +  u32 sz, n;
 +  const u8 *aBlob;
 +  int nBlob;
 +  JsonParse s;
 +  if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
 +  nBlob = sqlite3_value_bytes(pJson);
 +  if( nBlob<1 ) return 0;
 +  aBlob = sqlite3_value_blob(pJson);
 +  if( aBlob==0 || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
 +  memset(&s, 0, sizeof(s));
 +  s.aBlob = (u8*)aBlob;
 +  s.nBlob = nBlob;
 +  n = jsonbPayloadSize(&s, 0, &sz);
 +  if( n==0 ) return 0;
 +  if( sz+n!=(u32)nBlob ) return 0;
 +  if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
 +  return sz+n==(u32)nBlob;
 +}
 +
 +/* Translate a single element of JSONB into the JsonNode format.  The
 +** first byte of the element to be translated is at pParse->aBlob[i].
 +** Return the index in pParse->aBlob[] of the first byte past the end
 +** of the JSONB element.  Append the JsonNode translation in
 +** pParse->aNode[], which is increased in size as necessary.
 +*/
 +static int jsonXlateBlobToNode(JsonParse *pParse, u32 i){
 +  u8 t;     /* Node type */
 +  u32 sz;   /* Node size */
 +  u32 x;    /* Index of payload start */
 +
 +  const char *zPayload;
 +  x = jsonbPayloadSize(pParse, i, &sz);
 +  if( x==0 ) return -1;
 +  t = pParse->zJson[i] & 0x0f;
 +  zPayload = &pParse->zJson[i+x];
 +  switch( t ){
 +    case JSONB_NULL: {
 +      if( sz>0 ) return -1;
 +      jsonParseAddNode(pParse, JSON_NULL, 0, 0);
 +      break;
 +    }
 +    case JSONB_TRUE: {
 +      if( sz>0 ) return -1;
 +      jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
 +      break;
 +    }
 +    case JSONB_FALSE: {
 +      if( sz>0 ) return -1;
 +      jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
 +      break;
 +    }
 +    case JSONB_INT: {
 +      if( sz==0 ) return -1;
 +      jsonParseAddNode(pParse, JSON_INT, sz, zPayload);
 +      break;
 +    }
 +    case JSONB_INT5: {
 +      if( sz==0 ) return -1;
 +      pParse->hasNonstd = 1;
 +      jsonParseAddNode(pParse, JSON_INT | (JNODE_JSON5<<8), sz, zPayload);
 +      break;
 +    }
 +    case JSONB_FLOAT: {
 +      if( sz==0 ) return -1;
 +      jsonParseAddNode(pParse, JSON_REAL, sz, zPayload);
 +      break;
 +    }
 +    case JSONB_FLOAT5: {
 +      if( sz==0 ) return -1;
 +      pParse->hasNonstd = 1;
 +      jsonParseAddNode(pParse, JSON_REAL | (JNODE_JSON5<<8), sz, zPayload);
 +      break;
 +    }
 +    case JSONB_TEXTRAW: {
 +      jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), sz, zPayload);
 +      break;
 +    }
 +    case JSONB_TEXT: {
 +      jsonParseAddNode(pParse, JSON_STRING, sz, zPayload);
 +      break;
 +    }
 +    case JSONB_TEXTJ: {
 +      jsonParseAddNode(pParse, JSON_STRING | (JNODE_ESCAPE<<8), sz, zPayload);
 +      break;
 +    }
 +    case JSONB_TEXT5: {
 +      pParse->hasNonstd = 1;
 +      jsonParseAddNode(pParse, JSON_STRING | ((JNODE_ESCAPE|JNODE_JSON5)<<8),
 +                       sz, zPayload);
 +      break;
 +    }
 +    case JSONB_ARRAY: {
 +      int iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
 +      u32 j = i+x;
 +      while( j<i+x+sz ){
 +        int r = jsonXlateBlobToNode(pParse, j);
 +        if( r<=0 ) return -1;
 +        j = (u32)r;
 +      }
 +      if( !pParse->oom ){
 +        pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
 +      }
 +      break;
 +    }
 +    case JSONB_OBJECT: {
 +      int iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
 +      u32 j = i+x, k = 0;
 +      while( j<i+x+sz ){
 +        int r = jsonXlateBlobToNode(pParse, j);
 +        if( r<=0 ) return -1;
 +        if( (k++&1)==0 && !pParse->oom ){
 +          pParse->aNode[pParse->nNode-1].jnFlags |= JNODE_LABEL;
 +        }
 +        j = (u32)r;
 +      }
 +      if( !pParse->oom ){
 +        pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
 +      }
 +      if( k&1 ) return -1;
 +      break;
 +    }
 +    default: {
 +      return -1;
 +    }
 +  }
 +  return i+x+sz;
 +}
 +
 +/*
 +** Translate pNode (which is always a node found in pParse->aNode[]) into 
 +** the JSONB representation and append the translation onto the end of the
 +** pOut->aBlob[] array.
 +*/
 +static void jsonXlateNodeToBlob(
 +  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, pParse->nJson*2);
 +      iStart = pOut->nBlob;
 +      for(;;){
 +        while( j<=pNode->n ){
 +          if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
 +            jsonXlateNodeToBlob(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, pParse->nJson*2);
 +      iStart = pOut->nBlob;
 +      for(;;){
 +        while( j<=pNode->n ){
 +          if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
 +            jsonXlateNodeToBlob(pParse, &pNode[j], pOut);
 +            jsonXlateNodeToBlob(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;
 +    }
 +  }
 +}
 +
 +/*
 +** Given that a JSONB_ARRAY object starts at offset i, return
 +** the number of entries in that array.
 +*/
 +static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
 +  u32 n, sz, i, iEnd;
 +  u32 k = 0;
 +  n = jsonbPayloadSize(pParse, iRoot, &sz);
 +  iEnd = iRoot+n+sz;
 +  for(i=iRoot+n; n>0 && i<iEnd; i+=sz+n, k++){
 +    n = jsonbPayloadSize(pParse, i, &sz);
 +  }
 +  return k;
 +}
 +
 +/*
 +** Edit the size of the element at iRoot by the amount in pParse->delta.
 +*/
 +static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){
 +  u32 sz;
 +  assert( pParse->delta==0 );
 +  (void)jsonbPayloadSize(pParse, iRoot, &sz);
 +  sz += pParse->delta;
 +  jsonBlobChangePayloadSize(pParse, iRoot, sz);
 +}
 +
 +/*
 +** Error returns from jsonLookupBlobStep()
 +*/
 +#define JSON_BLOB_ERROR      0xffffffff
 +#define JSON_BLOB_NOTFOUND   0xfffffffe
 +#define JSON_BLOB_PATHERROR  0xfffffffd
 +#define JSON_BLOB_ISERROR(x) ((x)>=JSON_BLOB_PATHERROR)
 +
 +/*
 +** Search along zPath to find the Json element specified.  Return an
 +** index into pParse->aBlob[] for the start of that element's value.
 +**
 +** Return JSON_BLOB_NOTFOUND if no such element exists.
 +*/
 +static u32 jsonLookupBlobStep(
 +  JsonParse *pParse,      /* The JSON to search */
 +  u32 iRoot,              /* Begin the search at this element of aBlob[] */
 +  const char *zPath       /* The path to search */
 +){
 +  u32 i, j, k, nKey, sz, n, iEnd, rc;
 +  const char *zKey;
 +  u8 x;
 +
 +  if( zPath[0]==0 ){
 +    if( pParse->eEdit && jsonBlobMakeEditable(pParse) ){
 +      n = jsonbPayloadSize(pParse, iRoot, &sz);
 +      sz += n;
 +      if( pParse->eEdit==JEDIT_DEL ){
 +        memmove(&pParse->aBlob[iRoot], &pParse->aBlob[iRoot+sz],
 +                pParse->nBlob - iRoot);
 +        pParse->nBlob -= n;
 +        pParse->delta = -(int)n;
 +      }else if( pParse->eEdit==JEDIT_INS ){
 +        /* Already exists, so json_insert() is a no-op */
 +      }else{
 +        /* json_set() or json_replace() */
 +        int d = (int)pParse->nIns - (int)sz;
 +        pParse->delta = d;
 +        if( d!=0 ){
 +          if( pParse->nBlob + d > pParse->nBlobAlloc ){
 +            jsonBlobExpand(pParse, pParse->nBlob+d);
 +            if( pParse->oom ) return iRoot;
 +          }
 +          memmove(&pParse->aBlob[iRoot+pParse->nIns],
 +                  &pParse->aBlob[iRoot+sz],
 +                  pParse->nBlob - iRoot - sz);
 +        }
 +        memcpy(&pParse->aBlob[iRoot], pParse->aIns, pParse->nIns);
 +      }
 +    }
 +    return iRoot;
 +  }
 +  if( zPath[0]=='.' ){
 +    x = pParse->aBlob[iRoot];
 +    zPath++;
 +    if( zPath[0]=='"' ){
 +      zKey = zPath + 1;
 +      for(i=1; zPath[i] && zPath[i]!='"'; i++){}
 +      nKey = i-1;
 +      if( zPath[i] ){
 +        i++;
 +      }else{
 +        return JSON_BLOB_PATHERROR;
 +      }
 +      testcase( nKey==0 );
 +    }else{
 +      zKey = zPath;
 +      for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
 +      nKey = i;
 +      if( nKey==0 ){
 +        return JSON_BLOB_PATHERROR;
 +      }
 +    }
 +    if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_BLOB_NOTFOUND;
 +    n = jsonbPayloadSize(pParse, iRoot, &sz);
 +    j = iRoot + n;
 +    iEnd = j+sz;
 +    while( j<iEnd ){
 +      x = pParse->aBlob[j] & 0x0f;
 +      if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return JSON_BLOB_ERROR;
 +      n = jsonbPayloadSize(pParse, j, &sz);
 +      if( n==0 ) return JSON_BLOB_ERROR;
 +      k = j+n;
 +      if( k+sz>=iEnd ) return JSON_BLOB_ERROR;
 +      if( sz==nKey && memcmp(&pParse->aBlob[k], zKey, nKey)==0 ){
 +        j = k+sz;
 +        if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR;
 +        n = jsonbPayloadSize(pParse, j, &sz);
 +        if( n==0 || j+n+sz>iEnd ) return JSON_BLOB_ERROR;
 +        rc = jsonLookupBlobStep(pParse, j, &zPath[i]);
 +        if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
 +        return rc;
 +      }
 +      j = k+sz;
 +      if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR;
 +      n = jsonbPayloadSize(pParse, j, &sz);
 +      if( n==0 ) return JSON_BLOB_ERROR;
 +      j += n+sz;
 +    }
 +    if( j>iEnd ) return JSON_BLOB_ERROR;
 +  }else if( zPath[0]=='[' ){
 +    x = pParse->aBlob[iRoot] & 0x0f;
 +    if( x!=JSONB_ARRAY )  return JSON_BLOB_NOTFOUND;
 +    n = jsonbPayloadSize(pParse, iRoot, &sz);
 +    k = 0;
 +    i = 1;
 +    while( sqlite3Isdigit(zPath[i]) ){
 +      k = k*10 + zPath[i] - '0';
 +      i++;
 +    }
 +    if( i<2 || zPath[i]!=']' ){
 +      if( zPath[1]=='#' ){
 +        k = jsonbArrayCount(pParse, iRoot);
 +        i = 2;
 +        if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
 +          unsigned int nn = 0;
 +          i = 3;
 +          do{
 +            nn = nn*10 + zPath[i] - '0';
 +            i++;
 +          }while( sqlite3Isdigit(zPath[i]) );
 +          if( nn>k ) return JSON_BLOB_NOTFOUND;
 +          k -= nn;
 +        }
 +        if( zPath[i]!=']' ){
 +          return JSON_BLOB_PATHERROR;
 +        }
 +      }else{
 +        return JSON_BLOB_PATHERROR;
 +      }
 +    }
 +    j = iRoot+n;
 +    iEnd = j+sz;
 +    while( j<iEnd ){
 +      if( k==0 ){
 +        rc = jsonLookupBlobStep(pParse, j, &zPath[i+1]);
 +        if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
 +        return rc;
 +      }
 +      k--;
 +      n = jsonbPayloadSize(pParse, j, &sz);
 +      if( n==0 ) return JSON_BLOB_ERROR;
 +      j += n+sz;
 +    }
 +    if( j>iEnd ) return JSON_BLOB_ERROR;
 +    if( k>1 ) return JSON_BLOB_NOTFOUND;
 +  }else{
 +    return JSON_BLOB_PATHERROR; 
 +  }
 +  if( pParse->eEdit==JEDIT_INS && jsonBlobMakeEditable(pParse) ){
 +    assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc );
 +    memmove(&pParse->aBlob[j], &pParse->aBlob[j+pParse->nIns],
 +            pParse->nBlob - j);
 +    memcpy(&pParse->aBlob[j], pParse->aIns, pParse->nIns);
 +    pParse->delta = pParse->nIns;
 +    pParse->nBlob += pParse->nIns;
 +    jsonAfterEditSizeAdjust(pParse, iRoot);
 +    return j;
 +  }
 +  return JSON_BLOB_NOTFOUND;
 +}
 +
 +/*
 +** Convert a JSON BLOB into text and make that text the return value
 +** of an SQL function.
 +*/
 +static void jsonReturnTextJsonFromBlob(
 +  sqlite3_context *ctx,
 +  const u8 *aBlob,
 +  u32 nBlob
 +){
 +  JsonParse x;
 +  JsonString s;
 +
 +  if( aBlob==0 ) return;
 +  memset(&x, 0, sizeof(x));
 +  x.aBlob = (u8*)aBlob;
 +  x.nBlob = nBlob;
 +  jsonStringInit(&s, ctx);
 +  jsonXlateBlobToText(&x, 0, &s);
 +  jsonReturnString(&s);
 +}
 +
 +
 +/*
 +** Return the value of the BLOB node at index i.
 +**
 +** If the value is a primitive, return it as an SQL value.
 +** If the value is an array or object, return it as either
 +** JSON text or the BLOB encoding, depending on the JSON_B flag
 +** on the userdata.
 +*/
 +static void jsonReturnFromBlob(
 +  JsonParse *pParse,          /* Complete JSON parse tree */
 +  u32 i,                      /* Index of the node */
 +  sqlite3_context *pCtx       /* Return value for this function */
 +){
 +  u32 n, sz;
 +  int rc;
 +  sqlite3 *db = sqlite3_context_db_handle(pCtx);
 +
 +  n = jsonbPayloadSize(pParse, i, &sz);
 +  if( n==0 ) return;
 +  switch( pParse->aBlob[i] & 0x0f ){
 +    case JSONB_NULL: {
 +      sqlite3_result_null(pCtx);
 +      break;
 +    }
 +    case JSONB_TRUE: {
 +      sqlite3_result_int(pCtx, 1);
 +      break;
 +    }
 +    case JSONB_FALSE: {
 +      sqlite3_result_int(pCtx, 0);
 +      break;
 +    }
 +    case JSONB_INT5:
 +    case JSONB_INT: {
 +      sqlite3_int64 iRes = 0;
 +      char *z;
 +      int bNeg = 0;
 +      char x = (char)pParse->aBlob[i+n];
 +      if( x=='-' && ALWAYS(sz>0) ){ n++; sz--; bNeg = 1; }
 +      z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
 +      if( z==0 ) return;
 +      rc = sqlite3DecOrHexToI64(z, &iRes);
 +      sqlite3DbFree(db, z);
 +      if( rc<=1 ){
 +        sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
 +      }else if( rc==3 && bNeg ){
 +        sqlite3_result_int64(pCtx, SMALLEST_INT64);
 +      }else{
 +        if( bNeg ){ n--; sz++; }
 +        goto to_double;
 +      }
 +      break;
 +    }
 +    case JSONB_FLOAT5:
 +    case JSONB_FLOAT: {
 +      double r;
 +      char *z;
 +    to_double:
 +      z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
 +      if( z==0 ) return;
 +      sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
 +      sqlite3DbFree(db, z);
 +      sqlite3_result_double(pCtx, r);
 +      break;
 +    }
 +    case JSONB_TEXTRAW:
 +    case JSONB_TEXT: {
 +      sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz,
 +                          SQLITE_TRANSIENT);
 +      break;
 +    }
 +    case JSONB_TEXT5:
 +    case JSONB_TEXTJ: {
 +      /* Translate JSON formatted string into raw text */
 +      u32 iIn, iOut;
 +      const char *z;
 +      char *zOut;
 +      u32 nOut = sz;
 +      z = (const char*)&pParse->aBlob[i+n];
 +      zOut = sqlite3_malloc( nOut+1 );
 +      if( zOut==0 ){
 +        sqlite3_result_error_nomem(pCtx);
 +        break;
 +      }
 +      for(iIn=iOut=0; iIn<sz; iIn++){
 +        char c = z[iIn];
 +        if( c=='\\' ){
 +          c = z[++iIn];
 +          if( c=='u' ){
 +            u32 v = jsonHexToInt4(z+iIn+1);
 +            iIn += 4;
 +            if( v==0 ) break;
 +            if( v<=0x7f ){
 +              zOut[iOut++] = (char)v;
 +            }else if( v<=0x7ff ){
 +              zOut[iOut++] = (char)(0xc0 | (v>>6));
 +              zOut[iOut++] = 0x80 | (v&0x3f);
 +            }else{
 +              u32 vlo;
 +              if( (v&0xfc00)==0xd800
 +                && i<n-6
 +                && z[iIn+1]=='\\'
 +                && z[iIn+2]=='u'
 +                && ((vlo = jsonHexToInt4(z+iIn+3))&0xfc00)==0xdc00
 +              ){
 +                /* We have a surrogate pair */
 +                v = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
 +                iIn += 6;
 +                zOut[iOut++] = 0xf0 | (v>>18);
 +                zOut[iOut++] = 0x80 | ((v>>12)&0x3f);
 +                zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
 +                zOut[iOut++] = 0x80 | (v&0x3f);
 +              }else{
 +                zOut[iOut++] = 0xe0 | (v>>12);
 +                zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
 +                zOut[iOut++] = 0x80 | (v&0x3f);
 +              }
 +            }
 +            continue;
 +          }else if( c=='b' ){
 +            c = '\b';
 +          }else if( c=='f' ){
 +            c = '\f';
 +          }else if( c=='n' ){
 +            c = '\n';
 +          }else if( c=='r' ){
 +            c = '\r';
 +          }else if( c=='t' ){
 +            c = '\t';
 +          }else if( c=='v' ){
 +            c = '\v';
 +          }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){
 +            /* pass through unchanged */
 +          }else if( c=='0' ){
 +            c = 0;
 +          }else if( c=='x' ){
 +            c = (jsonHexToInt(z[iIn+1])<<4) | jsonHexToInt(z[iIn+2]);
 +            iIn += 2;
 +          }else if( c=='\r' && z[i+1]=='\n' ){
 +            iIn++;
 +            continue;
 +          }else if( 0xe2==(u8)c ){
 +            assert( 0x80==(u8)z[i+1] );
 +            assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] );
 +            iIn += 2;
 +            continue;
 +          }else{
 +            continue;
 +          }
 +        } /* end if( c=='\\' ) */
 +        zOut[iOut++] = c;
 +      } /* end for() */
 +      zOut[iOut] = 0;
 +      sqlite3_result_text(pCtx, zOut, iOut, sqlite3_free);
 +      break;
 +    }
 +    case JSONB_ARRAY:
 +    case JSONB_OBJECT: {
 +      int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
 +      if( flags & JSON_BLOB ){
 +        sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
 +      }else{
 +        jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
 +      }
 +      break;
 +    }
 +    default: {
 +      sqlite3_result_error(pCtx, "malformed JSON", -1);
 +      break;
 +    }
 +  }
 +}
 +
 +/* Do a JSON_EXTRACT(JSON, PATH) on a when JSON is a BLOB.
 +*/
 +static void jsonExtractFromBlob(
 +  sqlite3_context *ctx,
 +  sqlite3_value *pJson,
 +  sqlite3_value *pPath,
 +  int flags
 +){
 +  const char *zPath = (const char*)sqlite3_value_text(pPath);
 +  u32 i = 0;
 +  JsonParse px;
 +  if( zPath==0 ) return;
 +  memset(&px, 0, sizeof(px));
 +  px.nBlob = sqlite3_value_bytes(pJson);
 +  px.aBlob = (u8*)sqlite3_value_blob(pJson);
 +  if( px.aBlob==0 ) return;
 +  if( zPath[0]=='$' ){
 +    zPath++;
 +    i = jsonLookupBlobStep(&px, 0, zPath);
 +  }else if( (flags & JSON_ABPATH) ){
 +    /* The -> and ->> operators accept abbreviated PATH arguments.  This
 +    ** is mostly for compatibility with PostgreSQL, but also for
 +    ** convenience.
 +    **
 +    **     NUMBER   ==>  $[NUMBER]     // PG compatible
 +    **     LABEL    ==>  $.LABEL       // PG compatible
 +    **     [NUMBER] ==>  $[NUMBER]     // Not PG.  Purely for convenience
 +    */
 +    JsonString jx;
 +    jsonStringInit(&jx, ctx);
 +    if( sqlite3Isdigit(zPath[0]) ){
 +      jsonAppendRawNZ(&jx, "[", 1);
 +      jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
 +      jsonAppendRawNZ(&jx, "]", 2);
 +      zPath = jx.zBuf;
 +    }else if( zPath[0]!='[' ){
 +      jsonAppendRawNZ(&jx, ".", 1);
 +      jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
 +      jsonAppendChar(&jx, 0);
 +      zPath = jx.zBuf;
 +    }
 +    i = jsonLookupBlobStep(&px, 0, zPath);
 +    jsonStringReset(&jx);
 +  }else{
 +    sqlite3_result_error(ctx, "bad path", -1);
 +    return;
 +  }
 +  if( i<px.nBlob ){
 +    jsonReturnFromBlob(&px, i, ctx);
 +  }else if( i==JSON_BLOB_NOTFOUND ){
 +    return;  /* Return NULL if not found */
 +  }else if( i==JSON_BLOB_ERROR ){
 +    sqlite3_result_error(ctx, "malformed JSON", -1);
 +  }else{
 +    char *zMsg = sqlite3_mprintf("bad path syntax: %s",
 +                    sqlite3_value_text(pPath));
 +    sqlite3_result_error(ctx, zMsg, -1);
 +    sqlite3_free(zMsg);
 +  }
 +}
 +  
 +
 +/****************************************************************************
 +** SQL functions used for testing and debugging
 +****************************************************************************/
 +
 +#if SQLITE_DEBUG
 +/*
 +** Print N node entries.
 +*/
 +static void jsonDebugPrintNodeEntries(
 +  JsonNode *aNode,  /* First node entry to print */
 +  int N             /* Number of node entries to print */
 +){
 +  int i;
 +  for(i=0; i<N; i++){
 +    const char *zType;
 +    if( aNode[i].jnFlags & JNODE_LABEL ){
 +      zType = "label";
 +    }else{
 +      zType = jsonType[aNode[i].eType];
 +    }
 +    printf("node %4u: %-7s n=%-5d", i, zType, aNode[i].n);
 +    if( (aNode[i].jnFlags & ~JNODE_LABEL)!=0 ){
 +      u8 f = aNode[i].jnFlags;
 +      if( f & JNODE_RAW )     printf(" RAW");
 +      if( f & JNODE_ESCAPE )  printf(" ESCAPE");
 +      if( f & JNODE_REMOVE )  printf(" REMOVE");
 +      if( f & JNODE_REPLACE ) printf(" REPLACE");
 +      if( f & JNODE_APPEND )  printf(" APPEND");
 +      if( f & JNODE_JSON5 )   printf(" JSON5");
 +    }
 +    switch( aNode[i].eU ){
 +      case 1:  printf(" zJContent=[%.*s]\n",
 +                      aNode[i].n, aNode[i].u.zJContent);           break;
 +      case 2:  printf(" iAppend=%u\n", aNode[i].u.iAppend);        break;
 +      case 3:  printf(" iKey=%u\n", aNode[i].u.iKey);              break;
 +      case 4:  printf(" iPrev=%u\n", aNode[i].u.iPrev);            break;
 +      default: printf("\n");
 +    }
 +  }
 +}
 +#endif /* SQLITE_DEBUG */
 +
 +
 +#if 0  /* 1 for debugging.  0 normally.  Requires -DSQLITE_DEBUG too */
 +static void jsonDebugPrintParse(JsonParse *p){
 +  jsonDebugPrintNodeEntries(p->aNode, p->nNode);
 +}
 +static void jsonDebugPrintNode(JsonNode *pNode){
 +  jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode));
 +}
 +#else
 +   /* The usual case */
 +# define jsonDebugPrintNode(X)
 +# define jsonDebugPrintParse(X)
 +#endif
 +
 +#ifdef SQLITE_DEBUG
 +/*
 +** SQL function:   json_parse(JSON)
 +**
 +** Parse JSON using jsonParseCached().  Then print a dump of that
 +** parse on standard output.  Return the mimified JSON result, just
 +** like the json() function.
 +*/
 +static void jsonParseFunc(
 +  sqlite3_context *ctx,
 +  int argc,
 +  sqlite3_value **argv
 +){
 +  JsonParse *p;        /* The parse */
 +
 +  assert( argc==1 );
 +  p = jsonParseCached(ctx, argv[0], ctx, 0);
 +  if( p==0 ) return;
 +  printf("nNode     = %u\n", p->nNode);
 +  printf("nAlloc    = %u\n", p->nAlloc);
 +  printf("nJson     = %d\n", p->nJson);
 +  printf("nAlt      = %d\n", p->nAlt);
 +  printf("nErr      = %u\n", p->nErr);
 +  printf("oom       = %u\n", p->oom);
 +  printf("hasNonstd = %u\n", p->hasNonstd);
 +  printf("useMod    = %u\n", p->useMod);
 +  printf("hasMod    = %u\n", p->hasMod);
 +  printf("nJPRef    = %u\n", p->nJPRef);
 +  printf("iSubst    = %u\n", p->iSubst);
 +  printf("iHold     = %u\n", p->iHold);
 +  jsonDebugPrintNodeEntries(p->aNode, p->nNode);
-   jsonReturnNodeAsJson(p, p->aNode, ctx, 1);
++  jsonReturnNodeAsJson(p, p->aNode, ctx, 1, 0);
 +}
 +
 +/*
 +** The json_test1(JSON) function return true (1) if the input is JSON
 +** text generated by another json function.  It returns (0) if the input
 +** is not known to be JSON.
  */
  static void jsonTest1Func(
    sqlite3_context *ctx,
@@@ -4570,15 -2581,14 +4578,14 @@@ static void jsonExtractFunc
        }
        if( pNode ){
          if( flags & JSON_JSON ){
-           jsonReturnNodeAsJson(p, pNode, ctx, 0);
 -          jsonReturnJson(p, pNode, ctx, 0, 0);
++          jsonReturnNodeAsJson(p, pNode, ctx, 0, 0);
          }else{
-           jsonReturnFromNode(p, pNode, ctx);
-           sqlite3_result_subtype(ctx, 0);
 -          jsonReturn(p, pNode, ctx, 1);
++          jsonReturnFromNode(p, pNode, ctx, 1);
          }
        }
      }else{
        pNode = jsonLookup(p, zPath, 0, ctx);
-       if( p->nErr==0 && pNode ) jsonReturnFromNode(p, pNode, ctx);
 -      if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx, 0);
++      if( p->nErr==0 && pNode ) jsonReturnFromNode(p, pNode, ctx, 0);
      }
    }else{
      /* Two or more PATH arguments results in a JSON array with each
@@@ -4702,15 -2710,13 +4709,15 @@@ static void jsonPatchFunc
    pX->useMod = 1;
    pY->useMod = 1;
    pResult = jsonMergePatch(pX, 0, pY->aNode);
 -  assert( pResult!=0 || pX->oom );
 -  if( pResult && pX->oom==0 ){
 +  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 ){
      jsonDebugPrintParse(pX);
      jsonDebugPrintNode(pResult);
-     jsonReturnNodeAsJson(pX, pResult, ctx, 0);
 -    jsonReturnJson(pX, pResult, ctx, 0, 0);
 -  }else{
 -    sqlite3_result_error_nomem(ctx);
++    jsonReturnNodeAsJson(pX, pResult, ctx, 0, 0);
    }
  }
  
@@@ -4787,7 -2793,7 +4794,7 @@@ static void jsonRemoveFunc
      }
    }
    if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){
-     jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1);
 -    jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
++    jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1, 0);
    }
  remove_done:
    jsonDebugPrintParse(p);
@@@ -4923,7 -2922,7 +4930,7 @@@ static void jsonReplaceFunc
        jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
      }
    }
-   jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1);
 -  jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
++  jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1, 0);
  replace_err:
    jsonDebugPrintParse(pParse);
    jsonParseFree(pParse);
@@@ -4978,7 -2976,7 +4985,7 @@@ static void jsonSetFunc
      }
    }
    jsonDebugPrintParse(pParse);
-   jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1);
 -  jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
++  jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1, 0);
  jsonSetDone:
    jsonParseFree(pParse);
  }
@@@ -5526,7 -3491,7 +5533,7 @@@ static int jsonEachColumn
      case JEACH_KEY: {
        if( p->i==0 ) break;
        if( p->eType==JSON_OBJECT ){
-         jsonReturnFromNode(&p->sParse, pThis, ctx);
 -        jsonReturn(&p->sParse, pThis, ctx, 0);
++        jsonReturnFromNode(&p->sParse, pThis, ctx, 0);
        }else if( p->eType==JSON_ARRAY ){
          u32 iKey;
          if( p->bRecursive ){
      }
      case JEACH_VALUE: {
        if( pThis->jnFlags & JNODE_LABEL ) pThis++;
-       jsonReturnFromNode(&p->sParse, pThis, ctx);
 -      jsonReturn(&p->sParse, pThis, ctx, 0);
++      jsonReturnFromNode(&p->sParse, pThis, ctx, 0);
        break;
      }
      case JEACH_TYPE: {
      case JEACH_ATOM: {
        if( pThis->jnFlags & JNODE_LABEL ) pThis++;
        if( pThis->eType>=JSON_ARRAY ) break;
-       jsonReturnFromNode(&p->sParse, pThis, ctx);
 -      jsonReturn(&p->sParse, pThis, ctx, 0);
++      jsonReturnFromNode(&p->sParse, pThis, ctx, 0);
        break;
      }
      case JEACH_ID: {
@@@ -5859,56 -3811,43 +5866,58 @@@ static sqlite3_module jsonTreeModule = 
  void sqlite3RegisterJsonFunctions(void){
  #ifndef SQLITE_OMIT_JSON
    static FuncDef aJsonFunc[] = {
-     /*                    Might return JSON text (subtype J)                 */
 -    /*                     calls sqlite3_result_subtype()                    */
--    /*                                  |                                    */
-     /*              Uses cache ------,  |  ,---- Returns JSONB               */
 -    /*              Uses cache ______   |   __ calls sqlite3_value_subtype() */
--    /*                               |  |  |                                 */
-     /*     Number of arguments ---,  |  |  |  ,--- Flags                     */
 -    /*          Num args _________   |  |  |   ___ Flags                     */
 -    /*                            |  |  |  |  |                              */
--    /*                            |  |  |  |  |                              */
--    JFUNCTION(json,               1, 1, 1, 0, 0,          jsonRemoveFunc),
-     JFUNCTION(jsonb,              1, 1, 0, 1, 0,          jsonbFunc),
-     JFUNCTION(json_array,        -1, 0, 1, 0, 0,          jsonArrayFunc),
-     JFUNCTION(jsonb_array,       -1, 0, 0, 1, 0,          jsonArrayFunc),
 -    JFUNCTION(json_array,        -1, 0, 1, 1, 0,          jsonArrayFunc),
--    JFUNCTION(json_array_length,  1, 1, 0, 0, 0,          jsonArrayLengthFunc),
--    JFUNCTION(json_array_length,  2, 1, 0, 0, 0,          jsonArrayLengthFunc),
--    JFUNCTION(json_error_position,1, 1, 0, 0, 0,          jsonErrorFunc),
--    JFUNCTION(json_extract,      -1, 1, 1, 0, 0,          jsonExtractFunc),
-     JFUNCTION(jsonb_extract,     -1, 1, 0, 1, 0,          jsonExtractFunc),
--    JFUNCTION(->,                 2, 1, 1, 0, JSON_JSON,  jsonExtractFunc),
--    JFUNCTION(->>,                2, 1, 0, 0, JSON_SQL,   jsonExtractFunc),
-     JFUNCTION(json_insert,       -1, 1, 1, 0, 0,          jsonSetFunc),
-     JFUNCTION(jsonb_insert,      -1, 1, 0, 1, 0,          jsonSetFunc),
-     JFUNCTION(json_object,       -1, 0, 1, 0, 0,          jsonObjectFunc),
-     JFUNCTION(jsonb_object,      -1, 0, 0, 1, 0,          jsonObjectFunc),
 -    JFUNCTION(json_insert,       -1, 1, 1, 1, 0,          jsonSetFunc),
 -    JFUNCTION(json_object,       -1, 0, 1, 1, 0,          jsonObjectFunc),
--    JFUNCTION(json_patch,         2, 1, 1, 0, 0,          jsonPatchFunc),
-     JFUNCTION(jsonb_patch,        2, 1, 0, 1, 0,          jsonPatchFunc),
-     JFUNCTION(json_quote,         1, 0, 1, 0, 0,          jsonQuoteFunc),
 -    JFUNCTION(json_quote,         1, 0, 1, 1, 0,          jsonQuoteFunc),
--    JFUNCTION(json_remove,       -1, 1, 1, 0, 0,          jsonRemoveFunc),
-     JFUNCTION(jsonb_remove,      -1, 1, 0, 1, 0,          jsonRemoveFunc),
-     JFUNCTION(json_replace,      -1, 1, 1, 0, 0,          jsonReplaceFunc),
-     JFUNCTION(jsonb_replace,     -1, 1, 0, 1, 0,          jsonReplaceFunc),
-     JFUNCTION(json_set,          -1, 1, 1, 0, JSON_ISSET, jsonSetFunc),
-     JFUNCTION(jsonb_set,         -1, 1, 0, 1, JSON_ISSET, jsonSetFunc),
 -    JFUNCTION(json_replace,      -1, 1, 1, 1, 0,          jsonReplaceFunc),
 -    JFUNCTION(json_set,          -1, 1, 1, 1, JSON_ISSET, jsonSetFunc),
--    JFUNCTION(json_type,          1, 1, 0, 0, 0,          jsonTypeFunc),
--    JFUNCTION(json_type,          2, 1, 0, 0, 0,          jsonTypeFunc),
--    JFUNCTION(json_valid,         1, 1, 0, 0, 0,          jsonValidFunc),
++    /*   sqlite3_result_subtype() ----,  ,--- sqlite3_value_subtype()       */
++    /*                                |  |                                  */
++    /*             Uses cache ------, |  | ,---- Returns JSONB              */
++    /*                              | |  | |                                */
++    /*     Number of arguments ---, | |  | | ,--- Flags                     */
++    /*                            | | |  | | |                              */
++    JFUNCTION(json,               1,1,1, 0,0,0,          jsonRemoveFunc),
++    JFUNCTION(jsonb,              1,1,0, 0,1,0,          jsonbFunc),
++    JFUNCTION(json_array,        -1,0,1, 1,0,0,          jsonArrayFunc),
++    JFUNCTION(jsonb_array,       -1,0,1, 1,1,0,          jsonArrayFunc),
++    JFUNCTION(json_array_length,  1,1,0, 0,0,0,          jsonArrayLengthFunc),
++    JFUNCTION(json_array_length,  2,1,0, 0,0,0,          jsonArrayLengthFunc),
++    JFUNCTION(json_error_position,1,1,0, 0,0,0,          jsonErrorFunc),
++    JFUNCTION(json_extract,      -1,1,1, 0,0,0,          jsonExtractFunc),
++    JFUNCTION(jsonb_extract,     -1,1,0, 0,1,0,          jsonExtractFunc),
++    JFUNCTION(->,                 2,1,1, 0,0,JSON_JSON,  jsonExtractFunc),
++    JFUNCTION(->>,                2,1,0, 0,0,JSON_SQL,   jsonExtractFunc),
++    JFUNCTION(json_insert,       -1,1,1, 1,0,0,          jsonSetFunc),
++    JFUNCTION(jsonb_insert,      -1,1,0, 1,1,0,          jsonSetFunc),
++    JFUNCTION(json_object,       -1,0,1, 1,0,0,          jsonObjectFunc),
++    JFUNCTION(jsonb_object,      -1,0,1, 1,1,0,          jsonObjectFunc),
++    JFUNCTION(json_patch,         2,1,1, 0,0,0,          jsonPatchFunc),
++    JFUNCTION(jsonb_patch,        2,1,0, 0,1,0,          jsonPatchFunc),
++    JFUNCTION(json_quote,         1,0,1, 1,0,0,          jsonQuoteFunc),
++    JFUNCTION(json_remove,       -1,1,1, 0,0,0,          jsonRemoveFunc),
++    JFUNCTION(jsonb_remove,      -1,1,0, 0,1,0,          jsonRemoveFunc),
++    JFUNCTION(json_replace,      -1,1,1, 1,0,0,          jsonReplaceFunc),
++    JFUNCTION(jsonb_replace,     -1,1,0, 1,1,0,          jsonReplaceFunc),
++    JFUNCTION(json_set,          -1,1,1, 1,0,JSON_ISSET, jsonSetFunc),
++    JFUNCTION(jsonb_set,         -1,1,0, 1,1,JSON_ISSET, jsonSetFunc),
++    JFUNCTION(json_type,          1,1,0, 0,0,0,          jsonTypeFunc),
++    JFUNCTION(json_type,          2,1,0, 0,0,0,          jsonTypeFunc),
++    JFUNCTION(json_valid,         1,1,0, 0,0,0,          jsonValidFunc),
  #if SQLITE_DEBUG
-     JFUNCTION(json_parse,         1, 1, 0, 0, 0,          jsonParseFunc),
-     JFUNCTION(json_test1,         1, 1, 0, 0, 0,          jsonTest1Func),
-     JFUNCTION(jsonb_test2,        1, 1, 0, 1, 0,          jsonbTest2),
 -    JFUNCTION(json_parse,         1, 1, 1, 0, 0,          jsonParseFunc),
 -    JFUNCTION(json_test1,         1, 1, 0, 1, 0,          jsonTest1Func),
++    JFUNCTION(json_parse,         1,1,0, 0,0,0,          jsonParseFunc),
++    JFUNCTION(json_test1,         1,1,0, 1,0,0,          jsonTest1Func),
++    JFUNCTION(jsonb_test2,        1,1,0, 0,1,0,          jsonbTest2),
  #endif
      WAGGREGATE(json_group_array,  1, 0, 0,
         jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
-        SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+        SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+        SQLITE_DETERMINISTIC),
 +    WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0,
 +       jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
-        SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
++       SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
      WAGGREGATE(json_group_object, 2, 0, 0,
-        SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
 +       jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
++       SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
 +    WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0,
         jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
-        SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC)
+        SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+        SQLITE_DETERMINISTIC)
    };
    sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
  #endif
diff --cc src/sqliteInt.h
index f14e67f1253ee3b0dd200ef987ed1c6abafb64cc,bb61cb6916afed6bd2054ad8a47dd0e1aee3b9d9..9e57354db8c970df6fb0725081cc7576ecf19d0c
@@@ -2113,10 -2114,11 +2114,11 @@@ struct FuncDestructor 
  #define MFUNCTION(zName, nArg, xPtr, xFunc) \
    {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
     xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
- #define JFUNCTION(zName, nArg, bUseCache, bSubtype, bJsonB, iArg, xFunc) \
 -#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, iArg, xFunc) \
++#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \
    {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
-    SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|((bSubtype)*SQLITE_SUBTYPE), \
+    SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
+    ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
 -   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
 +   SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} }
  #define INLINE_FUNC(zName, nArg, iArg, mFlags) \
    {nArg, SQLITE_FUNC_BUILTIN|\
     SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
diff --cc src/vdbe.c
index f13506a3ad06c92a7dcde5d3bfe1adf69cb7248a,544c8d84578b3b02b747c8afb7978cc5df37e11e..2b788e5e862b5ca65548c6c0f808681ac32a83a4
@@@ -8339,8 -8338,6 +8339,9 @@@ case OP_VColumn: {           /* ncycle 
    memset(&sContext, 0, sizeof(sContext));
    sContext.pOut = pDest;
    sContext.enc = encoding;
 +  nullFunc.pUserData = 0;
++  nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE;
 +  sContext.pFunc = &nullFunc;
    assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
    if( pOp->p5 & OPFLAG_NOCHNG ){
      sqlite3VdbeMemSetNull(pDest);
diff --cc src/vdbeapi.c
Simple merge