SQLite

Check-in [4aab781d2a]
Login

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

Overview
Comment:Experimentally add another output pointer to the text/blob_v2() family which gets the sqlite3_value_type() assigned to it if it's not NULL, based on feedback in the forum.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | column-text-blob-v2
Files: files | file ages | folders
SHA3-256: 4aab781d2abc64a39c53acd0faf6d60bb38afcce42f25bc8c0fa52b73df05312
User & Date: stephan 2025-07-01 16:38:58.203
Context
2025-07-01
17:38
Random JS cleanups and docs. (check-in: 5a2734bbf8 user: stephan tags: column-text-blob-v2)
16:38
Experimentally add another output pointer to the text/blob_v2() family which gets the sqlite3_value_type() assigned to it if it's not NULL, based on feedback in the forum. (check-in: 4aab781d2a user: stephan tags: column-text-blob-v2)
14:44
Simplify the column_text/blob_v2() JS tests a bit and correct the text_v2() tests to call into both the column and value variants. (check-in: d5cb808043 user: stephan tags: column-text-blob-v2)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/wasm/api/sqlite3-api-glue.c-pp.js.
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    /* sqlite3_cancel_auto_extension() has a hand-written binding. */
    /* sqlite3_close_v2() is implemented by hand to perform some
       extra work. */
    ["sqlite3_changes", "int", "sqlite3*"],
    ["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
    ["sqlite3_collation_needed", "int", "sqlite3*", "*", "*"/*=>v(ppis)*/],
    ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"],
    ["sqlite3_column_blob_v2", "int", "sqlite3_stmt*", "int", "**", "*"],
    ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_count", "int", "sqlite3_stmt*"],
    ["sqlite3_column_decltype", "string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"],
    ["sqlite3_column_int","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_name","string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_text","string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_text_v2", "int", "sqlite3_stmt*", "int", "**", "*"],
    ["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_value","sqlite3_value*", "sqlite3_stmt*", "int"],
    ["sqlite3_commit_hook", "void*", [
      "sqlite3*",
      new wasm.xWrap.FuncPtrAdapter({
        name: 'sqlite3_commit_hook',
        signature: 'i(p)',







|







|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    /* sqlite3_cancel_auto_extension() has a hand-written binding. */
    /* sqlite3_close_v2() is implemented by hand to perform some
       extra work. */
    ["sqlite3_changes", "int", "sqlite3*"],
    ["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
    ["sqlite3_collation_needed", "int", "sqlite3*", "*", "*"/*=>v(ppis)*/],
    ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"],
    ["sqlite3_column_blob_v2", "int", "sqlite3_stmt*", "int", "**", "*", "*"],
    ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_count", "int", "sqlite3_stmt*"],
    ["sqlite3_column_decltype", "string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"],
    ["sqlite3_column_int","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_name","string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_text","string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_text_v2", "int", "sqlite3_stmt*", "int", "**", "*", "*"],
    ["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_value","sqlite3_value*", "sqlite3_stmt*", "int"],
    ["sqlite3_commit_hook", "void*", [
      "sqlite3*",
      new wasm.xWrap.FuncPtrAdapter({
        name: 'sqlite3_commit_hook',
        signature: 'i(p)',
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
       their first C-string arguments, so we cannot perform any value
       conversion on those. */
    ["sqlite3_uri_boolean", "int", "sqlite3_filename", "string", "int"],
    ["sqlite3_uri_key", "string", "sqlite3_filename", "int"],
    ["sqlite3_uri_parameter", "string", "sqlite3_filename", "string"],
    ["sqlite3_user_data","void*", "sqlite3_context*"],
    ["sqlite3_value_blob", "*", "sqlite3_value*"],
    ["sqlite3_value_blob_v2", "int", "sqlite3_value*", "**", "*"],
    ["sqlite3_value_bytes","int", "sqlite3_value*"],
    ["sqlite3_value_double","f64", "sqlite3_value*"],
    ["sqlite3_value_dup", "sqlite3_value*", "sqlite3_value*"],
    ["sqlite3_value_free", undefined, "sqlite3_value*"],
    ["sqlite3_value_frombind", "int", "sqlite3_value*"],
    ["sqlite3_value_int","int", "sqlite3_value*"],
    ["sqlite3_value_nochange", "int", "sqlite3_value*"],
    ["sqlite3_value_numeric_type", "int", "sqlite3_value*"],
    ["sqlite3_value_pointer", "*", "sqlite3_value*", "string:static"],
    ["sqlite3_value_subtype", "int", "sqlite3_value*"],
    ["sqlite3_value_text", "string", "sqlite3_value*"],
    ["sqlite3_value_text_v2", "int", "sqlite3_value*", "**", "*"],
    ["sqlite3_value_type", "int", "sqlite3_value*"],
    ["sqlite3_vfs_find", "*", "string"],
    ["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"],
    ["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"]
  ]/*wasm.bindingSignatures*/;

  if( !!wasm.exports.sqlite3_progress_handler ){







|











|







303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
       their first C-string arguments, so we cannot perform any value
       conversion on those. */
    ["sqlite3_uri_boolean", "int", "sqlite3_filename", "string", "int"],
    ["sqlite3_uri_key", "string", "sqlite3_filename", "int"],
    ["sqlite3_uri_parameter", "string", "sqlite3_filename", "string"],
    ["sqlite3_user_data","void*", "sqlite3_context*"],
    ["sqlite3_value_blob", "*", "sqlite3_value*"],
    ["sqlite3_value_blob_v2", "int", "sqlite3_value*", "**", "*", "*"],
    ["sqlite3_value_bytes","int", "sqlite3_value*"],
    ["sqlite3_value_double","f64", "sqlite3_value*"],
    ["sqlite3_value_dup", "sqlite3_value*", "sqlite3_value*"],
    ["sqlite3_value_free", undefined, "sqlite3_value*"],
    ["sqlite3_value_frombind", "int", "sqlite3_value*"],
    ["sqlite3_value_int","int", "sqlite3_value*"],
    ["sqlite3_value_nochange", "int", "sqlite3_value*"],
    ["sqlite3_value_numeric_type", "int", "sqlite3_value*"],
    ["sqlite3_value_pointer", "*", "sqlite3_value*", "string:static"],
    ["sqlite3_value_subtype", "int", "sqlite3_value*"],
    ["sqlite3_value_text", "string", "sqlite3_value*"],
    ["sqlite3_value_text_v2", "int", "sqlite3_value*", "**", "*", "*"],
    ["sqlite3_value_type", "int", "sqlite3_value*"],
    ["sqlite3_vfs_find", "*", "string"],
    ["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"],
    ["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"]
  ]/*wasm.bindingSignatures*/;

  if( !!wasm.exports.sqlite3_progress_handler ){
Changes to ext/wasm/tester1.c-pp.js.
3348
3349
3350
3351
3352
3353
3354
3355





3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417


3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446






3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464



3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484


3485
3486
3487
3488
3489
3490
3491
3492
               "values(1,123),(2,null),(3,'hi world'),(4,X'232A'),(5,'')"]);
      const P = wasm.pstack;
      const stack = P.pointer;
      let q;
      try {
        let sv, rc;
        q = db.prepare("select a, b from t order by a");
        let [ppOut,pnOut] = P.allocPtr(2);





        const next = ()=>{
          T.assert( q.step() );
          sv = capi.sqlite3_column_value(q, 1);
          T.assert( sv );
          wasm.pokePtr(ppOut, 0);
          wasm.poke32(pnOut, 0);
          rc = capi.sqlite3_value_text_v2(sv, ppOut, pnOut);
          T.assert( 0===rc );
          return sv;
        };
        const cmp = function(expect){
          const blob = wasm.peekPtr(ppOut);
          const len = wasm.peek32(pnOut);
          //log("blob=",wasm.cstrToJs(blob));
          const str = wasm.cstrToJs(blob);
          if( !blob ){
            T.assert( null===expect )
              .assert( 0===len );
            return;
          }
          T.assert(len === expect.length,
                   "Lengths don't match: got ["+str
                   +"] expected ["+expect+"]")
            .assert( str===expect, "String mismatch: got ["
                     +str+"] expected ["+expect+"]");
        };
        const cmp2 = (expect)=>{
          next();
          cmp(expect);
          wasm.pokePtr(ppOut, 0);
          wasm.poke32(pnOut, 0);
          const rc = capi.sqlite3_column_text_v2(q, 1, ppOut, pnOut);
          T.assert( 0==rc, "expecting column_text_v2() rc 0 but got "+rc );
          cmp(expect);
        };

        cmp2('123');
        cmp2(null);
        cmp2('hi world');
        cmp2( '#*' );
        cmp2( '' ); // empty strings are not null


        const checkRc = (name, descr, rc)=>{
          T.assert( capi[name] === rc,
                    descr+": expecting "+name+"("+
                    capi[name]+") but got "+rc);
        };

        /** The following tests cover the same code paths
            for both text_v2 and blob_v2, so are elided from
            the blob_v2 checks in the next test group. */

        // This does not set a persistent error flag on q:
        checkRc('SQLITE_RANGE', "column_text_v2() bad index",
                capi.sqlite3_column_text_v2(q, 11, ppOut, pnOut) );
        checkRc('SQLITE_OK', "column_text_v2() valid index",
                capi.sqlite3_column_text_v2(q, 1, ppOut, 0));

        checkRc('SQLITE_OK', "column null pnOut",
                capi.sqlite3_column_text_v2(q, 1, ppOut, 0));



        checkRc('SQLITE_MISUSE', "value null ppOut",
                capi.sqlite3_value_text_v2(sv, 0, pnOut));
        checkRc('SQLITE_MISUSE', "value null arg0",
                capi.sqlite3_value_text_v2(0, ppOut, pnOut));
        checkRc('SQLITE_MISUSE', "column null ppOut",
                capi.sqlite3_column_text_v2(q, 1, 0, pnOut));
        /* But a 0 pnOut is always okay. */
        checkRc('SQLITE_OK', "value null pnOut",
                capi.sqlite3_value_text_v2(sv, ppOut, 0));

      }finally{
        if( q ) q.finalize();
        db.close();
        P.restore(stack);
      }
    })

  ////////////////////////////////////////////////////////////////////
    .t("value_blob_v2() and friends...", function(sqlite3){
      const db = new sqlite3.oo1.DB();
      db.exec(["create table t(a,b); insert into t(a,b) ",
               "values(1,123),(2,null),(3,'hi'),(4,X'23002A'),(5,'')"]);
      const P = wasm.pstack;
      const stack = P.pointer;
      let q;
      try {
        let sv, rc;
        q = db.prepare("select a, b from t order by a");
        let [ppOut,pnOut] = P.allocPtr(2);






        const next = ()=>{
          T.assert( q.step() );
          sv = capi.sqlite3_column_value(q, 1);
          T.assert( sv );
          wasm.pokePtr(ppOut, 0);
          wasm.poke32(pnOut, 0);
          rc = capi.sqlite3_value_blob_v2(sv, ppOut, pnOut);
          T.assert( 0==rc, "expecting value_blob_v2() rc 0 but got "+rc );
          return sv;
        };
        const cmp = (byteList)=>{
          const blob = wasm.peekPtr(ppOut);
          const len = wasm.peek32(pnOut);
          //log("blob=",wasm.cstrToJs(blob));
          T.assert(len === byteList.length, "Lengths don't match")
          T.assert( len ? !!blob : !blob,
                    "Expecting len=non-0/blob=non-null or len=0/blob=null. "+
                    "Got len="+len+" blob=@"+blob );



          for( let i = 0; i < len; ++i ){
            T.assert( byteList[i] === wasm.peek8(blob+i),
                      "mismatch at offset "+i+": "+byteList[i]
                      +"!=="+wasm.peek8(blob+i) );
          }
        };
        const cmp2 = (byteList)=>{
          next();
          cmp(byteList);
          wasm.pokePtr(ppOut, 0);
          wasm.poke32(pnOut, 0);
          const rc = capi.sqlite3_column_blob_v2(q, 1, ppOut, pnOut);
          T.assert( 0==rc, "expecting column_blob_v2() rc 0 but got "+rc );
          cmp(byteList);
        };

        cmp2([49,50,51]); // 123
        cmp2([]); // null
        cmp2([104,105]); // "hi"
        cmp2([0x23, 0, 0x2a]); // X'23002A'


        cmp2([]); // empty blobs are null
        /** Tests which cover the same code paths for both text_v2 and
            blob_v2 are in the previous test group. */

      }finally{
        if( q ) q.finalize();
        db.close();
        P.restore(stack);







|
>
>
>
>
>




|
<
|



|















|

|
|
<
|

|


|
|
|
|
|
<













|

|


|

>
>

|

|

|


|



















|
>
>
>
>
>
>




|
<
|



|







>
>
>






|

|
|
<
|
|
|


|
|
|
|
>
>
|







3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365

3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389

3390
3391
3392
3393
3394
3395
3396
3397
3398
3399

3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461

3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486

3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
               "values(1,123),(2,null),(3,'hi world'),(4,X'232A'),(5,'')"]);
      const P = wasm.pstack;
      const stack = P.pointer;
      let q;
      try {
        let sv, rc;
        q = db.prepare("select a, b from t order by a");
        let [ppOut, pnOut, pType] = P.allocPtr(3);
        const clearPtrs = ()=>{
          wasm.pokePtr(ppOut, 0);
          wasm.poke32(pnOut, 0);
          wasm.poke32(pType, 0);
        };
        const next = ()=>{
          T.assert( q.step() );
          sv = capi.sqlite3_column_value(q, 1);
          T.assert( sv );
          clearPtrs();

          rc = capi.sqlite3_value_text_v2(sv, ppOut, pnOut, pType);
          T.assert( 0===rc );
          return sv;
        };
        const cmp = function(type,expect){
          const blob = wasm.peekPtr(ppOut);
          const len = wasm.peek32(pnOut);
          //log("blob=",wasm.cstrToJs(blob));
          const str = wasm.cstrToJs(blob);
          if( !blob ){
            T.assert( null===expect )
              .assert( 0===len );
            return;
          }
          T.assert(len === expect.length,
                   "Lengths don't match: got ["+str
                   +"] expected ["+expect+"]")
            .assert( str===expect, "String mismatch: got ["
                     +str+"] expected ["+expect+"]");
        };
        const cmp2 = (type,expect)=>{
          next();
          cmp(type,expect);
          clearPtrs();

          const rc = capi.sqlite3_column_text_v2(q, 1, ppOut, pnOut, pType);
          T.assert( 0==rc, "expecting column_text_v2() rc 0 but got "+rc );
          cmp(type,expect);
        };

        cmp2(capi.SQLITE_INTEGER,'123');
        cmp2(capi.SQLITE_NULL,null);
        cmp2(capi.SQLITE_TEXT,'hi world');
        cmp2(capi.SQLITE_BLOB, '#*');
        cmp2(capi.SQLITE_TEXT, ''); // empty strings are not null


        const checkRc = (name, descr, rc)=>{
          T.assert( capi[name] === rc,
                    descr+": expecting "+name+"("+
                    capi[name]+") but got "+rc);
        };

        /** The following tests cover the same code paths
            for both text_v2 and blob_v2, so are elided from
            the blob_v2 checks in the next test group. */

        // This does not set a persistent error flag on q:
        checkRc('SQLITE_RANGE', "column_text_v2() bad index",
                capi.sqlite3_column_text_v2(q, 11, ppOut, pnOut, 0) );
        checkRc('SQLITE_OK', "column_text_v2() valid index",
                capi.sqlite3_column_text_v2(q, 1, ppOut, 0, 0));

        checkRc('SQLITE_OK', "column null pnOut",
                capi.sqlite3_column_text_v2(q, 1, ppOut, 0, 0));

        /* Some of the following only applies because we build with
           SQLITE_ENABLE_API_ARMOR. */
        checkRc('SQLITE_MISUSE', "value null ppOut",
                capi.sqlite3_value_text_v2(sv, 0, pnOut, 0));
        checkRc('SQLITE_MISUSE', "value null arg0",
                capi.sqlite3_value_text_v2(0, ppOut, pnOut, 0));
        checkRc('SQLITE_MISUSE', "column null ppOut",
                capi.sqlite3_column_text_v2(q, 1, 0, pnOut, 0));
        /* But a 0 pnOut is always okay. */
        checkRc('SQLITE_OK', "value null pnOut",
                capi.sqlite3_value_text_v2(sv, ppOut, 0, 0));

      }finally{
        if( q ) q.finalize();
        db.close();
        P.restore(stack);
      }
    })

  ////////////////////////////////////////////////////////////////////
    .t("value_blob_v2() and friends...", function(sqlite3){
      const db = new sqlite3.oo1.DB();
      db.exec(["create table t(a,b); insert into t(a,b) ",
               "values(1,123),(2,null),(3,'hi'),(4,X'23002A'),(5,'')"]);
      const P = wasm.pstack;
      const stack = P.pointer;
      let q;
      try {
        let sv, rc;
        q = db.prepare("select a, b from t order by a");
        let [ppOut, pnOut, pType] = P.allocPtr(3);
        const clearPtrs = ()=>{
          wasm.pokePtr(ppOut, 0);
          wasm.poke32(pnOut, 0);
          wasm.poke32(pType, 0);
        };

        const next = ()=>{
          T.assert( q.step() );
          sv = capi.sqlite3_column_value(q, 1);
          T.assert( sv );
          clearPtrs();

          rc = capi.sqlite3_value_blob_v2(sv, ppOut, pnOut, pType);
          T.assert( 0==rc, "expecting value_blob_v2() rc 0 but got "+rc );
          return sv;
        };
        const cmp = (type,byteList)=>{
          const blob = wasm.peekPtr(ppOut);
          const len = wasm.peek32(pnOut);
          //log("blob=",wasm.cstrToJs(blob));
          T.assert(len === byteList.length, "Lengths don't match")
          T.assert( len ? !!blob : !blob,
                    "Expecting len=non-0/blob=non-null or len=0/blob=null. "+
                    "Got len="+len+" blob=@"+blob );
          T.assert( type === wasm.peek32(pType),
                    "Expecting value_blob_v2 type "+type+" but got "
                    +wasm.peek32(pType)+". Value="+wasm.cstrToJs(blob));
          for( let i = 0; i < len; ++i ){
            T.assert( byteList[i] === wasm.peek8(blob+i),
                      "mismatch at offset "+i+": "+byteList[i]
                      +"!=="+wasm.peek8(blob+i) );
          }
        };
        const cmp2 = (type,byteList)=>{
          next();
          cmp(type,byteList);
          clearPtrs();

          const rc = capi.sqlite3_column_blob_v2(q, 1, ppOut, pnOut, pType);
          T.assert( 0==rc, "expecting column_blob_v2() rc 0 but got "+rc);
          cmp(type,byteList);
        };

        cmp2(capi.SQLITE_INTEGER, [49,50,51]); // 123
        cmp2(capi.SQLITE_NULL, []); // null
        cmp2(capi.SQLITE_TEXT, [104,105]); // "hi"
        cmp2(capi.SQLITE_BLOB, [0x23, 0, 0x2a]); // X'23002A'
        cmp2(capi.SQLITE_TEXT, []) /* length-0 non-NULL blobs are NULL
                                      but this one has type TEXT */;

        /** Tests which cover the same code paths for both text_v2 and
            blob_v2 are in the previous test group. */

      }finally{
        if( q ) q.finalize();
        db.close();
        P.restore(stack);
Changes to src/sqlite.h.in.
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
** routines is the same as if the column had contained an SQL NULL value.
** Valid SQL NULL returns can be distinguished from out-of-memory errors
** by invoking the [sqlite3_errcode()] immediately after the suspect
** return value is obtained and before any
** other SQLite interface is called on the same [database connection].
*/
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
int sqlite3_column_blob_v2(sqlite3_stmt*, int iCol, const void **, int*);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
int sqlite3_column_text_v2(sqlite3_stmt*, int iCol, const unsigned char **, int*);
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
int sqlite3_column_type(sqlite3_stmt*, int iCol);

/*







|




|







5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
** routines is the same as if the column had contained an SQL NULL value.
** Valid SQL NULL returns can be distinguished from out-of-memory errors
** by invoking the [sqlite3_errcode()] immediately after the suspect
** return value is obtained and before any
** other SQLite interface is called on the same [database connection].
*/
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
int sqlite3_column_blob_v2(sqlite3_stmt*, int iCol, const void **, int*, int*);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
int sqlite3_column_text_v2(sqlite3_stmt*, int iCol, const unsigned char **, int*, int*);
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
int sqlite3_column_type(sqlite3_stmt*, int iCol);

/*
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
** routines is the same as if the column had contained an SQL NULL value.
** Valid SQL NULL returns can be distinguished from out-of-memory errors
** by invoking the [sqlite3_errcode()] immediately after the suspect
** return value is obtained and before any
** other SQLite interface is called on the same [database connection].
*/
const void *sqlite3_value_blob(sqlite3_value*);
int sqlite3_value_blob_v2(sqlite3_value*, const void **, int*);
double sqlite3_value_double(sqlite3_value*);
int sqlite3_value_int(sqlite3_value*);
sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
void *sqlite3_value_pointer(sqlite3_value*, const char*);
const unsigned char *sqlite3_value_text(sqlite3_value*);
int sqlite3_value_text_v2(sqlite3_value*, const unsigned char **, int*);
const void *sqlite3_value_text16(sqlite3_value*);
const void *sqlite3_value_text16le(sqlite3_value*);
const void *sqlite3_value_text16be(sqlite3_value*);
int sqlite3_value_bytes(sqlite3_value*);
int sqlite3_value_bytes16(sqlite3_value*);
int sqlite3_value_type(sqlite3_value*);
int sqlite3_value_numeric_type(sqlite3_value*);







|





|







5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
** routines is the same as if the column had contained an SQL NULL value.
** Valid SQL NULL returns can be distinguished from out-of-memory errors
** by invoking the [sqlite3_errcode()] immediately after the suspect
** return value is obtained and before any
** other SQLite interface is called on the same [database connection].
*/
const void *sqlite3_value_blob(sqlite3_value*);
int sqlite3_value_blob_v2(sqlite3_value*, const void **, int*, int*);
double sqlite3_value_double(sqlite3_value*);
int sqlite3_value_int(sqlite3_value*);
sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
void *sqlite3_value_pointer(sqlite3_value*, const char*);
const unsigned char *sqlite3_value_text(sqlite3_value*);
int sqlite3_value_text_v2(sqlite3_value*, const unsigned char **, int*, int*);
const void *sqlite3_value_text16(sqlite3_value*);
const void *sqlite3_value_text16le(sqlite3_value*);
const void *sqlite3_value_text16be(sqlite3_value*);
int sqlite3_value_bytes(sqlite3_value*);
int sqlite3_value_bytes16(sqlite3_value*);
int sqlite3_value_type(sqlite3_value*);
int sqlite3_value_numeric_type(sqlite3_value*);
Changes to src/sqliteInt.h.
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
void sqlite3FileSuffix3(const char*, char*);
#else
# define sqlite3FileSuffix3(X,Y)
#endif
u8 sqlite3GetBoolean(const char *z,u8);

const void *sqlite3ValueText(sqlite3_value*, u8);
int sqlite3ValueTextV2(sqlite3_value*, u8, const void **, int*);

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*);







|







5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
void sqlite3FileSuffix3(const char*, char*);
#else
# define sqlite3FileSuffix3(X,Y)
#endif
u8 sqlite3GetBoolean(const char *z,u8);

const void *sqlite3ValueText(sqlite3_value*, u8);
int sqlite3ValueTextV2(sqlite3_value*, u8, const void **, int*, int*);

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*);
Changes to src/vdbeapi.c.
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

210
211
212
213
214
215
216
217
218
219
220
    p->flags |= MEM_Blob;
    return p->n ? p->z : 0;
  }else{
    return sqlite3_value_text(pVal);
  }
}
int sqlite3_value_blob_v2(sqlite3_value *pVal, const void **pOut,
                          int *pnOut){
  Mem *p = (Mem*)pVal;
#ifdef SQLITE_ENABLE_API_ARMOR
  if( pVal==0 || pOut==0 ){
    return SQLITE_MISUSE_BKPT;
  }
#endif
  if( p->flags & (MEM_Blob|MEM_Str) ){
    if( ExpandBlob(p)!=SQLITE_OK ){
      assert( p->flags==MEM_Null && p->z==0 );
      return SQLITE_NOMEM_BKPT;
    }
    p->flags |= MEM_Blob;
    *pOut = p->n ? p->z : 0;
    if( pnOut ) *pnOut = p->n;

    return 0;
  }else{
    return sqlite3_value_text_v2(pVal, (const unsigned char **)pOut,
                                 pnOut);
  }
}
int sqlite3_value_bytes(sqlite3_value *pVal){
  return sqlite3ValueBytes(pVal, SQLITE_UTF8);
}
int sqlite3_value_bytes16(sqlite3_value *pVal){
  return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE);







|














>



|







188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
    p->flags |= MEM_Blob;
    return p->n ? p->z : 0;
  }else{
    return sqlite3_value_text(pVal);
  }
}
int sqlite3_value_blob_v2(sqlite3_value *pVal, const void **pOut,
                          int *pnOut, int *pType){
  Mem *p = (Mem*)pVal;
#ifdef SQLITE_ENABLE_API_ARMOR
  if( pVal==0 || pOut==0 ){
    return SQLITE_MISUSE_BKPT;
  }
#endif
  if( p->flags & (MEM_Blob|MEM_Str) ){
    if( ExpandBlob(p)!=SQLITE_OK ){
      assert( p->flags==MEM_Null && p->z==0 );
      return SQLITE_NOMEM_BKPT;
    }
    p->flags |= MEM_Blob;
    *pOut = p->n ? p->z : 0;
    if( pnOut ) *pnOut = p->n;
    if( pType ) *pType = sqlite3_value_type(pVal);
    return 0;
  }else{
    return sqlite3_value_text_v2(pVal, (const unsigned char **)pOut,
                                 pnOut, pType);
  }
}
int sqlite3_value_bytes(sqlite3_value *pVal){
  return sqlite3ValueBytes(pVal, SQLITE_UTF8);
}
int sqlite3_value_bytes16(sqlite3_value *pVal){
  return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE);
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
  }
}
const unsigned char *sqlite3_value_text(sqlite3_value *pVal){
  return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
}
int sqlite3_value_text_v2(sqlite3_value *pVal,
                          const unsigned char **pOut,
                          int *pnOut){
  int n = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
  if( pOut==0 ){
    return SQLITE_MISUSE_BKPT;
  }
#endif
  return sqlite3ValueTextV2(pVal, SQLITE_UTF8, (const void **)pOut,
                            pnOut ? pnOut : &n);

}
#ifndef SQLITE_OMIT_UTF16
const void *sqlite3_value_text16(sqlite3_value* pVal){
  return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE);
}
const void *sqlite3_value_text16be(sqlite3_value *pVal){
  return sqlite3ValueText(pVal, SQLITE_UTF16BE);







|

<
<
|
<
<
|
|
>







247
248
249
250
251
252
253
254
255


256


257
258
259
260
261
262
263
264
265
266
  }
}
const unsigned char *sqlite3_value_text(sqlite3_value *pVal){
  return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
}
int sqlite3_value_text_v2(sqlite3_value *pVal,
                          const unsigned char **pOut,
                          int *pnOut, int *pType){
  int n = 0;


  return (pVal && pOut)


    ? sqlite3ValueTextV2(pVal, SQLITE_UTF8, (const void **)pOut,
                         pnOut ? pnOut : &n, pType)
    : SQLITE_MISUSE_BKPT;
}
#ifndef SQLITE_OMIT_UTF16
const void *sqlite3_value_text16(sqlite3_value* pVal){
  return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE);
}
const void *sqlite3_value_text16be(sqlite3_value *pVal){
  return sqlite3ValueText(pVal, SQLITE_UTF16BE);
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
** SQLITE_MISUSE, but must not perist those errors and must not take
** prior error state into account (e.g. do not propagate a
** SQLITE_RANGE error across calls). They unavoidably persist
** SQLITE_NOMEM errors via deeper APIs. This routine specifically does
** not call columnMallocFailure() to avoid calling sqlite3ApiExit().
*/
static int columnMemV2(sqlite3_stmt *pStmt, int iCol, int bBlob,
                       const void **pOut, int * pnOut){
  int rc = 0;
  Vdbe * const pVm = (Vdbe*)pStmt;

  if( pVm==0 || pOut==0 ) return SQLITE_MISUSE_BKPT;
  assert( pVm->db );
  sqlite3_mutex_enter(pVm->db->mutex);
  if( pVm->pResultRow!=0 && iCol<pVm->nResColumn && iCol>=0 ){
    Mem * const pMem = &pVm->pResultRow[iCol];
    rc = bBlob
      ? sqlite3_value_blob_v2(pMem, pOut, pnOut)
      : sqlite3_value_text_v2(pMem, (const unsigned char **)pOut,
                              pnOut);
  }else{
    rc = pVm->pResultRow==0 ? SQLITE_MISUSE_BKPT : SQLITE_RANGE;
  }
  sqlite3_mutex_leave(pVm->db->mutex);
  return rc;
}








|









|

|







1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
** SQLITE_MISUSE, but must not perist those errors and must not take
** prior error state into account (e.g. do not propagate a
** SQLITE_RANGE error across calls). They unavoidably persist
** SQLITE_NOMEM errors via deeper APIs. This routine specifically does
** not call columnMallocFailure() to avoid calling sqlite3ApiExit().
*/
static int columnMemV2(sqlite3_stmt *pStmt, int iCol, int bBlob,
                       const void **pOut, int * pnOut, int *pType){
  int rc = 0;
  Vdbe * const pVm = (Vdbe*)pStmt;

  if( pVm==0 || pOut==0 ) return SQLITE_MISUSE_BKPT;
  assert( pVm->db );
  sqlite3_mutex_enter(pVm->db->mutex);
  if( pVm->pResultRow!=0 && iCol<pVm->nResColumn && iCol>=0 ){
    Mem * const pMem = &pVm->pResultRow[iCol];
    rc = bBlob
      ? sqlite3_value_blob_v2(pMem, pOut, pnOut, pType)
      : sqlite3_value_text_v2(pMem, (const unsigned char **)pOut,
                              pnOut, pType);
  }else{
    rc = pVm->pResultRow==0 ? SQLITE_MISUSE_BKPT : SQLITE_RANGE;
  }
  sqlite3_mutex_leave(pVm->db->mutex);
  return rc;
}

1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
  ** need to call malloc() to expand the result of a zeroblob()
  ** expression.
  */
  columnMallocFailure(pStmt);
  return val;
}
int sqlite3_column_blob_v2(sqlite3_stmt *pStmt, int iCol,
                           const void **pOut, int *pnOut){
  return columnMemV2(pStmt, iCol, 1, pOut, pnOut);
}
int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){
  int val = sqlite3_value_bytes( columnMem(pStmt,i) );
  columnMallocFailure(pStmt);
  return val;
}
int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){







|
|







1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
  ** need to call malloc() to expand the result of a zeroblob()
  ** expression.
  */
  columnMallocFailure(pStmt);
  return val;
}
int sqlite3_column_blob_v2(sqlite3_stmt *pStmt, int iCol,
                           const void **pOut, int *pnOut, int *pType){
  return columnMemV2(pStmt, iCol, 1, pOut, pnOut, pType);
}
int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){
  int val = sqlite3_value_bytes( columnMem(pStmt,i) );
  columnMallocFailure(pStmt);
  return val;
}
int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
}
const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){
  const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) );
  columnMallocFailure(pStmt);
  return val;
}
int sqlite3_column_text_v2(sqlite3_stmt *pStmt, int iCol,
                           const unsigned char **pOut,
                           int *pnOut){
  return columnMemV2(pStmt, iCol, 0, (const void **)pOut, pnOut);
}
sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){
  Mem *pOut = columnMem(pStmt, i);
  if( pOut->flags&MEM_Static ){
    pOut->flags &= ~MEM_Static;
    pOut->flags |= MEM_Ephem;
  }







|
|
|







1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
}
const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){
  const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) );
  columnMallocFailure(pStmt);
  return val;
}
int sqlite3_column_text_v2(sqlite3_stmt *pStmt, int iCol,
                           const unsigned char **pOut, int *pnOut,
                           int *pType){
  return columnMemV2(pStmt, iCol, 0, (const void **)pOut, pnOut, pType);
}
sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){
  Mem *pOut = columnMem(pStmt, i);
  if( pOut->flags&MEM_Static ){
    pOut->flags &= ~MEM_Static;
    pOut->flags |= MEM_Ephem;
  }
Changes to src/vdbemem.c.
1356
1357
1358
1359
1360
1361
1362
1363


1364
1365




1366
1367
1368

1369
1370
1371
1372
1373
1374
1375
  }
}

