Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix another couple of IO or malloc() failure problems in a shared-cache context. (CVS 2982) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
7e34163a65a5842ecc50a14a9d60601e |
User & Date: | danielk1977 2006-01-20 16:32:04.000 |
Context
2006-01-20
| ||
17:56 | Fix some compiler warnings. (CVS 2983) (check-in: b7bdac0afd user: drh tags: trunk) | |
16:32 | Fix another couple of IO or malloc() failure problems in a shared-cache context. (CVS 2982) (check-in: 7e34163a65 user: danielk1977 tags: trunk) | |
15:45 | Fix and test the processing of sqlite3_result_error() withing aggregate functions. Allow errors to come from the step function (a new capability). Ticket #1632. (CVS 2981) (check-in: fd4a6bb1ac user: drh 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.304 2006/01/20 16:32:04 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. |
︙ | ︙ | |||
502 503 504 505 506 507 508 509 510 511 512 513 514 515 | #define lockTable(a,b,c) SQLITE_OK #define unlockAllTables(a) #define restoreOrClearCursorPosition(a,b) SQLITE_OK #define saveAllCursors(a,b,c) SQLITE_OK #else /* ** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. */ static int saveCursorPosition(BtCursor *pCur){ int rc; | > > | 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | #define lockTable(a,b,c) SQLITE_OK #define unlockAllTables(a) #define restoreOrClearCursorPosition(a,b) SQLITE_OK #define saveAllCursors(a,b,c) SQLITE_OK #else static void releasePage(MemPage *pPage); /* ** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. */ static int saveCursorPosition(BtCursor *pCur){ int rc; |
︙ | ︙ | |||
536 537 538 539 540 541 542 543 544 545 546 547 548 549 | }else{ rc = SQLITE_NOMEM; } } assert( !pCur->pPage->intKey || !pCur->pKey ); /* Todo: Should we drop the reference to pCur->pPage here? */ if( rc==SQLITE_OK ){ pCur->eState = CURSOR_REQUIRESEEK; } return rc; } | > > | 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 | }else{ rc = SQLITE_NOMEM; } } assert( !pCur->pPage->intKey || !pCur->pKey ); /* Todo: Should we drop the reference to pCur->pPage here? */ releasePage(pCur->pPage); pCur->pPage = 0; if( rc==SQLITE_OK ){ pCur->eState = CURSOR_REQUIRESEEK; } return rc; } |
︙ | ︙ | |||
2613 2614 2615 2616 2617 2618 2619 | ** is active this routine is a no-op. ** ** All cursors will be invalidated by this operation. Any attempt ** to use a cursor that was open at the beginning of this operation ** will result in an error. */ int sqlite3BtreeRollbackStmt(Btree *p){ | | > | | | | > > | 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 | ** is active this routine is a no-op. ** ** All cursors will be invalidated by this operation. Any attempt ** to use a cursor that was open at the beginning of this operation ** will result in an error. */ int sqlite3BtreeRollbackStmt(Btree *p){ int rc = SQLITE_OK; BtShared *pBt = p->pBt; sqlite3MallocDisallow(); if( pBt->inStmt && !pBt->readOnly ){ rc = sqlite3pager_stmt_rollback(pBt->pPager); assert( countWriteCursors(pBt)==0 ); pBt->inStmt = 0; } sqlite3MallocAllow(); return rc; } /* ** Default key comparison function to be used if no comparison function ** is specified on the sqlite3BtreeCursor() call. */ |
︙ | ︙ | |||
3170 3171 3172 3173 3174 3175 3176 | */ static int moveToRoot(BtCursor *pCur){ MemPage *pRoot; int rc = SQLITE_OK; BtShared *pBt = pCur->pBtree->pBt; restoreOrClearCursorPosition(pCur, 0); | < | | 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 | */ static int moveToRoot(BtCursor *pCur){ MemPage *pRoot; int rc = SQLITE_OK; BtShared *pBt = pCur->pBtree->pBt; restoreOrClearCursorPosition(pCur, 0); pRoot = pCur->pPage; if( pRoot && pRoot->pgno==pCur->pgnoRoot ){ assert( pRoot->isInit ); }else{ if( SQLITE_OK!=(rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0)) ){ pCur->eState = CURSOR_INVALID; return rc; |
︙ | ︙ | |||
3439 3440 3441 3442 3443 3444 3445 | ** Advance the cursor to the next entry in the database. If ** successful then set *pRes=0. If the cursor ** was already pointing to the last entry in the database before ** this routine was called, then set *pRes=1. */ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ int rc; | | | > | 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 | ** Advance the cursor to the next entry in the database. If ** successful then set *pRes=0. If the cursor ** was already pointing to the last entry in the database before ** this routine was called, then set *pRes=1. */ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ int rc; MemPage *pPage; #ifndef SQLITE_OMIT_SHARED_CACHE rc = restoreOrClearCursorPosition(pCur, 1); if( rc!=SQLITE_OK ){ return rc; } if( pCur->skip>0 ){ pCur->skip = 0; *pRes = 0; return SQLITE_OK; } pCur->skip = 0; #endif assert( pRes!=0 ); pPage = pCur->pPage; if( CURSOR_INVALID==pCur->eState ){ *pRes = 1; return SQLITE_OK; } assert( pPage->isInit ); assert( pCur->idx<pPage->nCell ); |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** ** @(#) $Id: pager.c,v 1.247 2006/01/20 16:32:04 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" #include "os.h" #include "pager.h" #include <assert.h> #include <string.h> |
︙ | ︙ | |||
2689 2690 2691 2692 2693 2694 2695 | rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize); } TRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno); CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3); if( rc!=SQLITE_OK ){ i64 fileSize; | | | > > > > > | | 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 | rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize); } TRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno); CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3); if( rc!=SQLITE_OK ){ i64 fileSize; int rc2 = sqlite3OsFileSize(pPager->fd, &fileSize); if( rc2!=SQLITE_OK || fileSize>=pgno*pPager->pageSize ){ /* An IO error occured in one of the the sqlite3OsSeek() or ** sqlite3OsRead() calls above. Unreference the page and then ** set it's page number to 0 (0 means "not a page"). */ sqlite3pager_unref(PGHDR_TO_DATA(pPg)); pPg->pgno = 0; return rc; }else{ clear_simulated_io_error(); memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); } }else{ TEST_INCR(pPager->nRead); } |
︙ | ︙ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
237 238 239 240 241 242 243 | assert( p->aOp[p->nOp-1].opcode==OP_Noop ); assert( p->aOp[p->nOp-1].p3!=0 ); assert( p->aOp[p->nOp-1].p3type==P3_DYNAMIC ); db->xProfile(db->pProfileArg, p->aOp[p->nOp-1].p3, elapseTime); } #endif | | | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | assert( p->aOp[p->nOp-1].opcode==OP_Noop ); assert( p->aOp[p->nOp-1].p3!=0 ); assert( p->aOp[p->nOp-1].p3type==P3_DYNAMIC ); db->xProfile(db->pProfileArg, p->aOp[p->nOp-1].p3, elapseTime); } #endif sqlite3Error(p->db, rc, 0); p->rc = sqlite3ApiExit(p->db, p->rc); return rc; } /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 | 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. */ db->autoCommit = 1; } } } /* If the auto-commit flag is set and this is the only active vdbe, then ** we do either a commit or rollback of the current transaction. | > > | 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 | 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. */ abortOtherActiveVdbes(p); sqlite3RollbackAll(db); db->autoCommit = 1; } } } /* If the auto-commit flag is set and this is the only active vdbe, then ** we do either a commit or rollback of the current transaction. |
︙ | ︙ | |||
1260 1261 1262 1263 1264 1265 1266 | ** is required. */ int rc = vdbeCommit(db); if( rc==SQLITE_BUSY ){ return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; | | | > | | 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 | ** is required. */ int rc = vdbeCommit(db); if( rc==SQLITE_BUSY ){ return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; sqlite3RollbackAll(db); }else{ sqlite3CommitInternalChanges(db); } }else{ sqlite3RollbackAll(db); } }else if( !xFunc ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ xFunc = sqlite3BtreeCommitStmt; }else if( p->errorAction==OE_Abort ){ xFunc = sqlite3BtreeRollbackStmt; }else{ abortOtherActiveVdbes(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 ** and the return code is still SQLITE_OK, set the return code to the new |
︙ | ︙ | |||
1355 1356 1357 1358 1359 1360 1361 | /* If the VDBE has be run even partially, then transfer the error code ** and error message from the VDBE into the main database structure. But ** if the VDBE has just been set to run but has not actually executed any ** instructions yet, leave the main database error information unchanged. */ if( p->pc>=0 ){ if( p->zErrMsg ){ | | | > | 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 | /* If the VDBE has be run even partially, then transfer the error code ** and error message from the VDBE into the main database structure. But ** if the VDBE has just been set to run but has not actually executed any ** instructions yet, leave the main database error information unchanged. */ if( p->pc>=0 ){ if( p->zErrMsg ){ sqlite3* db = p->db; sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, sqlite3FreeX); db->errCode = p->rc; p->zErrMsg = 0; }else if( p->rc ){ sqlite3Error(p->db, p->rc, 0); }else{ sqlite3Error(p->db, SQLITE_OK, 0); } }else if( p->rc && p->expired ){ |
︙ | ︙ |
Changes to test/malloc3.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # This file contains tests to ensure that the library handles malloc() failures # correctly. The emphasis of these tests are the _prepare(), _step() and # _finalize() calls. # | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # # This file contains tests to ensure that the library handles malloc() failures # correctly. The emphasis of these tests are the _prepare(), _step() and # _finalize() calls. # # $Id: malloc3.test,v 1.8 2006/01/20 16:32:04 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Only run these tests if memory debugging is turned on. if {[info command sqlite_malloc_stat]==""} { puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..." |
︙ | ︙ | |||
488 489 490 491 492 493 494 | if {![info exists ::STMT32]} { set sql "SELECT name FROM sqlite_master" set ::STMT32 [sqlite3_prepare $::DB $sql -1 DUMMY] do_test $testid { sqlite3_step $::STMT32 } {SQLITE_ROW} } | > > > > | > | < > > | > > > > > | > | > > | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 | if {![info exists ::STMT32]} { set sql "SELECT name FROM sqlite_master" set ::STMT32 [sqlite3_prepare $::DB $sql -1 DUMMY] do_test $testid { sqlite3_step $::STMT32 } {SQLITE_ROW} } } SQL BEGIN TEST 33 { do_test $testid { execsql {SELECT * FROM ghi} } {a b c 1 2 3} } SQL -norollback { -- There is a unique index on ghi(g), so this statement may not cause -- an automatic ROLLBACK. Hence the "-norollback" switch. INSERT INTO ghi SELECT '2'||g, h, i FROM ghi; } TEST 34 { if {[info exists ::STMT32]} { do_test $testid { sqlite3_finalize $::STMT32 } {SQLITE_OK} unset ::STMT32 } } SQL COMMIT # # End of test program declaration #-------------------------------------------------------------------------- proc run_test {arglist {pcstart 0} {iFailStart 1}} { if {[llength $arglist] %2} { |
︙ | ︙ | |||
626 627 628 629 630 631 632 | # if {$iFail > ($iFailStart+1)} return } } # Turn of the Tcl interface's prepared statement caching facility. db cache size 0 | | | 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | # if {$iFail > ($iFailStart+1)} return } } # Turn of the Tcl interface's prepared statement caching facility. db cache size 0 run_test $::run_test_script # run_test [lrange $::run_test_script 0 3] 0 63 sqlite_malloc_fail 0 db close pp_check_for_leaks finish_test |
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 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # #*********************************************************************** # # 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.2 2006/01/20 16:32:05 danielk1977 Exp $ proc skip {args} {} set testdir [file dirname $argv0] source $testdir/tester.tcl db close ifcapable !shared_cache||!subquery { finish_test return } set ::enable_shared_cache [sqlite3_enable_shared_cache 1] do_ioerr_test shared_ioerr-1 -tclprep { sqlite3 db2 test.db execsql { PRAGMA read_uncommitted = 1; CREATE TABLE t1(a,b,c); BEGIN; SELECT * FROM sqlite_master; |
︙ | ︙ | |||
48 49 50 51 52 53 54 | BEGIN TRANSACTION; INSERT INTO t1 VALUES(1,2,3); INSERT INTO t1 VALUES(4,5,6); COMMIT; SELECT * FROM t1; DELETE FROM t1 WHERE a<100; } -cleanup { | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | BEGIN TRANSACTION; INSERT INTO t1 VALUES(1,2,3); INSERT INTO t1 VALUES(4,5,6); COMMIT; SELECT * FROM t1; DELETE FROM t1 WHERE a<100; } -cleanup { do_test shared_ioerr-1.$n.cleanup.1 { set res [catchsql { SELECT * FROM t1; } db2] set possible_results [list \ "1 {disk I/O error}" \ "0 {1 2 3}" \ "0 {1 2 3 1 2 3 4 5 6}" \ |
︙ | ︙ | |||
126 127 128 129 130 131 132 133 134 135 136 137 | puts "" puts "Result: \"$res\" ($::residx)" } set success } {1} db2 close } catch {db close} sqlite3_enable_shared_cache $::enable_shared_cache finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | puts "" puts "Result: \"$res\" ($::residx)" } set success } {1} db2 close } # This test is designed to provoke an IO error when a cursor position is # "saved" (because another cursor is going to modify the underlying table). # do_ioerr_test shared_ioerr-3 -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 < 5} {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 { INSERT INTO t1 VALUES(6, NULL); } } -cleanup { do_test shared_ioerr-3.$n.cleanup.1 { sqlite3_step $::STMT } {SQLITE_ROW} do_test shared_ioerr-3.$n.cleanup.2 { sqlite3_column_text $::STMT 0 } {2222222222} do_test shared_ioerr-3.$n.cleanup.3 { sqlite3_finalize $::STMT } {SQLITE_OK} # db2 eval {select * from sqlite_master} db2 close } catch {db close} sqlite3_enable_shared_cache $::enable_shared_cache finish_test |