SQLite

Check-in [2dbb22c7]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Extend the enhancement to json_set() and json_replace(). Clean up cruft.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | json-opt-rcstr
Files: files | file ages | folders
SHA3-256: 2dbb22c75e86f2e3ced38ac14b4943570d5c2f86cd5e37e875bf0c863be28836
User & Date: drh 2023-07-26 01:05:08
Context
2023-07-26
01:12
Add the concept of an RCStr string - a "Reference Counted String". Use these to keep track of big JSON strings that might need to be used in multiple places at the same time with different lifetimes. Add two different JSON string values to each parse - one with and one without changes. The net result is a large reduction in the number of parses that occur when doing an UPDATE of a indexed JSON field using json_replace() or similar. (check-in: a4c1af61 user: drh tags: json-opt)
01:05
Extend the enhancement to json_set() and json_replace(). Clean up cruft. (Closed-Leaf check-in: 2dbb22c7 user: drh tags: json-opt-rcstr)
00:48
Add the JsonParse.zAlt field to old revised JSON text after a change. Demonstrate that this elminates the need for reparsing after a change by using it in the json_remove() function. This is an incremental check-in containing lots of cruft. (check-in: f930b139 user: drh tags: json-opt-rcstr)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/json.c.

1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
  sqlite3_context *pCtx,       /* Report errors here */
  char *zJson,                 /* Input JSON text to be parsed */
  int bTakeJson                /* Assume ownership of zJson if true */
){
  int i;
  memset(pParse, 0, sizeof(*pParse));
  if( zJson==0 ) return 1;
//printf("PARSE %s\n", zJson);
  pParse->zJson = zJson;
  pParse->bOwnsJson = bTakeJson;
  pParse->nJPRef = 1;
  i = jsonParseValue(pParse, 0);
  if( pParse->oom ) i = -1;
  if( i>0 ){
    assert( pParse->iDepth==0 );







<







1754
1755
1756
1757
1758
1759
1760

1761
1762
1763
1764
1765
1766
1767
  sqlite3_context *pCtx,       /* Report errors here */
  char *zJson,                 /* Input JSON text to be parsed */
  int bTakeJson                /* Assume ownership of zJson if true */
){
  int i;
  memset(pParse, 0, sizeof(*pParse));
  if( zJson==0 ) return 1;

  pParse->zJson = zJson;
  pParse->bOwnsJson = bTakeJson;
  pParse->nJPRef = 1;
  i = jsonParseValue(pParse, 0);
  if( pParse->oom ) i = -1;
  if( i>0 ){
    assert( pParse->iDepth==0 );
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
     && p->nJson==nJson
     && (p->hasMod==0 || bUnedited==0)
     && memcmp(p->zJson,zJson,nJson)==0
    ){
      p->nErr = 0;
      p->useMod = 0;
      pMatch = p;
//printf("HIT %s at %d\n", zJson, iKey);
    }else
    if( pMatch==0
     && p->zAlt!=0
     && bUnedited==0
     && p->nAlt==nJson
     && memcmp(p->zAlt, zJson, nJson)==0
    ){
      p->nErr = 0;
      p->useMod = 1;
      pMatch = p;
//printf("HIT %s at %d-alt\n", zJson, iKey);
    }else if( p->iHold<iMinHold ){
      iMinHold = p->iHold;
      iMinKey = iKey;
    }
    if( p->iHold>iMaxHold ){
      iMaxHold = p->iHold;
    }
  }
  if( pMatch ){
    /* The input JSON text was found in the cache.  Use the preexisting
    ** parse of this JSON */
    pMatch->nErr = 0;
    pMatch->iHold = iMaxHold+1;
    assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */
    return pMatch;
  }

  /* The input JSON was not found anywhere in the cache.  We will need
  ** to parse it ourselves and generate a new JsonParse object.
  */
//printf("MISS %s\n", zJson);
  p = sqlite3_malloc64( sizeof(*p) );
  if( p==0 ){
    sqlite3_result_error_nomem(pCtx);
    return 0;
  }
  memset(p, 0, sizeof(*p));
  p->zJson = sqlite3RCStrNew( nJson );







<










<




















<







1891
1892
1893
1894
1895
1896
1897

1898
1899
1900
1901
1902
1903
1904
1905
1906
1907

1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927

1928
1929
1930
1931
1932
1933
1934
     && p->nJson==nJson
     && (p->hasMod==0 || bUnedited==0)
     && memcmp(p->zJson,zJson,nJson)==0
    ){
      p->nErr = 0;
      p->useMod = 0;
      pMatch = p;

    }else
    if( pMatch==0
     && p->zAlt!=0
     && bUnedited==0
     && p->nAlt==nJson
     && memcmp(p->zAlt, zJson, nJson)==0
    ){
      p->nErr = 0;
      p->useMod = 1;
      pMatch = p;

    }else if( p->iHold<iMinHold ){
      iMinHold = p->iHold;
      iMinKey = iKey;
    }
    if( p->iHold>iMaxHold ){
      iMaxHold = p->iHold;
    }
  }
  if( pMatch ){
    /* The input JSON text was found in the cache.  Use the preexisting
    ** parse of this JSON */
    pMatch->nErr = 0;
    pMatch->iHold = iMaxHold+1;
    assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */
    return pMatch;
  }

  /* The input JSON was not found anywhere in the cache.  We will need
  ** to parse it ourselves and generate a new JsonParse object.
  */

  p = sqlite3_malloc64( sizeof(*p) );
  if( p==0 ){
    sqlite3_result_error_nomem(pCtx);
    return 0;
  }
  memset(p, 0, sizeof(*p));
  p->zJson = sqlite3RCStrNew( nJson );
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882