/*
** This works like valueToText() but returns its result using
** different semantics. On success, return 0, set *pOut to a
** zero-terminated version of that string, and set *pnOut (which must
** not be NULL, to avoid an extra branch in this function) to the


** string-length of that memory. On error, return non-0 and do not
** modify pOut or pnOut.




*/
static SQLITE_NOINLINE int valueToTextV2(sqlite3_value* pVal, u8 enc,
                                        const void **pOut, int *pnOut){

  assert( pVal!=0 );
  assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
  assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
  assert( !sqlite3VdbeMemIsRowSet(pVal) );
  assert( (pVal->flags & (MEM_Null))==0 );
  assert( pOut!=0 );
  assert( pnOut!=0 );







|
>
>
|
|
>
>
>
>


|
>







1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
  }
}

/*
** This works like valueToText() but returns its result using
** different semantics. On success, return 0, set *pOut to a
** zero-terminated version of that string, and set *pnOut (which must
** not be NULL to the string-length of that memory. If pType is not
** NULL, it will be set to the sqlite3_value_type() of pVal.
**
** On error, return non-0 and do not modify pOut, pnOut, or pType.
**
** Maintenance note: this is almost a copy/paste clone of
** valueToText(), but the two should probably not be consolidated. The
** initial version of this API did so in [730c6a623e29b59b] and the
** CPU cycles doubled.
*/
static SQLITE_NOINLINE int valueToTextV2(sqlite3_value* pVal, u8 enc,
                                         const void **pOut, int *pnOut,
                                         int *pType){
  assert( pVal!=0 );
  assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
  assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
  assert( !sqlite3VdbeMemIsRowSet(pVal) );
  assert( (pVal->flags & (MEM_Null))==0 );
  assert( pOut!=0 );
  assert( pnOut!=0 );
1401
1402
1403
1404
1405
1406
1407

1408
1409
1410
1411
1412
1413
1414
    return SQLITE_NOMEM_BKPT;
  }
