Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -1004,33 +1004,89 @@ } return p; } /* -** Argument pCtx is actually a pointer to a database handle. Allocate and -** return an sqlite3_value object associated with this database handle. +** Context object passed by sqlite3Stat4ProbeSetValue() through to +** valueNew(). See comments above valueNew() for details. +*/ +struct ValueNewStat4Ctx { + Parse *pParse; + Index *pIdx; + UnpackedRecord **ppRec; + int iVal; +}; + +/* +** Allocate and return a pointer to a new sqlite3_value object. If +** the second argument to this function is NULL, the object is allocated +** by calling sqlite3ValueNew(). ** -** This function used as the xAlloc callback for valueFromExpr() when -** it is called by sqlite3ValueFromExpr(). +** Otherwise, if the second argument is non-zero, then this function is +** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not +** already been allocated, allocate the UnpackedRecord structure that +** that function will return to its caller here. Then return a pointer +** an sqlite3_value within the UnpackedRecord.a[] array. */ -static sqlite3_value *valueNew(void *pCtx){ - return sqlite3ValueNew((sqlite3*)pCtx); +static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ +#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3) + if( p ){ + UnpackedRecord *pRec = p->ppRec[0]; + + if( pRec==0 ){ + Index *pIdx = p->pIdx; /* Index being probed */ + int nByte; /* Bytes of space to allocate */ + int i; /* Counter variable */ + int nCol = pIdx->nColumn+1; /* Number of index columns including rowid */ + + nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord); + pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte); + if( pRec ){ + pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx); + if( pRec->pKeyInfo ){ + assert( pRec->pKeyInfo->nField+1==nCol ); + pRec->pKeyInfo->enc = ENC(db); + pRec->flags = UNPACKED_PREFIX_MATCH; + pRec->aMem = (Mem *)&pRec[1]; + for(i=0; iaMem[i].flags = MEM_Null; + pRec->aMem[i].type = SQLITE_NULL; + pRec->aMem[i].db = db; + } + }else{ + sqlite3DbFree(db, pRec); + pRec = 0; + } + } + if( pRec==0 ) return 0; + p->ppRec[0] = pRec; + } + + pRec->nField = p->iVal+1; + return &pRec->aMem[p->iVal]; + } +#endif + return sqlite3ValueNew(db); } /* -** This function is the same as sqlite3ValueFromExpr(), except that instead -** of allocating any required sqlite3_value object by calling -** sqlite3ValueNew(), it does so by calling the supplied xAlloc hook. +** Extract a value from the supplied expression in the manner described +** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object +** using valueNew(). +** +** If pCtx is NULL and an error occurs after the sqlite3_value object +** has been allocated, it is freed before returning. Or, if pCtx is not +** NULL, it is assumed that the caller will free any allocated object +** in all cases. */ int valueFromExpr( - sqlite3 *db, /* The database connection */ - Expr *pExpr, /* The expression to evaluate */ - u8 enc, /* Encoding to use */ - u8 affinity, /* Affinity to use */ - sqlite3_value **ppVal, /* Write the new value here */ - sqlite3_value *(*xAlloc)(void*), /* Used to allocate new sqlite3_value */ - void *pAlloc /* Argument passed to xAlloc */ + sqlite3 *db, /* The database connection */ + Expr *pExpr, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 affinity, /* Affinity to use */ + sqlite3_value **ppVal, /* Write the new value here */ + struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */ ){ int op; char *zVal = 0; sqlite3_value *pVal = 0; int negInt = 1; @@ -1062,11 +1118,11 @@ negInt = -1; zNeg = "-"; } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ - pVal = xAlloc(pAlloc); + pVal = valueNew(db, pCtx); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); }else{ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); @@ -1098,19 +1154,19 @@ } pVal->r = -pVal->r; sqlite3ValueApplyAffinity(pVal, affinity, enc); } }else if( op==TK_NULL ){ - pVal = xAlloc(pAlloc); + pVal = valueNew(db, pCtx); if( pVal==0 ) goto no_mem; } #ifndef SQLITE_OMIT_BLOB_LITERAL else if( op==TK_BLOB ){ int nVal; assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); assert( pExpr->u.zToken[1]=='\'' ); - pVal = xAlloc(pAlloc); + pVal = valueNew(db, pCtx); if( !pVal ) goto no_mem; zVal = &pExpr->u.zToken[2]; nVal = sqlite3Strlen30(zVal)-1; assert( zVal[nVal]=='\'' ); sqlite3VdbeMemSetStr(pVal, sqlite3HexToBlob(db, zVal, nVal), nVal/2, @@ -1125,12 +1181,12 @@ return SQLITE_OK; no_mem: db->mallocFailed = 1; sqlite3DbFree(db, zVal); - if( *ppVal==0 ) sqlite3ValueFree(pVal); - *ppVal = 0; + assert( *ppVal==0 ); + if( pCtx==0 ) sqlite3ValueFree(pVal); return SQLITE_NOMEM; } /* ** Create a new sqlite3_value object, containing the value of pExpr. @@ -1147,11 +1203,11 @@ Expr *pExpr, /* The expression to evaluate */ u8 enc, /* Encoding to use */ u8 affinity, /* Affinity to use */ sqlite3_value **ppVal /* Write the new value here */ ){ - return valueFromExpr(db, pExpr, enc, affinity, ppVal, valueNew, (void*)db); + return valueFromExpr(db, pExpr, enc, affinity, ppVal, 0); } #if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3) /* ** The implementation of the sqlite_record() function. This function accepts @@ -1205,68 +1261,10 @@ for(i=0; ippRec[0]; - - if( pRec==0 ){ - sqlite3 *db = p->pParse->db; /* Database handle */ - Index *pIdx = p->pIdx; /* Index being probed */ - int nByte; /* Bytes of space to allocate */ - int i; /* Counter variable */ - int nCol = pIdx->nColumn+1; /* Number of index columns including rowid */ - - nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord); - pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte); - if( pRec ){ - pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx); - if( pRec->pKeyInfo ){ - assert( pRec->pKeyInfo->nField+1==nCol ); - pRec->pKeyInfo->enc = ENC(db); - pRec->flags = UNPACKED_PREFIX_MATCH; - pRec->aMem = (Mem *)&pRec[1]; - for(i=0; iaMem[i].flags = MEM_Null; - pRec->aMem[i].type = SQLITE_NULL; - pRec->aMem[i].db = db; - } - }else{ - sqlite3DbFree(db, pRec); - pRec = 0; - } - } - if( pRec==0 ) return 0; - p->ppRec[0] = pRec; - } - - pRec->nField = p->iVal+1; - return &pRec->aMem[p->iVal]; -} - /* ** This function is used to allocate and populate UnpackedRecord ** structures intended to be compared against sample index keys stored ** in the sqlite_stat4 table. ** @@ -1311,16 +1309,12 @@ alloc.pParse = pParse; alloc.pIdx = pIdx; alloc.ppRec = ppRec; alloc.iVal = iVal; -#if 0 - if( iVal>0 ){ *pbOk = 0; return SQLITE_OK; } -#endif - if( !pExpr ){ - pVal = valueNewStat4((void*)&alloc); + pVal = valueNew(pParse->db, &alloc); if( pVal ){ sqlite3VdbeMemSetNull((Mem*)pVal); *pbOk = 1; } }else if( pExpr->op==TK_VARIABLE @@ -1328,11 +1322,11 @@ ){ Vdbe *v; int iVar = pExpr->iColumn; sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); if( (v = pParse->pReprepare) ){ - pVal = valueNewStat4((void*)&alloc); + pVal = valueNew(pParse->db, &alloc); if( pVal ){ rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iVal-1]); if( rc==SQLITE_OK ){ sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); } @@ -1343,13 +1337,11 @@ }else{ *pbOk = 0; } }else{ sqlite3 *db = pParse->db; - rc = valueFromExpr( - db, pExpr, ENC(db), affinity, &pVal, valueNewStat4, (void*)&alloc - ); + rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, &alloc); *pbOk = (pVal!=0); } assert( pVal==0 || pVal->db==pParse->db ); return rc; Index: test/analyze9.test ================================================================== --- test/analyze9.test +++ test/analyze9.test @@ -253,10 +253,34 @@ do_execsql_test 4.6 { SELECT count(*) FROM sqlite_stat4 WHERE lindex(test_decode(sample), 3) IN ('34', '68', '102', '136', '170', '204', '238', '272') } {8} + +reset_db +do_test 4.7 { + execsql { + BEGIN; + CREATE TABLE t1(o,t INTEGER PRIMARY KEY); + CREATE INDEX i1 ON t1(o); + } + for {set i 0} {$i<10000} {incr i [expr (($i<1000)?1:10)]} { + execsql { INSERT INTO t1 VALUES('x', $i) } + } + execsql { + COMMIT; + ANALYZE; + SELECT count(*) FROM sqlite_stat4; + } +} {8} +do_execsql_test 4.8 { + SELECT test_decode(sample) FROM sqlite_stat4; +} { + {x 211} {x 423} {x 635} {x 847} + {x 1590} {x 3710} {x 5830} {x 7950} +} + #------------------------------------------------------------------------- # The following would cause a crash at one point. # reset_db Index: test/mallocA.test ================================================================== --- test/mallocA.test +++ test/mallocA.test @@ -13,10 +13,11 @@ # $Id: mallocA.test,v 1.8 2008/02/18 22:24:58 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl +set testprefix mallocA # Only run these tests if memory debugging is turned on. # if {!$MEMDEBUG} { puts "Skipping mallocA tests: not compiled with -DSQLITE_MEMDEBUG..." @@ -38,11 +39,10 @@ CREATE TABLE t2(x,y,z); } db close copy_file test.db test.db.bu - do_malloc_test mallocA-1 -testdb test.db.bu -sqlbody { ANALYZE } do_malloc_test mallocA-1.1 -testdb test.db.bu -sqlbody { ANALYZE t1 @@ -51,10 +51,11 @@ ANALYZE main } do_malloc_test mallocA-1.3 -testdb test.db.bu -sqlbody { ANALYZE main.t1 } + ifcapable reindex { do_malloc_test mallocA-2 -testdb test.db.bu -sqlbody { REINDEX; } do_malloc_test mallocA-3 -testdb test.db.bu -sqlbody { @@ -65,14 +66,43 @@ } do_malloc_test mallocA-5 -testdb test.db.bu -sqlbody { REINDEX nocase; } } + +reset_db +sqlite3_db_config_lookaside db 0 0 0 +do_execsql_test 6-prep { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + INSERT INTO t1 VALUES('abc', 'w'); -- rowid=1 + INSERT INTO t1 VALUES('abc', 'x'); -- rowid=2 + INSERT INTO t1 VALUES('abc', 'y'); -- rowid=3 + INSERT INTO t1 VALUES('abc', 'z'); -- rowid=4 + + INSERT INTO t1 VALUES('def', 'w'); -- rowid=5 + INSERT INTO t1 VALUES('def', 'x'); -- rowid=6 + INSERT INTO t1 VALUES('def', 'y'); -- rowid=7 + INSERT INTO t1 VALUES('def', 'z'); -- rowid=8 + + ANALYZE; +} + +do_faultsim_test 6.1 -faults oom* -body { + execsql { SELECT rowid FROM t1 WHERE a='abc' AND b='x' } +} -test { + faultsim_test_result [list 0 2] +} +do_faultsim_test 6.2 -faults oom* -body { + execsql { SELECT rowid FROM t1 WHERE a='abc' AND b<'y' } +} -test { + faultsim_test_result [list 0 {1 2}] +} # Ensure that no file descriptors were leaked. do_test malloc-99.X { catch {db close} set sqlite_open_file_count } {0} forcedelete test.db.bu finish_test