diff --git a/manifest b/manifest index 8dcf36d7e6..6f55fe208d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\san\sr-tree\sextension\stest\scase\sto\saccount\sfor\srecent\schanges\sto\sthe\squery\splanner.\sAlso\sfix\sa\scomment\sin\srtree.c. -D 2010-08-11T12:26:46 +C Improve\scoverage\sof\spager.c. +D 2010-08-11T18:56:46 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -156,7 +156,7 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e F src/os_unix.c 11194cbcf6a57456e58022dc537ab8c3497d9bb9 F src/os_win.c 51cb62f76262d961ea4249489383d714501315a7 -F src/pager.c b3db762350ee71f5f8bde04f21ca2ffcccded483 +F src/pager.c 3fb56c4d03798058e5cf57b668468fa616e20bac F src/pager.h 80726162dc3942f59ab27b738fb667b9ba0a89d5 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07 @@ -539,10 +539,10 @@ F test/notify3.test d60923e186e0900f4812a845fcdfd8eea096e33a F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347 F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec -F test/pager1.test d8e4b2bc8164c920e6ea0572c9e13576d6e4f3fa -F test/pager2.test f5c757c271ce642d36a393ecbfb3aef1c240dcef +F test/pager1.test 20457c96ab1d4e876d335cfd6ddd0f539bda3f81 +F test/pager2.test 9edc1584cbc8ecd3b34dbcc8e86467bf05caa27f F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f -F test/pagerfault.test c1d176326ce244db157ce9c3ba128be2a9b172d6 +F test/pagerfault.test 495ab0a0ed30aebe7e4278fcee148986085d4c8b F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401 F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806 F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb @@ -843,7 +843,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 7c674aaba52b00a78babc1e1ece7e5b652b65039 -R e8f0a89b3e6e46377528a5271511edf8 +P eaaca669a4afc83906806b22365b010c83bc3db8 +R 2966f30f98145ee75718d7c3465c6545 U dan -Z ddfd9f64a4ee25bfe95e56a61185d67f +Z cd90e6256916f41a20463762a4c0c757 diff --git a/manifest.uuid b/manifest.uuid index 0f05938afe..3bfa43329f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -eaaca669a4afc83906806b22365b010c83bc3db8 \ No newline at end of file +2fa05d01b6b11788a5b73d203fcac9d4a4ba9fd8 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index 4d9be40eb8..33a61de111 100644 --- a/src/pager.c +++ b/src/pager.c @@ -273,10 +273,13 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ ** ** ERROR: ** -** The ERROR state is entered when an IO, OOM or disk-full error -** occurs at a point in the code that makes it difficult to be sure -** that the in-memory pager state (cache contents, db size etc.) are -** consistent with the contents of the file-system. +** The ERROR state is entered when an IO or disk-full error (including +** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it +** difficult to be sure that the in-memory pager state (cache contents, +** db size etc.) are consistent with the contents of the file-system. +** +** Temporary pager files may enter the ERROR state, but in-memory pagers +** cannot. ** ** For example, if an IO error occurs while performing a rollback, ** the contents of the page-cache may be left in an inconsistent state. @@ -323,6 +326,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ ** * The Pager.errCode variable is set to something other than SQLITE_OK. ** * There are one or more outstanding references to pages (after the ** last reference is dropped the pager should move back to OPEN state). +** * The pager is not an in-memory pager. ** ** ** Notes: @@ -825,8 +829,22 @@ static int assert_pager_state(Pager *p){ assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal ); assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) ); - /* Check that MEMDB implies noSync. */ - assert( !MEMDB || p->noSync ); + /* Check that MEMDB implies noSync. And an in-memory journal. Since + ** this means an in-memory pager performs no IO at all, it cannot encounter + ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing + ** a journal file. (although the in-memory journal implementation may + ** return SQLITE_IOERR_NOMEM while the journal file is being written). It + ** is therefore not possible for an in-memory pager to enter the ERROR + ** state. + */ + if( MEMDB ){ + assert( p->noSync ); + assert( p->journalMode==PAGER_JOURNALMODE_OFF + || p->journalMode==PAGER_JOURNALMODE_MEMORY + ); + assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); + assert( pagerUseWal(p)==0 ); + } /* If changeCountDone is set, a RESERVED lock or greater must be held ** on the file. @@ -1759,7 +1777,8 @@ static void pager_unlock(Pager *pPager){ ** it can safely move back to PAGER_OPEN state. This happens in both ** normal and exclusive-locking mode. */ - if( pPager->errCode && !MEMDB ){ + if( pPager->errCode ){ + assert( !MEMDB ); pager_reset(pPager); pPager->changeCountDone = pPager->tempFile; pPager->eState = PAGER_OPEN; @@ -2567,13 +2586,10 @@ static int pager_playback(Pager *pPager, int isHot){ char *zMaster = 0; /* Name of master journal file if any */ int needPagerReset; /* True to reset page prior to first page rollback */ - if( !isOpen(pPager->jfd) ){ - return SQLITE_OK; - } - /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ + assert( isOpen(pPager->jfd) ); rc = sqlite3OsFileSize(pPager->jfd, &szJ); if( rc!=SQLITE_OK || szJ==0 ){ goto end_playback; @@ -3341,46 +3357,47 @@ void sqlite3PagerSetBusyhandler( ** then *pPageSize is set to the old, retained page size before returning. */ int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){ - int rc = pPager->errCode; - /* It is not possible to do a full assert_pager_state() here, as this ** function may be called from within PagerOpen(), before the state ** of the Pager object is internally consistent. + ** + ** At one point this function returned an error if the pager was in + ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that + ** there is at least one outstanding page reference, this function + ** is a no-op for that case anyhow. */ - assert( rc==SQLITE_OK || pPager->eState==PAGER_ERROR ); - if( rc==SQLITE_OK ){ - u16 pageSize = *pPageSize; - assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); - if( (pPager->memDb==0 || pPager->dbSize==0) - && sqlite3PcacheRefCount(pPager->pPCache)==0 - && pageSize && pageSize!=pPager->pageSize - ){ - char *pNew; /* New temp space */ - i64 nByte = 0; - if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){ - rc = sqlite3OsFileSize(pPager->fd, &nByte); - if( rc!=SQLITE_OK ) return rc; - } - pNew = (char *)sqlite3PageMalloc(pageSize); - if( !pNew ){ - rc = SQLITE_NOMEM; - }else{ - pager_reset(pPager); - pPager->dbSize = nByte/pageSize; - pPager->pageSize = pageSize; - sqlite3PageFree(pPager->pTmpSpace); - pPager->pTmpSpace = pNew; - sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); - } + u16 pageSize = *pPageSize; + assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); + if( (pPager->memDb==0 || pPager->dbSize==0) + && sqlite3PcacheRefCount(pPager->pPCache)==0 + && pageSize && pageSize!=pPager->pageSize + ){ + char *pNew; /* New temp space */ + i64 nByte = 0; + if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){ + int rc = sqlite3OsFileSize(pPager->fd, &nByte); + if( rc!=SQLITE_OK ) return rc; + } + pNew = (char *)sqlite3PageMalloc(pageSize); + if( !pNew ){ + return SQLITE_NOMEM; + }else{ + pager_reset(pPager); + pPager->dbSize = nByte/pageSize; + pPager->pageSize = pageSize; + sqlite3PageFree(pPager->pTmpSpace); + pPager->pTmpSpace = pNew; + sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); } - *pPageSize = (u16)pPager->pageSize; - if( nReserve<0 ) nReserve = pPager->nReserve; - assert( nReserve>=0 && nReserve<1000 ); - pPager->nReserve = (i16)nReserve; - pagerReportSize(pPager); } - return rc; + + *pPageSize = (u16)pPager->pageSize; + if( nReserve<0 ) nReserve = pPager->nReserve; + assert( nReserve>=0 && nReserve<1000 ); + pPager->nReserve = (i16)nReserve; + pagerReportSize(pPager); + return SQLITE_OK; } /* @@ -4743,9 +4760,11 @@ int sqlite3PagerSharedLock(Pager *pPager){ ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); + assert( pPager->pWal==0 || rc==SQLITE_OK ); } - if( pagerUseWal(pPager) && rc==SQLITE_OK ){ + if( pagerUseWal(pPager) ){ + assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); } @@ -4755,6 +4774,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ failed: if( rc!=SQLITE_OK ){ + assert( !MEMDB ); pager_unlock(pPager); assert( pPager->eState==PAGER_OPEN ); }else{ @@ -5080,7 +5100,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ assert( pPager->eState>=PAGER_READER ); pPager->subjInMemory = (u8)subjInMemory; - if( pPager->eState==PAGER_READER ){ + if( ALWAYS(pPager->eState==PAGER_READER) ){ assert( pPager->pInJournal==0 ); if( pagerUseWal(pPager) ){ diff --git a/test/pager1.test b/test/pager1.test index 7f28002705..330bb69a7f 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -1062,6 +1062,10 @@ do_execsql_test pager1-6.8 { } {11} do_execsql_test pager1-6.9 { COMMIT } {} +do_execsql_test pager1-6.10 { PRAGMA max_page_count = 10 } {10} +do_execsql_test pager1-6.11 { SELECT * FROM t11 } {1 2 3 4} +do_execsql_test pager1-6.12 { PRAGMA max_page_count } {11} + #------------------------------------------------------------------------- # The following tests work with "PRAGMA journal_mode=TRUNCATE" and @@ -1742,7 +1746,7 @@ do_multiclient_test tn { } } {1 2 3 4 5 6} do_test pager1-17.$tn.3.2 { - csql1 { INSERT INTO t1 VALUES(3, 4) } + csql1 { INSERT INTO t1 VALUES(3, 4) } } {1 {database is locked}} do_test pager1-17.$tn.3.3 { sql2 COMMIT } {} } @@ -2232,6 +2236,7 @@ db close tv delete #------------------------------------------------------------------------- +# do_test pager1.27.1 { faultsim_delete_and_reopen sqlite3_pager_refcounts db @@ -2243,4 +2248,29 @@ do_test pager1.27.1 { execsql COMMIT } {} +#------------------------------------------------------------------------- +# Test that attempting to open a write-transaction with +# locking_mode=exclusive in WAL mode fails if there are other clients on +# the same database. +# +catch { db close } +do_multiclient_test tn { + do_test pager1-28.$tn.1 { + sql1 { + PRAGMA journal_mode = WAL; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES('a', 'b'); + } + } {wal} + do_test pager1-28.$tn.2 { sql2 { SELECT * FROM t1 } } {a b} + + do_test pager1-28.$tn.3 { sql1 { PRAGMA locking_mode=exclusive } } {exclusive} + do_test pager1-28.$tn.4 { + csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); } + } {1 {database is locked}} + code2 { db2 close ; sqlite3 db2 test.db } + do_test pager1-28.$tn.4 { + sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT } + } {} +} finish_test diff --git a/test/pager2.test b/test/pager2.test index 977d45f91b..780aec5274 100644 --- a/test/pager2.test +++ b/test/pager2.test @@ -116,4 +116,21 @@ foreach code [list { db close tv delete + +#------------------------------------------------------------------------- +# Test a ROLLBACK with journal_mode=off. +# +breakpoint +do_test pager2-2.1 { + faultsim_delete_and_reopen + execsql { + CREATE TABLE t1(a, b); + PRAGMA journal_mode = off; + BEGIN; + INSERT INTO t1 VALUES(1, 2); + ROLLBACK; + SELECT * FROM t1; + } +} {off 1 2} + finish_test diff --git a/test/pagerfault.test b/test/pagerfault.test index f9c82ddb60..363bc3b7fa 100644 --- a/test/pagerfault.test +++ b/test/pagerfault.test @@ -52,7 +52,7 @@ do_faultsim_test pagerfault-1 -prep { } -body { execsql { SELECT count(*) FROM t1 } } -test { - faultsim_test_result {0 4} + faultsim_test_result {0 4} faultsim_integrity_check if {[db one { SELECT count(*) FROM t1 }] != 4} { error "Database content appears incorrect" @@ -1078,4 +1078,50 @@ do_faultsim_test pagerfault-22 -prep { faultsim_integrity_check } +#------------------------------------------------------------------------- +# Provoke an OOM error during a commit of multi-file transaction. One of +# the databases written during the transaction is an in-memory database. +# This test causes rollback of the in-memory database after CommitPhaseOne() +# has successfully returned. i.e. the series of calls for the aborted commit +# is: +# +# PagerCommitPhaseOne() -> SQLITE_OK +# PagerCommitPhaseOne() -> SQLITE_IOERR +# PagerRollback() +# PagerRollback() +# +do_faultsim_test pagerfault-23 -prep { + foreach f [glob -nocomplain test.db*] { file delete -force $f } + sqlite3 db :memory: + db eval { + ATTACH 'test.db2' AS aux; + CREATE TABLE t1(a, b); + CREATE TABLE aux.t2(a, b); + } +} -body { + execsql { + BEGIN; + INSERT INTO t1 VALUES(1,2); + INSERT INTO t2 VALUES(3,4); + COMMIT; + } +} -test { + faultsim_test_result {0 {}} + faultsim_integrity_check +} + +do_faultsim_test pagerfault-24 -prep { + faultsim_delete_and_reopen + db eval { PRAGMA temp_store = file } + execsql { CREATE TABLE x(a, b) } +} -body { + execsql { CREATE TEMP TABLE t1(a, b) } +} -test { + faultsim_test_result {0 {}} {1 {unable to open a temporary database file for storing temporary tables}} + set ic [db eval { PRAGMA temp.integrity_check }] + if {$ic != "ok"} { error "Integrity check: $ic" } +} + + + finish_test