#endif
  if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){
    assert( sqlite3VdbeMemValidStrRep(pVal) );
    *pOut = pVal->z;
    *pnOut = pVal->n;

    return 0;
  }
  return (pVal->db && pVal->db->mallocFailed)
    ? SQLITE_NOMEM_BKPT
    : SQLITE_ERROR;
}








>







1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
    return SQLITE_NOMEM_BKPT;
  }
#endif
  if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){
    assert( sqlite3VdbeMemValidStrRep(pVal) );
    *pOut = pVal->z;
    *pnOut = pVal->n;
    if( pType ) *pType = sqlite3_value_type(pVal);
    return 0;
  }
  return (pVal->db && pVal->db->mallocFailed)
    ? SQLITE_NOMEM_BKPT
    : SQLITE_ERROR;
}

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
}

/* This works similarly to sqlite3ValueText() but returns its result
** with different semantics.
**
** On success, returns 0, sets *pOut to the underlying value (or NULL
** in the case of NULL), and sets *pnOut to the memory's usable

** length. On error, neither *pOut nor *pnOut are modified.

**
** Design note: pnOut must not be NULL to avoid an extra branch in
** this function. It is thought (but is untested) that such a branch
** would be more expensive than ensuring that pnOut is not NULL
** will. Public APIs wrapping this may optionally accept a NULL pnOut,
** but must not pass that NULL on to here.
*/
int sqlite3ValueTextV2(sqlite3_value* pVal, u8 enc,
                       const void **pOut, int *pnOut){
  if( !pVal ) return SQLITE_MISUSE_BKPT;

  assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
  assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
  assert( !sqlite3VdbeMemIsRowSet(pVal) );
  assert( pOut!=0 );
  assert( pnOut!=0 );
  if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
    assert( sqlite3VdbeMemValidStrRep(pVal) );
    *pOut = pVal->z;
    *pnOut = pVal->n;

    return 0;
  }
  if( pVal->flags&MEM_Null ){
    *pOut = 0;
    *pnOut = 0;

    return 0;
  }
  return valueToTextV2(pVal, enc, pOut, pnOut);
}