2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
** this routine is a no-op.  If JSON or PATH is malformed, throw an error.
*/
static void jsonReplaceFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse x;          /* The parse */
  JsonNode *pNode;
  const char *zPath;
  u32 i;

  if( argc<1 ) return;
  if( (argc&1)==0 ) {
    jsonWrongNumArgs(ctx, "replace");
    return;
  }
  if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0]), 0) ) return;
  assert( x.nNode );
  for(i=1; i<(u32)argc; i+=2){
    zPath = (const char*)sqlite3_value_text(argv[i]);

    pNode = jsonLookup(&x, zPath, 0, ctx);
    if( x.nErr ) goto replace_err;
    if( pNode ){
      jsonReplaceNode(ctx, &x, (u32)(pNode - x.aNode), argv[i+1]);
    }
  }
  jsonReturnJson(&x, x.aNode, ctx, 0);
replace_err:
  jsonDebugPrintParse(&x);
  jsonParseReset(&x);
}


/*
** json_set(JSON, PATH, VALUE, ...)
**
** Set the value at PATH to VALUE.  Create the PATH if it does not already
** exist.  Overwrite existing values that do exist.
** If JSON or PATH is malformed, throw an error.
**
** json_insert(JSON, PATH, VALUE, ...)
**
** Create PATH and initialize it to VALUE.  If PATH already exists, this
** routine is a no-op.  If JSON or PATH is malformed, throw an error.
*/
static void jsonSetFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse x;          /* The parse */
  JsonNode *pNode;
  const char *zPath;
  u32 i;
  int bApnd;
  int bIsSet = sqlite3_user_data(ctx)!=0;

  if( argc<1 ) return;
  if( (argc&1)==0 ) {
    jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
    return;
  }
  if( jsonParse(&x, ctx, (char*)sqlite3_value_text(argv[0]), 0) ) return;
  assert( x.nNode );
  for(i=1; i<(u32)argc; i+=2){
    zPath = (const char*)sqlite3_value_text(argv[i]);
    bApnd = 0;
    x.useMod = 1;
    pNode = jsonLookup(&x, zPath, &bApnd, ctx);
    if( x.oom ){
      sqlite3_result_error_nomem(ctx);
      goto jsonSetDone;
    }else if( x.nErr ){
      goto jsonSetDone;
    }else if( pNode && (bApnd || bIsSet) ){
      jsonReplaceNode(ctx, &x, (u32)(pNode - x.aNode), argv[i+1]);
    }
  }
  jsonDebugPrintParse(&x);
  jsonReturnJson(&x, x.aNode, ctx, 0);

