Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Handle errors in saving cursor positions during a rollback by aborting all active statements. (CVS 3027) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
5df9f022bfb22976f22b996bda169635 |
User & Date: | danielk1977 2006-01-24 16:37:58.000 |
Context
2006-01-25
| ||
15:55 | Rename files using for testing only so that their name begins with "test". (CVS 3028) (check-in: e4e6a205e4 user: drh tags: trunk) | |
2006-01-24
| ||
16:37 | Handle errors in saving cursor positions during a rollback by aborting all active statements. (CVS 3027) (check-in: 5df9f022bf user: danielk1977 tags: trunk) | |
14:21 | Save the position of any open cursors before a rollback. (CVS 3026) (check-in: 32d45bcf74 user: danielk1977 tags: trunk) | |
Changes
Changes to src/btree.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2004 April 6 ** ** 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. ** ************************************************************************* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** 2004 April 6 ** ** 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. ** ************************************************************************* ** $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 ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: ** "Sorting And Searching", pages 473-480. Addison-Wesley ** Publishing Company, Reading, Massachusetts. |
︙ | ︙ | |||
1689 1690 1691 1692 1693 1694 1695 | BtShared *pBt = p->pBt; BtCursor *pCur; #ifndef SQLITE_OMIT_SHARED_CACHE ThreadData *pTsd; #endif | < < < | > > > | 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 | BtShared *pBt = p->pBt; BtCursor *pCur; #ifndef SQLITE_OMIT_SHARED_CACHE ThreadData *pTsd; #endif /* Close all cursors opened via this handle. */ pCur = pBt->pCursor; while( pCur ){ BtCursor *pTmp = pCur; pCur = pCur->pNext; if( pTmp->pBtree==p ){ sqlite3BtreeCloseCursor(pTmp); } } /* 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); #ifndef SQLITE_OMIT_SHARED_CACHE /* If there are still other outstanding references to the shared-btree ** structure, return now. The remainder of this procedure cleans ** up the shared-btree. |
︙ | ︙ | |||
2534 2535 2536 2537 2538 2539 2540 | ** that was open at the beginning of this operation will result ** in an error. ** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ int sqlite3BtreeRollback(Btree *p){ | | > > > > > | > > > > > > | > > > > > > > > | | | 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 | ** that was open at the beginning of this operation will result ** in an error. ** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ int sqlite3BtreeRollback(Btree *p){ int rc; BtShared *pBt = p->pBt; MemPage *pPage1; rc = saveAllCursors(pBt, 0, 0); #ifndef SQLITE_OMIT_SHARED_CACHE if( rc!=SQLITE_OK ){ /* 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; } /* The rollback may have destroyed the pPage1->aData value. So ** call getPage() on page 1 again to make sure pPage1->aData is ** set correctly. */ if( getPage(pBt, 1, &pPage1)==SQLITE_OK ){ releasePage(pPage1); } assert( countWriteCursors(pBt)==0 ); |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** ************************************************************************* ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ** ************************************************************************* ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** ** $Id: main.c,v 1.331 2006/01/24 16:37:58 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" #include <ctype.h> /* ** The following constant value is used by the SQLITE_BIGENDIAN and |
︙ | ︙ | |||
127 128 129 130 131 132 133 | ** "failed to open" state. */ if( db->magic!=SQLITE_MAGIC_CLOSED && sqlite3SafetyOn(db) ){ /* printf("DID NOT CLOSE\n"); fflush(stdout); */ return SQLITE_ERROR; } | < < < | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | ** "failed to open" state. */ if( db->magic!=SQLITE_MAGIC_CLOSED && sqlite3SafetyOn(db) ){ /* printf("DID NOT CLOSE\n"); fflush(stdout); */ return SQLITE_ERROR; } for(j=0; j<db->nDb; j++){ struct Db *pDb = &db->aDb[j]; if( pDb->pBt ){ sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; if( j!=1 ){ pDb->pSchema = 0; |
︙ | ︙ | |||
173 174 175 176 177 178 179 | ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). ** So it needs to be freed here. Todo: Why not roll the temp schema into ** the same sqliteMalloc() as the one that allocates the database ** structure? */ sqliteFree(db->aDb[1].pSchema); sqliteFree(db); | < | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). ** So it needs to be freed here. Todo: Why not roll the temp schema into ** the same sqliteMalloc() as the one that allocates the database ** structure? */ sqliteFree(db->aDb[1].pSchema); sqliteFree(db); sqlite3ReleaseThreadData(); return SQLITE_OK; } /* ** Rollback all database files. */ |
︙ | ︙ |
Changes to src/sqliteInt.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2001 September 15 ** ** 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. ** ************************************************************************* ** Internal interface definitions for SQLite. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2001 September 15 ** ** 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. ** ************************************************************************* ** Internal interface definitions for SQLite. ** ** @(#) $Id: sqliteInt.h,v 1.479 2006/01/24 16:37:58 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ /* ** Extra interface definitions for those who need them */ |
︙ | ︙ | |||
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 | KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*)); int sqlite3ApiExit(sqlite3 *db, int); int sqlite3MallocFailed(void); void sqlite3FailedMalloc(void); #ifndef SQLITE_OMIT_SHARED_CACHE void sqlite3TableLock(Parse *, int, int, u8, const char *); #else #define sqlite3TableLock(v,w,x,y,z) #endif | > | 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 | KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*)); 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 *); #else #define sqlite3TableLock(v,w,x,y,z) #endif |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
1097 1098 1099 1100 1101 1102 1103 | /* ** Find every active VM other than pVdbe and change its status to ** aborted. This happens when one VM causes a rollback due to an ** ON CONFLICT ROLLBACK clause (for example). The other VMs must be ** aborted so that they do not have data rolled out from underneath ** them leading to a segfault. */ | | | | | 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 | /* ** Find every active VM other than pVdbe and change its status to ** aborted. This happens when one VM causes a rollback due to an ** ON CONFLICT ROLLBACK clause (for example). The other VMs must be ** aborted so that they do not have data rolled out from underneath ** them leading to a segfault. */ void sqlite3AbortOtherActiveVdbes(sqlite3 *db, Vdbe *pExcept){ Vdbe *pOther; 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; } } /* |
︙ | ︙ | |||
1233 1234 1235 1236 1237 1238 1239 | if( !isReadOnly ){ if( p->rc==SQLITE_NOMEM && isStatement ){ xFunc = sqlite3BtreeRollbackStmt; }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ | | | 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 | if( !isReadOnly ){ if( p->rc==SQLITE_NOMEM && isStatement ){ xFunc = sqlite3BtreeRollbackStmt; }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ sqlite3AbortOtherActiveVdbes(db, p); sqlite3RollbackAll(db); db->autoCommit = 1; } } } /* If the auto-commit flag is set and this is the only active vdbe, then |
︙ | ︙ | |||
1270 1271 1272 1273 1274 1275 1276 | } }else if( !xFunc ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ xFunc = sqlite3BtreeCommitStmt; }else if( p->errorAction==OE_Abort ){ xFunc = sqlite3BtreeRollbackStmt; }else{ | | | 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 | } }else if( !xFunc ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ xFunc = sqlite3BtreeCommitStmt; }else if( p->errorAction==OE_Abort ){ xFunc = sqlite3BtreeRollbackStmt; }else{ sqlite3AbortOtherActiveVdbes(db, p); sqlite3RollbackAll(db); db->autoCommit = 1; } } /* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or ** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs |
︙ | ︙ |
Changes to test/shared_err.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # The focus of the tests in this file are IO errors that occur in a shared # cache context. What happens to connection B if one connection A encounters # an IO-error whilst reading or writing the file-system? # | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # # The focus of the tests in this file are IO errors that occur in a shared # 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.9 2006/01/24 16:37:59 danielk1977 Exp $ proc skip {args} {} set testdir [file dirname $argv0] source $testdir/tester.tcl db close |
︙ | ︙ | |||
307 308 309 310 311 312 313 | sqlite3_step $::STMT ;# Cursor points at 1111111111 } -tclbody { execsql { INSERT INTO t1 VALUES(6, NULL); } } -cleanup { do_test shared_malloc-4.$::n.cleanup.1 { | | > > | | | | > | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | sqlite3_step $::STMT ;# Cursor points at 1111111111 } -tclbody { execsql { INSERT INTO t1 VALUES(6, NULL); } } -cleanup { do_test shared_malloc-4.$::n.cleanup.1 { 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} # db2 eval {select * from sqlite_master} db2 close } |
︙ | ︙ | |||
344 345 346 347 348 349 350 351 352 353 354 | do_test shared_misuse-7.1 { sqlite3 db test.db catch { sqlite3_enable_shared_cache 0 } msg set msg } {library routine called out of sequence} catch {db close} sqlite3_enable_shared_cache $::enable_shared_cache finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | do_test shared_misuse-7.1 { sqlite3 db test.db catch { sqlite3_enable_shared_cache 0 } msg 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 |