/* 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?







>
|
>

<
<
|
<
|

|
|
|
>









>





>


|







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
}

/* This works similarly to sqlite3ValueText() but returns its result
** with different semantics.
**
** On success, returns 0, sets *pOut to the underlying value (or NULL
** in the case of NULL), and sets *pnOut to the memory's usable
** length. If *pType is not NULL, it is set to the
** sqlite3_value_type() of pVal. On error, neither *pOut nor *pnOut
** nor *pType are modified.
**


** Results are undefined if pVal, pOut, or pnOut are NULL. pType may

** be NULL.
*/
int sqlite3ValueTextV2(sqlite3_value* pVal, u8 enc, const void **pOut,
                       int *pnOut, int *pType){
  /*if( !pVal ) return SQLITE_MISUSE_BKPT;*/
  assert( pVal!=0 );
  assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
  assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
  assert( !sqlite3VdbeMemIsRowSet(pVal) );
  assert( pOut!=0 );
  assert( pnOut!=0 );
  if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
    assert( sqlite3VdbeMemValidStrRep(pVal) );
    *pOut = pVal->z;
    *pnOut = pVal->n;
    if( pType ) *pType = sqlite3_value_type( pVal );
    return 0;
  }
  if( pVal->flags&MEM_Null ){
    *pOut = 0;
    *pnOut = 0;
    if( pType ) *pType = sqlite3_value_type( pVal );
    return 0;
  }
  return valueToTextV2(pVal, enc, pOut, pnOut, pType);
}

/* 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?