jsonSetDone:
  jsonParseReset(&x);
}

/*
** json_type(JSON)
** json_type(JSON, PATH)
**
** Return the top-level "type" of a JSON string.  json_type() raises an







|









|
|


>
|
|

|


|

|
<




















|











|
|



|
|
|


|


|


|
|


|







2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888

2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
** this routine is a no-op.  If JSON or PATH is malformed, throw an error.
*/
static void jsonReplaceFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *pParse;          /* The parse */
  JsonNode *pNode;
  const char *zPath;
  u32 i;

  if( argc<1 ) return;
  if( (argc&1)==0 ) {
    jsonWrongNumArgs(ctx, "replace");
    return;
  }
  pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
  if( pParse==0 ) return;
  for(i=1; i<(u32)argc; i+=2){
    zPath = (const char*)sqlite3_value_text(argv[i]);
    pParse->useMod = 1;
    pNode = jsonLookup(pParse, zPath, 0, ctx);
    if( pParse->nErr ) goto replace_err;
    if( pNode ){
      jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
    }
  }
  jsonReturnJson(pParse, pParse->aNode, ctx, 1);
replace_err:
  jsonDebugPrintParse(pParse);

}


/*
** json_set(JSON, PATH, VALUE, ...)
**
** Set the value at PATH to VALUE.  Create the PATH if it does not already
** exist.  Overwrite existing values that do exist.
** If JSON or PATH is malformed, throw an error.
**
** json_insert(JSON, PATH, VALUE, ...)
**
** Create PATH and initialize it to VALUE.  If PATH already exists, this
** routine is a no-op.  If JSON or PATH is malformed, throw an error.
*/
static void jsonSetFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *pParse;       /* The parse */
  JsonNode *pNode;
  const char *zPath;
  u32 i;
  int bApnd;
  int bIsSet = sqlite3_user_data(ctx)!=0;

  if( argc<1 ) return;
  if( (argc&1)==0 ) {
    jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
    return;
  }
  pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
  if( pParse==0 ) return;
  for(i=1; i<(u32)argc; i+=2){
    zPath = (const char*)sqlite3_value_text(argv[i]);
    bApnd = 0;
    pParse->useMod = 1;
    pNode = jsonLookup(pParse, zPath, &bApnd, ctx);
    if( pParse->oom ){
      sqlite3_result_error_nomem(ctx);
      goto jsonSetDone;
    }else if( pParse->nErr ){
      goto jsonSetDone;
    }else if( pNode && (bApnd || bIsSet) ){
      jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
    }
  }
  jsonDebugPrintParse(pParse);
  jsonReturnJson(pParse, pParse->aNode, ctx, 1);

jsonSetDone:
  /* no cleanup required */;
}

/*
** json_type(JSON)
** json_type(JSON, PATH)
**
** Return the top-level "type" of a JSON string.  json_type() raises an

Changes to src/printf.c.

1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
** reference count reaches zero.
*/
void sqlite3RCStrUnref(char *z){
  RCStr *p = (RCStr*)z;
  assert( p!=0 );
  p--;
  assert( p->nRCRef>0 );
  assert( p->uMagic==SQLITE_RCSTR_MAGIC );
  if( p->nRCRef>=2 ){
    p->nRCRef--;
  }else{
    if( p->xFree ) p->xFree(p->pAttach);
#ifdef SQLITE_DEBUG
    p->uMagic = 0;
#endif
    sqlite3_free(p);
  }
}

#if 0
/*
** Return true if the reference count on the string is exactly one, meaning
** that the string can be modified.  Return false if the reference count
** is greater than one.
*/
int sqlite3RCStrIsWriteable(char *z){
  RCStr *p = (RCStr*)z;
  assert( p!=0 );
  p--;
  assert( p->nRCRef>0 );
  assert( p->uMagic==SQLITE_RCSTR_MAGIC );
  return p->nRCRef==1;
}
#endif

