diff --git a/manifest b/manifest index a7780cc0b2..022a259127 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Mark\san\sunreachable\sbranch\sin\srtree. -D 2021-09-16T14:07:09.331 +C Add\sa\sreference\scounter\sto\sa\sstructure\sused\sinternally\sby\sthe\sTcl\sinterface\sso\sthat\sit\sdoes\snot\ssegfault\sif\sthe\sdatabase\sconnection\sis\sclosed\sfrom\sany\sof\sthe\svarious\scallback\sscripts\sthat\smay\sbe\sinvoked. +D 2021-09-16T14:17:14.659 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -554,7 +554,7 @@ F src/sqliteInt.h b62ee1fc9da3634616b969abe8b55fafcb540e4e7e5637ee2a2bb5fa477f4a F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 -F src/tclsqlite.c 05663f6b5010b044eac0ef22fc8fb5ea3406d2502700a898261683258042c88b +F src/tclsqlite.c 428e813dabf82804bc13196af35a0c3c6ef4347fe557fa6717c5c66bba6e8520 F src/test1.c 63761c2be2607f1b425fde991beda48aed384f8d67f2b4ee549174c88b433009 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644 @@ -1449,7 +1449,7 @@ F test/tabfunc01.test d6821e7042e5653104dac0c63d75eff24a2415ab1889fc68b5db7fde59 F test/table.test eb3463b7add9f16a5bb836badf118cf391b809d09fdccd1f79684600d07ec132 F test/tableapi.test ecbcc29c4ab62c1912c3717c48ea5c5e59f7d64e4a91034e6148bd2b82f177f4 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 -F test/tclsqlite.test 316c96f974f0e6d7480186e3f5bb53413e5ee5480596544a97484888912a365c +F test/tclsqlite.test 97cda6e4843e9f3e06c56f656d9b77ee0178fe1ee33fb09a6eeae8f125757ac1 F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08 F test/tempdb2.test 353864e96fd3ae2f70773d0ffbf8b1fe48589b02c2ec05013b540879410c3440 F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900 @@ -1923,7 +1923,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d26963a530ccbe36a174823d2f0bd7e06c6fca4f38ecdd04cd700c4000cc3719 -R 46d60b2ce92f695e97622b11cd163c23 -U drh -Z bd29d46e35cb4f2d20bb44fb2445a352 +P e6c4afd5b365b8dea8c0742352a09029da0f9715dd2407d4ccf53f1e0a4ce625 +R 2bba8a7e10b47ebc6a75449daec54256 +U dan +Z 6227cb06cae79681b47e5b13e50e774e diff --git a/manifest.uuid b/manifest.uuid index e0e0600d9b..ad44cfcfeb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e6c4afd5b365b8dea8c0742352a09029da0f9715dd2407d4ccf53f1e0a4ce625 \ No newline at end of file +e54a33ce56432b23947583d34cf12fc64a55bbc49eb77c7f33cff5926df51070 \ No newline at end of file diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 8874ab7699..b9b78b42b9 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -181,6 +181,7 @@ struct SqliteDb { int nVMStep; /* Another statistic for most recent operation */ int nTransaction; /* Number of nested [transaction] methods */ int openFlags; /* Flags used to open. (SQLITE_OPEN_URI) */ + int nRef; /* Delete object when this reaches 0 */ #ifdef SQLITE_TEST int bLegacyPrepare; /* True to use sqlite3_prepare() */ #endif @@ -517,64 +518,84 @@ static void flushStmtCache(SqliteDb *pDb){ pDb->stmtList = 0; } +/* +** Increment the reference counter on the SqliteDb object. The reference +** should be released by calling delDatabaseRef(). +*/ +static void addDatabaseRef(SqliteDb *pDb){ + pDb->nRef++; +} + +/* +** Decrement the reference counter associated with the SqliteDb object. +** If it reaches zero, delete the object. +*/ +static void delDatabaseRef(SqliteDb *pDb){ + assert( pDb->nRef>0 ); + pDb->nRef--; + if( pDb->nRef==0 ){ + flushStmtCache(pDb); + closeIncrblobChannels(pDb); + sqlite3_close(pDb->db); + while( pDb->pFunc ){ + SqlFunc *pFunc = pDb->pFunc; + pDb->pFunc = pFunc->pNext; + assert( pFunc->pDb==pDb ); + Tcl_DecrRefCount(pFunc->pScript); + Tcl_Free((char*)pFunc); + } + while( pDb->pCollate ){ + SqlCollate *pCollate = pDb->pCollate; + pDb->pCollate = pCollate->pNext; + Tcl_Free((char*)pCollate); + } + if( pDb->zBusy ){ + Tcl_Free(pDb->zBusy); + } + if( pDb->zTrace ){ + Tcl_Free(pDb->zTrace); + } + if( pDb->zTraceV2 ){ + Tcl_Free(pDb->zTraceV2); + } + if( pDb->zProfile ){ + Tcl_Free(pDb->zProfile); + } + if( pDb->zBindFallback ){ + Tcl_Free(pDb->zBindFallback); + } + if( pDb->zAuth ){ + Tcl_Free(pDb->zAuth); + } + if( pDb->zNull ){ + Tcl_Free(pDb->zNull); + } + if( pDb->pUpdateHook ){ + Tcl_DecrRefCount(pDb->pUpdateHook); + } + if( pDb->pPreUpdateHook ){ + Tcl_DecrRefCount(pDb->pPreUpdateHook); + } + if( pDb->pRollbackHook ){ + Tcl_DecrRefCount(pDb->pRollbackHook); + } + if( pDb->pWalHook ){ + Tcl_DecrRefCount(pDb->pWalHook); + } + if( pDb->pCollateNeeded ){ + Tcl_DecrRefCount(pDb->pCollateNeeded); + } + Tcl_Free((char*)pDb); + } +} + /* ** TCL calls this procedure when an sqlite3 database command is ** deleted. */ static void SQLITE_TCLAPI DbDeleteCmd(void *db){ SqliteDb *pDb = (SqliteDb*)db; - flushStmtCache(pDb); - closeIncrblobChannels(pDb); - sqlite3_close(pDb->db); - while( pDb->pFunc ){ - SqlFunc *pFunc = pDb->pFunc; - pDb->pFunc = pFunc->pNext; - assert( pFunc->pDb==pDb ); - Tcl_DecrRefCount(pFunc->pScript); - Tcl_Free((char*)pFunc); - } - while( pDb->pCollate ){ - SqlCollate *pCollate = pDb->pCollate; - pDb->pCollate = pCollate->pNext; - Tcl_Free((char*)pCollate); - } - if( pDb->zBusy ){ - Tcl_Free(pDb->zBusy); - } - if( pDb->zTrace ){ - Tcl_Free(pDb->zTrace); - } - if( pDb->zTraceV2 ){ - Tcl_Free(pDb->zTraceV2); - } - if( pDb->zProfile ){ - Tcl_Free(pDb->zProfile); - } - if( pDb->zBindFallback ){ - Tcl_Free(pDb->zBindFallback); - } - if( pDb->zAuth ){ - Tcl_Free(pDb->zAuth); - } - if( pDb->zNull ){ - Tcl_Free(pDb->zNull); - } - if( pDb->pUpdateHook ){ - Tcl_DecrRefCount(pDb->pUpdateHook); - } - if( pDb->pPreUpdateHook ){ - Tcl_DecrRefCount(pDb->pPreUpdateHook); - } - if( pDb->pRollbackHook ){ - Tcl_DecrRefCount(pDb->pRollbackHook); - } - if( pDb->pWalHook ){ - Tcl_DecrRefCount(pDb->pWalHook); - } - if( pDb->pCollateNeeded ){ - Tcl_DecrRefCount(pDb->pCollateNeeded); - } - Tcl_Free((char*)pDb); + delDatabaseRef(pDb); } /* @@ -1246,6 +1267,7 @@ static int SQLITE_TCLAPI DbTransPostCmd( } pDb->disableAuth--; + delDatabaseRef(pDb); return rc; } @@ -1579,6 +1601,7 @@ static void dbEvalInit( Tcl_IncrRefCount(pArray); } p->evalFlags = evalFlags; + addDatabaseRef(p->pDb); } /* @@ -1719,6 +1742,7 @@ static void dbEvalFinalize(DbEvalContext *p){ } Tcl_DecrRefCount(p->pSql); dbReleaseColumnNames(p); + delDatabaseRef(p->pDb); } /* @@ -3435,6 +3459,7 @@ deserialize_error: ** opened above. If not using NRE, evaluate the script directly, then ** call function DbTransPostCmd() to commit (or rollback) the transaction ** or savepoint. */ + addDatabaseRef(pDb); /* DbTransPostCmd() calls delDatabaseRef() */ if( DbUseNre() ){ Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); (void)Tcl_NREvalObj(interp, pScript, 0); @@ -3842,6 +3867,7 @@ static int SQLITE_TCLAPI DbMain( }else{ Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); } + p->nRef = 1; return TCL_OK; } diff --git a/test/tclsqlite.test b/test/tclsqlite.test index b7e160e07e..c2fa522e64 100644 --- a/test/tclsqlite.test +++ b/test/tclsqlite.test @@ -848,4 +848,40 @@ do_catchsql_test 19.911 { } {1 {invalid command name "bind_fallback_does_not_exist"}} db bind_fallback {} +#------------------------------------------------------------------------- +do_test 20.0 { + db transaction { + db close + } +} {} + +do_test 20.1 { + sqlite3 db test.db + set rc [catch { + db eval {SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3} { db close } + } msg] + list $rc $msg +} {1 {invalid command name "db"}} + + +proc closedb {} { + db close + return 10 +} +proc func1 {} { return 1 } + +sqlite3 db test.db +db func closedb closedb +db func func1 func1 + +do_test 20.2 { + set rc [catch { + db eval { + SELECT closedb(),func1() UNION ALL SELECT 20,30 UNION ALL SELECT 30,40 + } + } msg] + list $rc $msg +} {0 {10 1 20 30 30 40}} + finish_test +