From: danielk1977 Date: Tue, 24 Jan 2006 16:37:57 +0000 (+0000) Subject: Handle errors in saving cursor positions during a rollback by aborting all active... X-Git-Tag: version-3.6.10~3140 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8d34dfd6569000f5121b45f6578f6b62e9ccadc7;p=thirdparty%2Fsqlite.git Handle errors in saving cursor positions during a rollback by aborting all active statements. (CVS 3027) FossilOrigin-Name: 5df9f022bfb22976f22b996bda169635354b825c --- diff --git a/manifest b/manifest index bfc95388a8..49d8a52a9a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Save\sthe\sposition\sof\sany\sopen\scursors\sbefore\sa\srollback.\s(CVS\s3026) -D 2006-01-24T14:21:24 +C Handle\serrors\sin\ssaving\scursor\spositions\sduring\sa\srollback\sby\saborting\sall\sactive\sstatements.\s(CVS\s3027) +D 2006-01-24T16:37:58 F Makefile.in 53841eb72e9eeb6030a8ce28c2595a92f440fd10 F Makefile.linux-gcc 74ba0eadf88748a9ce3fd03d2a3ede2e6715baec F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -34,7 +34,7 @@ F src/alter.c 90b779cf00489535cab6490df6dc050f40e4e874 F src/analyze.c 7d2b7ab9a9c2fd6e55700f69064dfdd3e36d7a8a F src/attach.c d73a3505de3fb9e373d0a158978116c4212031d0 F src/auth.c 9ae84d2d94eb96195e04515715e08e85963e96c2 -F src/btree.c 5b71740985d4845f3a6ac7e0bb809e41d25830d4 +F src/btree.c f45f57e6cbd3b3db947cdd699db64e5215d20b2a F src/btree.h 5663c4f43e8521546ccebc8fc95acb013b8f3184 F src/build.c feaa61e769d7887ffeaa060d746638c7b3e994ef F src/callback.c 1bf497306c32229114f826707054df7ebe10abf2 @@ -48,7 +48,7 @@ F src/hash.c 8747cf51d12de46512880dfcf1b68b4e24072863 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 F src/insert.c 7e931b7f06afbcefcbbaab175c02eff8268db33f F src/legacy.c 86b669707b3cefd570e34154e2f6457547d1df4f -F src/main.c dc3fc9b02b1a022574d6e12d25abe58b93b85b1f +F src/main.c 2693776249865dc69b97904205638e84a34a059c F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217 F src/os.c 59f05de8c5777c34876607114a2fbe55ae578235 F src/os.h 93035a0e3b9dd05cdd0aaef32ea28ca28e02fe78 @@ -70,7 +70,7 @@ F src/select.c daee9b20702ba51cf3807fc1b130edd8846e3e48 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c 66b073375efbdee19045e7e0cd38b85f9aff71da F src/sqlite.h.in 492580f7e3ff71eb43193eb7bb98e2d549889ce3 -F src/sqliteInt.h 35a3c3556abfca796cf44fea83c9f04385efcfb6 +F src/sqliteInt.h 0121298397ac14eb468ab1ba9d488ac7ed7d88a1 F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316 F src/tclsqlite.c 7764ab34df617b3d3cfd5f0fdf3444ed219c11d6 F src/test1.c ce715e15c8045c598fe83a17f862ddeedf60c057 @@ -91,7 +91,7 @@ F src/vdbe.c 799e6280aef25bae55d2da21b5a6dbdda5e76e36 F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13 F src/vdbeInt.h eb3f86ab08ef11635bc78eb88c3ff13f923c233b F src/vdbeapi.c dcb2636f49b4807e34960d52a2fc257b3a751140 -F src/vdbeaux.c 0c27d3b3bd8dda7ed73eb8fcfa74350ca6633895 +F src/vdbeaux.c bc90137791c9442ddd9e81453f10a23688d19dbf F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbemem.c 2034e93b32c14bda6e306bb54e3a8e930b963027 F src/where.c 8409e00fa2cb5fce873b4c911165cfed097e9c49 @@ -228,7 +228,7 @@ F test/select7.test 1bf795b948c133a15a2a5e99d3270e652ec58ce6 F test/server1.test e328b8e641ba8fe9273132cfef497383185dc1f5 F test/shared.test 0ed247941236788c255b3b29b5a82d5ca71b6432 F test/shared2.test 3466dc54ca69a3c50ac259e106fb5cd067b8cd53 -F test/shared_err.test 162ad76d510370e4d3878cb6c376e1292db06005 +F test/shared_err.test 299a9180a6376b2089e8e0d469f383fe91bfa4ff F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5 F test/subquery.test ae324ee928c5fb463a3ce08a8860d6e7f1ca5797 F test/subselect.test 2d13fb7f450db3595adcdd24079a0dd1d2d6abc2 @@ -344,7 +344,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P c30705a00d7d9d61fb9cb47a1019b1a186d690a7 -R 6675c7ec185eda4043b15c5e61211396 +P 32d45bcf746e7e926b8cc8bd038d66e7c2ec6562 +R bfc84cb6ae1c5988a33aa090aaeb35d5 U danielk1977 -Z 32002e918852056122387d8fed3cdfe2 +Z b319ec5ca674b878a1973ce33be953dd diff --git a/manifest.uuid b/manifest.uuid index 0129a576c6..9044aca99b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -32d45bcf746e7e926b8cc8bd038d66e7c2ec6562 \ No newline at end of file +5df9f022bfb22976f22b996bda169635354b825c \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 895d075648..f0e749302f 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.310 2006/01/24 14:21:24 danielk1977 Exp $ +** $Id: btree.c,v 1.311 2006/01/24 16:37:58 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -1693,9 +1693,6 @@ int sqlite3BtreeClose(Btree *p){ ThreadData *pTsd; #endif - /* Drop any table-locks */ - unlockAllTables(p); - /* Close all cursors opened via this handle. */ pCur = pBt->pCursor; while( pCur ){ @@ -1706,7 +1703,10 @@ int sqlite3BtreeClose(Btree *p){ } } - /* Rollback any active transaction and free the handle structure */ + /* Rollback any active transaction and free the handle structure. + ** The call to sqlite3BtreeRollback() drops any table-locks held by + ** this handle. + */ sqlite3BtreeRollback(p); sqliteFree(p); @@ -2538,21 +2538,40 @@ void sqlite3BtreeCursorList(Btree *p){ ** are no active cursors, it also releases the read lock. */ int sqlite3BtreeRollback(Btree *p){ - int rc = SQLITE_OK; + int rc; BtShared *pBt = p->pBt; MemPage *pPage1; rc = saveAllCursors(pBt, 0, 0); +#ifndef SQLITE_OMIT_SHARED_CACHE if( rc!=SQLITE_OK ){ - return rc; + /* This is a horrible situation. An IO or malloc() error occured whilst + ** trying to save cursor positions. If this is an automatic rollback (as + ** the result of a constraint, malloc() failure or IO error) then + ** the cache may be internally inconsistent (not contain valid trees) so + ** we cannot simply return the error to the caller. Instead, abort + ** all queries that may be using any of the cursors that failed to save. + */ + while( pBt->pCursor ){ + sqlite3 *db = pBt->pCursor->pBtree->pSqlite; + if( db ){ + sqlite3AbortOtherActiveVdbes(db, 0); + } + } } +#endif btreeIntegrity(p); unlockAllTables(p); if( p->inTrans==TRANS_WRITE ){ + int rc2; + assert( TRANS_WRITE==pBt->inTransaction ); + rc2 = sqlite3pager_rollback(pBt->pPager); + if( rc2!=SQLITE_OK ){ + rc = rc2; + } - rc = sqlite3pager_rollback(pBt->pPager); /* The rollback may have destroyed the pPage1->aData value. So ** call getPage() on page 1 again to make sure pPage1->aData is ** set correctly. */ diff --git a/src/main.c b/src/main.c index bbb2c05f4f..7daadf6936 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.330 2006/01/24 13:09:33 danielk1977 Exp $ +** $Id: main.c,v 1.331 2006/01/24 16:37:58 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -131,9 +131,6 @@ int sqlite3_close(sqlite3 *db){ return SQLITE_ERROR; } - /* sqlite3_close() may not invoke sqliteMalloc(). */ - sqlite3MallocDisallow(); - for(j=0; jnDb; j++){ struct Db *pDb = &db->aDb[j]; if( pDb->pBt ){ @@ -177,7 +174,6 @@ int sqlite3_close(sqlite3 *db){ */ sqliteFree(db->aDb[1].pSchema); sqliteFree(db); - sqlite3MallocAllow(); sqlite3ReleaseThreadData(); return SQLITE_OK; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 759eb68f1b..20bf1c5e8b 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.478 2006/01/23 13:28:54 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.479 2006/01/24 16:37:58 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1747,6 +1747,7 @@ int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, int sqlite3ApiExit(sqlite3 *db, int); int sqlite3MallocFailed(void); void sqlite3FailedMalloc(void); +void sqlite3AbortOtherActiveVdbes(sqlite3 *, Vdbe *); #ifndef SQLITE_OMIT_SHARED_CACHE void sqlite3TableLock(Parse *, int, int, u8, const char *); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index cf0c2ad35b..5758b8007a 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1101,10 +1101,10 @@ static int vdbeCommit(sqlite3 *db){ ** aborted so that they do not have data rolled out from underneath ** them leading to a segfault. */ -static void abortOtherActiveVdbes(Vdbe *pVdbe){ +void sqlite3AbortOtherActiveVdbes(sqlite3 *db, Vdbe *pExcept){ Vdbe *pOther; - for(pOther=pVdbe->db->pVdbe; pOther; pOther=pOther->pNext){ - if( pOther==pVdbe ) continue; + for(pOther=db->pVdbe; pOther; pOther=pOther->pNext){ + if( pOther==pExcept ) continue; if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue; closeAllCursors(pOther); pOther->aborted = 1; @@ -1237,7 +1237,7 @@ int sqlite3VdbeHalt(Vdbe *p){ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ - abortOtherActiveVdbes(p); + sqlite3AbortOtherActiveVdbes(db, p); sqlite3RollbackAll(db); db->autoCommit = 1; } @@ -1274,7 +1274,7 @@ int sqlite3VdbeHalt(Vdbe *p){ }else if( p->errorAction==OE_Abort ){ xFunc = sqlite3BtreeRollbackStmt; }else{ - abortOtherActiveVdbes(p); + sqlite3AbortOtherActiveVdbes(db, p); sqlite3RollbackAll(db); db->autoCommit = 1; } diff --git a/test/shared_err.test b/test/shared_err.test index 30fa24d37a..0fe0220eac 100644 --- a/test/shared_err.test +++ b/test/shared_err.test @@ -13,7 +13,7 @@ # cache context. What happens to connection B if one connection A encounters # an IO-error whilst reading or writing the file-system? # -# $Id: shared_err.test,v 1.8 2006/01/24 11:30:27 danielk1977 Exp $ +# $Id: shared_err.test,v 1.9 2006/01/24 16:37:59 danielk1977 Exp $ proc skip {args} {} @@ -311,11 +311,14 @@ do_malloc_test 4 -tclprep { } } -cleanup { do_test shared_malloc-4.$::n.cleanup.1 { - sqlite3_step $::STMT - } {SQLITE_ROW} - do_test shared_malloc-4.$::n.cleanup.2 { - sqlite3_column_text $::STMT 0 - } {2222222222} + set ::rc [sqlite3_step $::STMT] + expr {$::rc=="SQLITE_ROW" || $::rc=="SQLITE_ABORT"} + } {1} + if {$::rc=="SQLITE_ROW"} { + do_test shared_malloc-4.$::n.cleanup.2 { + sqlite3_column_text $::STMT 0 + } {2222222222} + } do_test shared_malloc-4.$::n.cleanup.3 { sqlite3_finalize $::STMT } {SQLITE_OK} @@ -349,6 +352,61 @@ do_test shared_misuse-7.1 { set msg } {library routine called out of sequence} +# Again provoke a malloc() failure when a cursor position is being saved, +# this time during a ROLLBACK operation by some other handle. +# +# The library should return an SQLITE_NOMEM to the caller. The query that +# owns the cursor (the one for which the position is not saved) should +# be aborted. +# +set ::aborted 0 +do_malloc_test 8 -tclprep { + sqlite3 db2 test.db + execsql { + PRAGMA read_uncommitted = 1; + BEGIN; + CREATE TABLE t1(a, b, UNIQUE(a, b)); + } db2 + for {set i 0} {$i < 2} {incr i} { + set a [string repeat $i 10] + set b [string repeat $i 2000] + execsql {INSERT INTO t1 VALUES($a, $b)} db2 + } + execsql {COMMIT} db2 + set ::DB2 [sqlite3_connection_pointer db2] + set ::STMT [sqlite3_prepare $::DB2 "SELECT a FROM t1 ORDER BY a" -1 DUMMY] + sqlite3_step $::STMT ;# Cursor points at 0000000000 + sqlite3_step $::STMT ;# Cursor points at 1111111111 +} -tclbody { + execsql { + BEGIN; + INSERT INTO t1 VALUES(6, NULL); + ROLLBACK; + } +} -cleanup { + do_test shared_malloc-8.$::n.cleanup.1 { + lrange [execsql { + SELECT a FROM t1; + } db2] 0 1 + } {0000000000 1111111111} + do_test shared_malloc-8.$::n.cleanup.2 { + set rc1 [sqlite3_step $::STMT] + set rc2 [sqlite3_finalize $::STMT] + if {$rc1=="SQLITE_ABORT"} { + incr ::aborted + } + expr { + ($rc1=="SQLITE_DONE" && $rc2=="SQLITE_OK") || + ($rc1=="SQLITE_ABORT" && $rc2=="SQLITE_OK") + } + } {1} + db2 close +} +do_test shared_malloc-8.X { + # Test that one or more queries were aborted due to the malloc() failure. + expr $::aborted>=1 +} {1} + catch {db close} sqlite3_enable_shared_cache $::enable_shared_cache finish_test