/*
** Create a new string that is capable of holding N bytes of text, not counting
** the zero byte at the end.  The string is uninitialized.
**
** The reference count is initially 1.  Call sqlite3RCStrUnref() to free the
** newly allocated string.
**
** This routine returns 0 on an OOM.
*/
char *sqlite3RCStrNew(u64 N){
  RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 );
  if( p==0 ) return 0;
  p->nRCRef = 1;
  p->xFree = 0;
  p->pAttach = 0;
#ifdef SQLITE_DEBUG
  p->uMagic = SQLITE_RCSTR_MAGIC;
#endif
  return (char*)&p[1];
}

#if 0
/*
** Return the number of bytes allocated to the string.  The value returned
** does not include the space for the zero-terminator at the end.
*/
u64 sqlite3RCStrSize(char *z){
  RCStr *p = (RCStr*)z;
  u64 N;
  assert( p!=0 );
  p--;
  assert( p->nRCRef>0 );
  assert( p->uMagic==SQLITE_RCSTR_MAGIC );
  N = sqlite3_msize(p);
  N -= sizeof(p) + 1;
  return N;
}
#endif

/*
** Change the size of the string so that it is able to hold N bytes.
** The string might be reallocated, so return the new allocation.
*/
char *sqlite3RCStrResize(char *z, u64 N){
  RCStr *p = (RCStr*)z;
  RCStr *pNew;
  assert( p!=0 );
  p--;
  assert( p->nRCRef==1 );
  assert( p->uMagic==SQLITE_RCSTR_MAGIC );
  pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1);
  if( pNew==0 ){
    sqlite3_free(p);
    return 0;
  }else{
    return (char*)&pNew[1];
  }
}

#if 0
/*
** Add a new attachment to the string.
**
** A string may have no more than one attachment.  When a new attachment
** is added, any prior attachment is destroyed.  Remove an attachment
** by adding a zero-attachment.
*/
void sqlite3RCStrAttach(char *z, void *pAttach, void(*xFree)(void*)){
  RCStr *p = (RCStr*)z;
  assert( p!=0 );
  p--;
  assert( p->nRCRef>0 );
  assert( p->uMagic==SQLITE_RCSTR_MAGIC );
  if( p->xFree ) p->xFree(p->pAttach);
  p->xFree = xFree;
  p->pAttach = pAttach;
}
#endif

#if 0
/*
** Return the attachment associated with a string if the attachment
** has the destructure specified in the second argument.  If the
** string has no attachment or if the destructor does not match,
** then return a NULL pointr.
*/
void *sqlite3RCStrGetAttachment(char *z, void(*xFree)(void*)){
  RCStr *p = (RCStr*)z;
  assert( p!=0 );
  p--;
  assert( p->nRCRef>0 );
  assert( p->uMagic==SQLITE_RCSTR_MAGIC );
  return p->xFree==xFree ? p->pAttach : 0;
}
#endif







<



<
<
<
<




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<













<
<
<
<
<



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










<








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
1390
1391
1392
1393
1394
1395
1396

1397
1398
1399




1400
1401
1402
1403
















1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416





1417
1418
1419


















1420
1421
1422
1423
1424
1425
1426
1427
1428
1429

1430
1431
1432
1433
1434
1435
1436
1437





































** reference count reaches zero.
*/
void sqlite3RCStrUnref(char *z){
  RCStr *p = (RCStr*)z;
  assert( p!=0 );
  p--;
  assert( p->nRCRef>0 );

  if( p->nRCRef>=2 ){
    p->nRCRef--;
  }else{




    sqlite3_free(p);
  }
}

















/*
** Create a new string that is capable of holding N bytes of text, not counting
** the zero byte at the end.  The string is uninitialized.
**
** The reference count is initially 1.  Call sqlite3RCStrUnref() to free the
** newly allocated string.
**
** This routine returns 0 on an OOM.
*/
char *sqlite3RCStrNew(u64 N){
  RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 );
  if( p==0 ) return 0;
  p->nRCRef = 1;





  return (char*)&p[1];
}



















