From 6147a0f6a1c10d47e721c91b77b8a791fbd1376b Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 7 Oct 2025 21:02:37 +0000 Subject: [PATCH] Add test cases for the carray module. FossilOrigin-Name: dcfc0164a95eddb5e924c606850fe2015e8f2f516d36b380cbb79d6bdfe034fc --- ext/misc/carray.c | 49 +++++++++---- manifest | 18 +++-- manifest.uuid | 2 +- src/test1.c | 131 ++++++++++++++++++++++++++++++--- test/carray02.test | 165 ++++++++++++++++++++++++++++++++++++++++++ test/carrayfault.test | 96 ++++++++++++++++++++++++ 6 files changed, 425 insertions(+), 36 deletions(-) create mode 100644 test/carray02.test create mode 100644 test/carrayfault.test diff --git a/ext/misc/carray.c b/ext/misc/carray.c index 1287f27843..df60b7db32 100644 --- a/ext/misc/carray.c +++ b/ext/misc/carray.c @@ -236,10 +236,11 @@ static int carrayColumn( sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT); return SQLITE_OK; } - case CARRAY_BLOB: { + default: { const struct iovec *p = (struct iovec*)pCur->pPtr; + assert( pCur->eType==CARRAY_BLOB ); sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base, - (int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT); + (int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT); return SQLITE_OK; } } @@ -441,42 +442,53 @@ SQLITE_API int sqlite3_carray_bind( int mFlags, void (*xDestroy)(void*) ){ - carray_bind *pNew; + carray_bind *pNew = 0; int i; + int rc = SQLITE_OK; + + /* Ensure that the mFlags value is acceptable. */ + assert( CARRAY_INT32==0 && CARRAY_INT64==1 && CARRAY_DOUBLE==2 ); + assert( CARRAY_TEXT==3 && CARRAY_BLOB==4 ); + if( mFlagsCARRAY_BLOB ){ + rc = SQLITE_ERROR; + goto carray_bind_error; + } + pNew = sqlite3_malloc64(sizeof(*pNew)); if( pNew==0 ){ - if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){ - xDestroy(aData); - } - return SQLITE_NOMEM; + rc = SQLITE_NOMEM; + goto carray_bind_error; } + pNew->nData = nData; pNew->mFlags = mFlags; if( xDestroy==SQLITE_TRANSIENT ){ sqlite3_int64 sz = nData; - switch( mFlags & 0x07 ){ + switch( mFlags ){ case CARRAY_INT32: sz *= 4; break; case CARRAY_INT64: sz *= 8; break; case CARRAY_DOUBLE: sz *= 8; break; case CARRAY_TEXT: sz *= sizeof(char*); break; - case CARRAY_BLOB: sz *= sizeof(struct iovec); break; + default: sz *= sizeof(struct iovec); break; } - if( (mFlags & 0x07)==CARRAY_TEXT ){ + if( mFlags==CARRAY_TEXT ){ for(i=0; iaData = sqlite3_malloc64( sz ); if( pNew->aData==0 ){ - sqlite3_free(pNew); - return SQLITE_NOMEM; + rc = SQLITE_NOMEM; + goto carray_bind_error; } - if( (mFlags & 0x07)==CARRAY_TEXT ){ + + if( mFlags==CARRAY_TEXT ){ char **az = (char**)pNew->aData; char *z = (char*)&az[nData]; for(i=0; iaData; unsigned char *z = (unsigned char*)&p[nData]; for(i=0; ixDel = xDestroy; } return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel); + + carray_bind_error: + if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){ + xDestroy(aData); + } + sqlite3_free(pNew); + return rc; } diff --git a/manifest b/manifest index 56446e1771..7aaa4331de 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improve\sthe\sinvariant\schecker\smodule\sso\sthat\sso\sthat\sit\sadded\s"+"\sbefore\n"column\sISNULL"\sin\squeries\swhere\sthe\sbase\squery\scontains\sa\sGROUP\sBY,\sto\nprevent\sthe\sISNULL\sterm\sfrom\sbeing\spushed\sdown\sinto\sthe\ssubquery,\ssince\nthat\scan\scause\sambiguities\sif\scolumn\sis\sUNIQUE. -D 2025-10-07T18:06:05.110 +C Add\stest\scases\sfor\sthe\scarray\smodule. +D 2025-10-07T21:02:37.621 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -362,7 +362,7 @@ F ext/misc/base85.c ff54cc676c6ec86231f75ecc86ea45416fcb69751dfb79690d5f5da5f7d3 F ext/misc/basexx.c 89ad6b76558efbceb627afd5e2ef1d84b2e96d9aaf9b7ecb20e3d00b51be6fcf F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a F ext/misc/btreeinfo.c 8f5e6da2c82ec2f06ee0216e922370a436dafdbb06ffa7a552203515ff9e7ddf -F ext/misc/carray.c 6fd2be4dfa3e9ecf227221d92d808454e18710c123034fbb74999f6625f9143f +F ext/misc/carray.c 9f7f838b3343660256c519f05aae05303d54a62770f64e8e4bbad255d5c78c94 F ext/misc/carray.h 4bef8af4e9ddda024f5540cc4d456c3e4a4a7624d6315edf85dce1ce8419beb8 F ext/misc/cksumvfs.c 9d7d0cf1a8893ac5d48922bfe9f3f217b4a61a6265f559263a02bb2001259913 F ext/misc/closure.c 5559daf1daf742228431db929d1aa86dd535a4224cc634a81d2fd0d1e6ad7839 @@ -746,7 +746,7 @@ F src/status.c 0e72e4f6be6ccfde2488eb63210297e75f569f3ce9920f6c3d77590ec6ce5ffd F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 F src/tclsqlite.c 3c604c49e6cf4211960a9ddb9505280fd22cde32175f40884c641c0f5a286036 F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a -F src/test1.c 82d8aab7162a758d030728cab6ae6011785234854b7d6d2aebd877f985d1016a +F src/test1.c ce9ccd92d48e4980ebd1924be3131739cfa70ac535059474155b596cdb4a529e F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff F src/test3.c 432646f581d8af1bb495e58fc98234380250954f5d5535e507fc785eccc3987a F src/test4.c 0ac87fc13cdb334ab3a71823f99b6c32a6bebe5d603cd6a71d84c823d43a25a0 @@ -950,6 +950,8 @@ F test/capi3c.test 31d3a6778f2d06f2d9222bd7660c41a516d1518a059b069e96ebbeadb5a49 F test/capi3d.test 8b778794af891b0dca3d900bd345fbc8ebd2aa2aae425a9dccdd10d5233dfbde F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe F test/carray01.test 23ed7074307c4a829ba5ff2970993a9d87db7c5cdbbe1a2cbef672d0df6d6e31 +F test/carray02.test 6df8d1bf95f7c5f41f80b81e47f14332222ac820cf3de50af10ec2fb989a903c +F test/carrayfault.test 52ef4956acbcb7b34eb177167e7af2b5ac8d9cbb80213ef4a3a9d7efb1a1f4be F test/cast.test a2a3b32df86e3c0601ffa2e9f028a18796305d251801efea807092dbf374a040 F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef F test/changes.test 4377d202a487f66fc2822c1bf57c46798c8b2caf7446f4f701723b1dbb6b86f6 @@ -2168,8 +2170,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 06b4bd2aba22c57f5a5fed606c3bee225dee6fdc13bb16cc58194040ef0d7d85 -R 453915b0b72d47b5291d37cc0a19ae2f -U drh -Z 1eb68a79b24ce28ad8c3cdcce7ce295e +P b4ff920fbeef9a8590219596d73c09976da3da53c08a685be56f6b2cd2cdc70c +R 1dda2a73403b3668fbe9a0e2c83e251e +U dan +Z 20b6eca0d0874b62c280c82aff95f36b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3cdeabada1..7c39cb764a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b4ff920fbeef9a8590219596d73c09976da3da53c08a685be56f6b2cd2cdc70c +dcfc0164a95eddb5e924c606850fe2015e8f2f516d36b380cbb79d6bdfe034fc diff --git a/src/test1.c b/src/test1.c index 78c7b9f03f..bb2ccd3837 100644 --- a/src/test1.c +++ b/src/test1.c @@ -4360,10 +4360,71 @@ static int SQLITE_TCLAPI test_bind_value_from_select( #endif #ifndef SQLITE_OMIT_VIRTUALTABLE + +/* +** These two are used by the -malloc option to sqlite3_carray_bind() +*/ +static void *testCarrayAlloc(int n){ + u8 *pRet = (u8*)sqlite3_malloc(n+16); + if( pRet ){ + pRet = &pRet[16]; + } + return (void*)pRet; +} +static void testCarrayFree(void *p){ + if( p ){ + u8 *p2 = (u8*)p; + sqlite3_free(&p2[-16]); + } +} + +static void delIntptr(void *p){ + ckfree(p); +} + +/* +** bind_carray_intptr STMT IPARAM INT0 INT1 INT2... +*/ +static int SQLITE_TCLAPI bind_carray_intptr( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt = 0; + int iVar = 0; + int *aInt = 0; + int nInt = 0; + int ii = 0; + int rc = SQLITE_OK; + + if( objc<3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "STMT"); + return TCL_ERROR; + } + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &iVar) ) return TCL_ERROR; + nInt = objc - 3; + + aInt = ckalloc((nInt+1) * sizeof(int)); + for(ii=0; ii2 } {3 4 5} + 2 { + WITH s(i) AS ( VALUES(1) UNION ALL VALUES(2) ) + SELECT i, value FROM s, carray(?) WHERE i=value; + } {1 1 2 2} +} { + do_test 2.2.$tn { + set STMT [sqlite3_prepare_v2 db $sql -1 TAIL] + sqlite3_carray_bind -int32 $STMT 1 1 2 3 4 5 + set r [run_stmt $STMT] + sqlite3_finalize $STMT + set r + } $res +} + +# In each of the following, ? is bound to an int array containing 1 to 5. +# Bound using C code like: +# +# sqlite3_bind_pointer(pStmt, 1, aInt, "carray", SQLITE_TRANSIENT) +# +foreach {tn sql res} { + 1 { SELECT value FROM carray(?, 5) } {1 2 3 4 5} + 2 { SELECT value FROM carray(?, 3, 'int32') } {1 2 3} + 3 { SELECT value, pointer, count, ctype FROM carray(?, 5, 'int32') } + {1 {} 5 int32 2 {} 5 int32 3 {} 5 int32 4 {} 5 int32 5 {} 5 int32} + 4 { SELECT rowid, value FROM carray(?, 5, 'int32') } + {1 1 2 2 3 3 4 4 5 5} +} { + do_test 2.3.$tn { + set STMT [sqlite3_prepare_v2 db $sql -1 TAIL] + bind_carray_intptr $STMT 1 1 2 3 4 5 + set r [run_stmt $STMT] + sqlite3_finalize $STMT + set r + } $res +} + +# In each of the following. Both carray(?1) and carray(?2) contain integer +# values 1 to 5. Bound by sqlite3_carray_bind(). +# +foreach {tn sql res} { + 1 { + SELECT * FROM carray(?1) AS a, carray(?2) AS b + WHERE a.value=b.value + } {1 1 2 2 3 3 4 4 5 5} + + 2 { + SELECT * FROM carray(?1) AS a, carray(?2) AS b + WHERE a.value=b.value AND a.value<3 AND b.value<3 + } {1 1 2 2 3 3} + + 3 { + SELECT * FROM carray(?1) AS a, carray(?2) AS b + WHERE a.value<3 AND b.value<3 AND a.value=b.value + } {1 1 2 2 3 3} + + 4 { + SELECT * FROM carray(?1) AS a, carray(?2, a.value) AS b + WHERE a.value=b.value + } {1 1 2 2 3 3} + +} { + do_test 2.4.$tn { + set STMT [sqlite3_prepare_v2 db { + SELECT * FROM carray(?1) AS a, carray(?2) AS b WHERE a.value=b.value + } -1 TAIL] + sqlite3_carray_bind $STMT 1 1 2 3 4 5 + sqlite3_carray_bind $STMT 2 1 2 3 4 5 + set r [run_stmt $STMT] + sqlite3_finalize $STMT + set r + } {1 1 2 2 3 3 4 4 5 5} +} + +# Test that not binding any pointer, or passing a value that is not a bound +# pointer to carray() produces no rows of output. +# +do_execsql_test 3.0.0 { + SELECT * FROM carray +} {} +do_execsql_test 3.0.1 { + SELECT * FROM carray('0xFFFF', 5) +} {} +do_execsql_test 3.0.2 { + SELECT * FROM carray('0xFFFF') +} {} +do_execsql_test 3.0.3 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('0xFFFF'); + SELECT * FROM t1, carray WHERE carray.pointer = t1.x; +} {} + +# Test passing an unknown datatype. +# +do_test 3.1 { + set STMT [sqlite3_prepare_v2 db {SELECT * FROM carray(?, 5, 'apples')} -1 T] + sqlite3_carray_bind $STMT 1 1 2 3 4 5 + sqlite3_step $STMT + list [sqlite3_finalize $STMT] [sqlite3_errmsg db] +} {SQLITE_ERROR {unknown datatype: 'apples'}} + +finish_test + + diff --git a/test/carrayfault.test b/test/carrayfault.test new file mode 100644 index 0000000000..85618764b4 --- /dev/null +++ b/test/carrayfault.test @@ -0,0 +1,96 @@ +# 2025-10-07 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix carrayfault + + +# Check that OOM faults in sqlite3_carray_init() are handled. +# +do_faultsim_test 1 -faults oom* -prep { + sqlite3 db test.db +} -body { + load_static_extension db carray +} -test { + faultsim_test_result {0 {}} {1 {initialization of carray failed: }} +} + +sqlite3 db test.db +load_static_extension db carray + +do_execsql_test 2.0 { + CREATE TABLE t1(a); +} + +set STMT [sqlite3_prepare_v2 db "SELECT value FROM carray(?)" -1 TAIL] + +# Test OOM faults in sqlite3_carray_bind() when binding an integer +# array. +# +foreach {tn mem} { + 1 -static + 2 -transient + 3 -malloc +} { + do_faultsim_test 2.$tn -faults oom* -prep { + } -body { + sqlite3_carray_bind $::mem -int64 $::STMT 1 100 200 300 400 + } -test { + faultsim_test_result {0 {}} {1 {out of memory}} + } +} + +# Test OOM faults in sqlite3_carray_bind() when binding a text array. +# +do_faultsim_test 3 -faults oom* -prep { +} -body { + sqlite3_carray_bind -transient -text $::STMT 1 "hello" "world" +} -test { + faultsim_test_result {0 {}} {1 {initialization of carray failed: }} +} + +sqlite3_finalize $STMT + + +# Test OOM faults when running queries against carray. +# +do_faultsim_test 4 -faults oom* -prep { + set ::STMT [sqlite3_prepare_v2 db "SELECT value FROM carray(?)" -1 TAIL] + sqlite3_carray_bind -transient -text $::STMT 1 "hello" "world" +} -body { + set myres [list] + while { "SQLITE_ROW"==[sqlite3_step $::STMT] } { + lappend myres [sqlite3_column_text $::STMT 1] + } + sqlite3_reset $::STMT +} -test { + faultsim_test_result {0 SQLITE_OK} {0 SQLITE_NOMEM} + sqlite3_finalize $::STMT +} + +# Test OOM faults when preparing queries against carray. +# +do_faultsim_test 5 -faults oom* -prep { + sqlite3 db test.db + load_static_extension db carray +} -body { + execsql "SELECT value FROM carray(?)" +} -test { + faultsim_test_result {0 {}} +} + + + +sqlite3_carray_bind + +finish_test -- 2.47.3