From 748ce2111578d9c71ad8a2ba748930a51bcd6ecb Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 20 May 2009 20:10:47 +0000 Subject: [PATCH] Fix a memory leak that occurs when an out-of-memory error occurs while preparing a statement that has multiple virtual table updates within triggers. Other virtual table changes to support full-coverage testing. (CVS 6661) FossilOrigin-Name: 02b77a582c65e0eb45eb1a9abbdeef11f52d7ce6 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vtab.c | 54 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/manifest b/manifest index 71494ab7c1..69cc83edd0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sunused,\sundocumented,\sand\suntested\serror\sreporting\slogic\sfrom\sthe\nxFindFunction\sinterface\sin\svirtual\stables.\s(CVS\s6660) -D 2009-05-20T16:22:02 +C Fix\sa\smemory\sleak\sthat\soccurs\swhen\san\sout-of-memory\serror\soccurs\swhile\npreparing\sa\sstatement\sthat\shas\smultiple\svirtual\stable\supdates\swithin\ntriggers.\s\sOther\svirtual\stable\schanges\sto\ssupport\sfull-coverage\stesting.\s(CVS\s6661) +D 2009-05-20T20:10:47 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 583e87706abc3026960ed759aff6371faf84c211 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -210,7 +210,7 @@ F src/vdbeapi.c 86aa27a5f3493aaffb8ac051782aa3b22670d7ed F src/vdbeaux.c 1a07329bdf51cc3687f88d9f5b2bd3f1d47cc5a8 F src/vdbeblob.c 5c5abe9af28316772e7829359f6f9cda2c737ebd F src/vdbemem.c d8b985eeb88214941380372466a30ca410043a93 -F src/vtab.c f998f539acbf0f1c5f8ee6bd0cdf6a641c802242 +F src/vtab.c b0216337ae7d27708dedd56d220e6f4fecda92f1 F src/walker.c 7cdf63223c953d4343c6833e940f110281a378ee F src/where.c c5fa4a7a58880aecc657ebce5f8df98c9b67eec0 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -729,7 +729,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 929cfbc66f6e2ea6b44417305d0f4ae36567c9bf -R cfd08e5b3a0fcc71899f8a11df4112dc +P 55d6ced262f5281076e4a68097ed21e150a9c225 +R 746ae05c43433a273890f71e50932349 U drh -Z b7b7868ab892ecebd1d46c334eedc57e +Z b6bf88ddaf7ac2f73c27614c2e34432b diff --git a/manifest.uuid b/manifest.uuid index d0c11339a6..bc9d718b51 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -55d6ced262f5281076e4a68097ed21e150a9c225 \ No newline at end of file +02b77a582c65e0eb45eb1a9abbdeef11f52d7ce6 \ No newline at end of file diff --git a/src/vtab.c b/src/vtab.c index 1b8e8cea8e..3c7316c026 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.88 2009/05/20 16:22:02 drh Exp $ +** $Id: vtab.c,v 1.89 2009/05/20 20:10:47 drh Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" @@ -107,11 +107,14 @@ void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){ assert(db); assert( sqlite3SafetyCheckOk(db) ); if( pVtab->nRef==0 ){ +#ifdef SQLITE_DEBUG if( db->magic==SQLITE_MAGIC_BUSY ){ (void)sqlite3SafetyOff(db); pVtab->pModule->xDisconnect(pVtab); (void)sqlite3SafetyOn(db); - } else { + } else +#endif + { pVtab->pModule->xDisconnect(pVtab); } } @@ -188,7 +191,7 @@ void sqlite3VtabBeginParse( sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0); pTable = pParse->pNewTable; - if( pTable==0 || pParse->nErr ) return; + if( pTable==0 ) return; assert( 0==pTable->pIndex ); db = pParse->db; @@ -221,7 +224,7 @@ void sqlite3VtabBeginParse( ** virtual table currently under construction in pParse->pTable. */ static void addArgumentToVtab(Parse *pParse){ - if( pParse->sArg.z && pParse->pNewTable ){ + if( pParse->sArg.z && ALWAYS(pParse->pNewTable) ){ const char *z = (const char*)pParse->sArg.z; int n = pParse->sArg.n; sqlite3 *db = pParse->db; @@ -378,7 +381,9 @@ static int vtabCallConstructor( rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVtab, &zErr); rc2 = sqlite3SafetyOn(db); if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; - if( rc==SQLITE_OK && pVtab ){ + /* Justification of ALWAYS(): A correct vtab constructor must allocate + ** the sqlite3_vtab object if successful. */ + if( rc==SQLITE_OK && ALWAYS(pVtab) ){ pVtab->pModule = pMod->pModule; pVtab->nRef = 1; pTab->pVtab = pVtab; @@ -453,7 +458,8 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ Module *pMod; int rc = SQLITE_OK; - if( !pTab || (pTab->tabFlags & TF_Virtual)==0 || pTab->pVtab ){ + assert( pTab ); + if( (pTab->tabFlags & TF_Virtual)==0 || pTab->pVtab ){ return SQLITE_OK; } @@ -529,7 +535,9 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr); } - if( rc==SQLITE_OK && pTab->pVtab ){ + /* Justification of ALWAYS(): The xConstructor method is required to + ** create a valid sqlite3_vtab if it returns SQLITE_OK. */ + if( rc==SQLITE_OK && ALWAYS(pTab->pVtab) ){ rc = addToVTrans(db, pTab->pVtab); } @@ -598,20 +606,16 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ ** ** This call is a no-op if zTab is not a virtual table. */ -int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab) -{ +int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){ int rc = SQLITE_OK; Table *pTab; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - assert(pTab); - if( pTab->pVtab ){ + if( ALWAYS(pTab!=0 && pTab->pVtab!=0) ){ int (*xDestroy)(sqlite3_vtab *pVTab) = pTab->pMod->pModule->xDestroy; rc = sqlite3SafetyOff(db); assert( rc==SQLITE_OK ); - if( xDestroy ){ - rc = xDestroy(pTab->pVtab); - } + rc = xDestroy(pTab->pVtab); (void)sqlite3SafetyOn(db); if( rc==SQLITE_OK ){ int i; @@ -639,9 +643,11 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab) static void callFinaliser(sqlite3 *db, int offset){ int i; if( db->aVTrans ){ - for(i=0; inVTrans && db->aVTrans[i]; i++){ + for(i=0; inVTrans; i++){ sqlite3_vtab *pVtab = db->aVTrans[i]; int (*x)(sqlite3_vtab *); + + assert( pVtab!=0 ); x = *(int (**)(sqlite3_vtab *))((char *)pVtab->pModule + offset); if( x ) x(pVtab); sqlite3VtabUnlock(db, pVtab); @@ -668,9 +674,10 @@ int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ rc = sqlite3SafetyOff(db); db->aVTrans = 0; - for(i=0; rc==SQLITE_OK && inVTrans && aVTrans[i]; i++){ + for(i=0; rc==SQLITE_OK && inVTrans; i++){ sqlite3_vtab *pVtab = aVTrans[i]; int (*x)(sqlite3_vtab *); + assert( pVtab!=0 ); x = pVtab->pModule->xSync; if( x ){ rc = x(pVtab); @@ -721,7 +728,7 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ /* Special case: If db->aVTrans is NULL and db->nVTrans is greater ** than zero, then this function is being called from within a ** virtual module xSync() callback. It is illegal to write to - ** virtual module tables in this case, so return SQLITE_MISUSE. + ** virtual module tables in this case, so return SQLITE_LOCKED. */ if( sqlite3VtabInSync(db) ){ return SQLITE_LOCKED; @@ -736,7 +743,7 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ /* If pVtab is already in the aVTrans array, return early */ - for(i=0; (inVTrans) && 0!=db->aVTrans[i]; i++){ + for(i=0; inVTrans; i++){ if( db->aVTrans[i]==pVtab ){ return SQLITE_OK; } @@ -782,10 +789,10 @@ FuncDef *sqlite3VtabOverloadFunction( /* Check to see the left operand is a column in a virtual table */ - if( pExpr==0 ) return pDef; + if( NEVER(pExpr==0) ) return pDef; if( pExpr->op!=TK_COLUMN ) return pDef; pTab = pExpr->pTab; - if( pTab==0 ) return pDef; + if( NEVER(pTab==0) ) return pDef; if( (pTab->tabFlags & TF_Virtual)==0 ) return pDef; pVtab = pTab->pVtab; assert( pVtab!=0 ); @@ -832,13 +839,16 @@ FuncDef *sqlite3VtabOverloadFunction( */ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ int i, n; + Table **apVtabLock; + assert( IsVirtual(pTab) ); for(i=0; inVtabLock; i++){ if( pTab==pParse->apVtabLock[i] ) return; } n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]); - pParse->apVtabLock = sqlite3_realloc(pParse->apVtabLock, n); - if( pParse->apVtabLock ){ + apVtabLock = sqlite3_realloc(pParse->apVtabLock, n); + if( apVtabLock ){ + pParse->apVtabLock = apVtabLock; pParse->apVtabLock[pParse->nVtabLock++] = pTab; }else{ pParse->db->mallocFailed = 1;