/*
** Change the size of the string so that it is able to hold N bytes.
** The string might be reallocated, so return the new allocation.
*/
char *sqlite3RCStrResize(char *z, u64 N){
  RCStr *p = (RCStr*)z;
  RCStr *pNew;
  assert( p!=0 );
  p--;
  assert( p->nRCRef==1 );

  pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1);
  if( pNew==0 ){
    sqlite3_free(p);
    return 0;
  }else{
    return (char*)&pNew[1];
  }
}





































Changes to src/sqliteInt.h.

4062
4063
4064
4065
4066
4067
4068
4069
4070
4071

4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100

4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112

#define isMalloced(X)  (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0)

/*
** The following object is the header for an "RCStr" or "reference-counted
** string".  An RCStr is passed around and used like any other char*
** that has been dynamically allocated.  The important interface
** difference is that it uses sqlite3RCStrUnref() as its destructor
** rather than sqlite3_free().  Other than that the two are interchangeable.
**

** Thus to return an RCStr object as the result of an SQL function use:
** 
**    sqlite3_result_text64(ctx,z,sz,sqlite3RCStrUnref,SQLITE_UTF8)
**                                   ^^^^^^^^^^^^^^^^^
**                                   Instead of sqlite3_free() or similar
**
** An SQL function can check its arguments to see if they are RCStr
** strings using the sqlite3ValueIsOfClass() function:
**
**    sqlite3ValueIsOfClass(argv[i], sqlite3RCStrUnref);
**
** An RCStr string might be better than an ordinary string in some cases
** because:
**
**    (1)  You can duplicate it using sqlite3RCStrRef(x).
**
**    (2)  You can also add an associated object to the string.  For
**         example, if the string is JSON, perhaps the associated object
**         is a parse of that JSON.
**
** Methods for an RCStr string begin with "sqlite3RCStr...".
*/
struct RCStr {
  u32 nRCRef;            /* Number of references */
#ifdef SQLITE_DEBUG
  u32 uMagic;            /* Magic number for sanity checking */
#endif
  void *pAttach;         /* Attachment to this string */
  void (*xFree)(void*);  /* Destructor for the attachment */

};

/* The Magic number used by RCStr for sanity checking.  SQLITE_DEBUG only. */
#define SQLITE_RCSTR_MAGIC 0x3dc05d54


/*
** A pointer to this structure is used to communicate information
** from sqlite3Init and OP_ParseSchema into the sqlite3InitCallback.
*/
typedef struct {
  sqlite3 *db;        /* The database being initialized */







|
<

>
|
|
|
<
|

<
<
<
<
<
|
<
<
|
<
<
<
<
<
<


|
<
<
<
<
<
>

<
<
<
<







4062
4063
4064
4065
4066
4067
4068
4069

4070
4071
4072
4073
4074

4075
4076





4077


4078






4079
4080
4081





4082
4083




4084
4085
4086
4087
4088
4089
4090

#define isMalloced(X)  (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0)

/*
** The following object is the header for an "RCStr" or "reference-counted
** string".  An RCStr is passed around and used like any other char*
** that has been dynamically allocated.  The important interface
** differences:

**
**   1.  RCStr strings are reference counted.  They are deallocated
**       when the reference count reaches zero.
**
**   2.  Use sqlite3RCStrUnref() to free an RCStr string rather than

**       sqlite3_free()
**





**   3.  Make a (read-only) copy of a read-only RCStr string using


**       sqlite3RCStrRef().






*/
struct RCStr {
  u64 nRCRef;            /* Number of references */





  /* Total structure size should be a multiple of 8 bytes for alignment */
};





/*
** A pointer to this structure is used to communicate information
** from sqlite3Init and OP_ParseSchema into the sqlite3InitCallback.
*/
typedef struct {
  sqlite3 *db;        /* The database being initialized */
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
void sqlite3FileSuffix3(const char*, char*);
#else
# define sqlite3FileSuffix3(X,Y)
#endif
u8 sqlite3GetBoolean(const char *z,u8);

const void *sqlite3ValueText(sqlite3_value*, u8);
//int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*));
int sqlite3ValueBytes(sqlite3_value*, u8);
void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
                        void(*)(void*));
void sqlite3ValueSetNull(sqlite3_value*);
void sqlite3ValueFree(sqlite3_value*);
#ifndef SQLITE_UNTESTABLE
void sqlite3ResultIntReal(sqlite3_context*);







<







5196
5197
5198
5199
5200
5201
5202

5203
5204
5205
5206
5207
5208
5209
void sqlite3FileSuffix3(const char*, char*);
#else
# define sqlite3FileSuffix3(X,Y)
#endif
u8 sqlite3GetBoolean(const char *z,u8);

const void *sqlite3ValueText(sqlite3_value*, u8);

int sqlite3ValueBytes(sqlite3_value*, u8);
void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
                        void(*)(void*));
void sqlite3ValueSetNull(sqlite3_value*);
void sqlite3ValueFree(sqlite3_value*);
#ifndef SQLITE_UNTESTABLE
void sqlite3ResultIntReal(sqlite3_context*);
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
void sqlite3OomClear(sqlite3*);
int sqlite3ApiExit(sqlite3 *db, int);
int sqlite3OpenTempDatabase(Parse *);

char *sqlite3RCStrRef(char*);
void sqlite3RCStrUnref(char*);
char *sqlite3RCStrNew(u64);
//u64 sqlite3RCStrSize(char*);
char *sqlite3RCStrResize(char*,u64);
//int sqlite3RCStrIsWriteable(char*);
//void sqlite3RCStrAttach(char*, void*, void(*)(void*));
//void *sqlite3RCStrGetAttachment(char*,void(*)(void*));

void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
int sqlite3StrAccumEnlarge(StrAccum*, i64);
char *sqlite3StrAccumFinish(StrAccum*);
void sqlite3StrAccumSetError(StrAccum*, u8);
void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*);
void sqlite3SelectDestInit(SelectDest*,int,int);







<

<
<
<







5306
5307
5308
5309
5310
5311
5312

5313



5314
5315
5316
5317
5318
5319
5320
void sqlite3OomClear(sqlite3*);
int sqlite3ApiExit(sqlite3 *db, int);
int sqlite3OpenTempDatabase(Parse *);

char *sqlite3RCStrRef(char*);
void sqlite3RCStrUnref(char*);
char *sqlite3RCStrNew(u64);

char *sqlite3RCStrResize(char*,u64);




void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
int sqlite3StrAccumEnlarge(StrAccum*, i64);
char *sqlite3StrAccumFinish(StrAccum*);
void sqlite3StrAccumSetError(StrAccum*, u8);
void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*);
void sqlite3SelectDestInit(SelectDest*,int,int);

Changes to src/vdbemem.c.

1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
  }
  if( pVal->flags&MEM_Null ){
    return 0;
  }
  return valueToText(pVal, enc);
}

#if 0
/* Return true if sqlit3_value object pVal is a string or blob value
** that uses the destructor specified in the second argument.
**
** TODO:  Maybe someday promote this interface into a published API so
** that third-party extensions can get access to it?
*/
int sqlite3ValueIsOfClass(const sqlite3_value *pVal, void(*xFree)(void*)){
  if( ALWAYS(pVal!=0)
   && (pVal->flags & (MEM_Str|MEM_Blob))!=0
   && (pVal->flags & MEM_Dyn)!=0
   && pVal->xDel==xFree
  ){
    return 1;
  }else{
    return 0;
  }
}
#endif

/*
** Create a new sqlite3_value object.
*/
sqlite3_value *sqlite3ValueNew(sqlite3 *db){
  Mem *p = sqlite3DbMallocZero(db, sizeof(*p));
  if( p ){
    p->flags = MEM_Null;







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







1364
1365
1366
1367
1368
1369
1370




















1371
1372
1373
1374
1375
1376
1377
  }
  if( pVal->flags&MEM_Null ){
    return 0;
  }
  return valueToText(pVal, enc);
}





















/*
** Create a new sqlite3_value object.
*/
sqlite3_value *sqlite3ValueNew(sqlite3 *db){
  Mem *p = sqlite3DbMallocZero(db, sizeof(*p));
  if( p ){
    p->flags = MEM_Null;