Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -3404,10 +3404,30 @@ pList = pList->pDirty; } return rc; } + +/* +** Ensure that the sub-journal file is open. If it is already open, this +** function is a no-op. +** +** SQLITE_OK is returned if everything goes according to plan. An +** SQLITE_IOERR_XXX error code is returned if a call to sqlite3OsOpen() +** fails. +*/ +static int openSubJournal(Pager *pPager){ + int rc = SQLITE_OK; + if( !isOpen(pPager->sjfd) ){ + if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ + sqlite3MemJournalOpen(pPager->sjfd); + }else{ + rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); + } + } + return rc; +} /* ** Append a record of the current state of page pPg to the sub-journal. ** It is the callers responsibility to use subjRequiresPage() to check ** that it is really required before calling this function. @@ -3421,25 +3441,35 @@ ** bitvec. */ static int subjournalPage(PgHdr *pPg){ int rc = SQLITE_OK; Pager *pPager = pPg->pPager; - if( isOpen(pPager->sjfd) ){ - void *pData = pPg->pData; - i64 offset = pPager->nSubRec*(4+pPager->pageSize); - char *pData2; + if( pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ - CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); - PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); - + /* Open the sub-journal, if it has not already been opened */ + assert( pPager->useJournal ); + assert( isOpen(pPager->jfd) || pagerUseWal(pPager) ); + assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 ); assert( pagerUseWal(pPager) || pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); - rc = write32bits(pPager->sjfd, offset, pPg->pgno); + rc = openSubJournal(pPager); + + /* If the sub-journal was opened successfully (or was already open), + ** write the journal record into the file. */ if( rc==SQLITE_OK ){ - rc = sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); + void *pData = pPg->pData; + i64 offset = pPager->nSubRec*(4+pPager->pageSize); + char *pData2; + + CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); + PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); + rc = write32bits(pPager->sjfd, offset, pPg->pgno); + if( rc==SQLITE_OK ){ + rc = sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); + } } } if( rc==SQLITE_OK ){ pPager->nSubRec++; assert( pPager->nSavepoint>0 ); @@ -4398,31 +4428,10 @@ sqlite3PcacheRelease(pPg); pagerUnlockIfUnused(pPager); } } -/* -** If the main journal file has already been opened, ensure that the -** sub-journal file is open too. If the main journal is not open, -** this function is a no-op. -** -** SQLITE_OK is returned if everything goes according to plan. -** An SQLITE_IOERR_XXX error code is returned if a call to -** sqlite3OsOpen() fails. -*/ -static int openSubJournal(Pager *pPager){ - int rc = SQLITE_OK; - if( (pagerUseWal(pPager) || isOpen(pPager->jfd)) && !isOpen(pPager->sjfd) ){ - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ - sqlite3MemJournalOpen(pPager->sjfd); - }else{ - rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); - } - } - return rc; -} - /* ** This function is called at the start of every write transaction. ** There must already be a RESERVED or EXCLUSIVE lock on the database ** file when this routine is called. ** @@ -4501,13 +4510,10 @@ pPager->journalOff = 0; pPager->setMaster = 0; pPager->journalHdr = 0; rc = writeJournalHdr(pPager); } - if( rc==SQLITE_OK && pPager->nSavepoint ){ - rc = openSubJournal(pPager); - } if( rc!=SQLITE_OK ){ sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; } @@ -5467,13 +5473,10 @@ sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); } pPager->nSavepoint = ii+1; } assert( pPager->nSavepoint==nSavepoint ); - - /* Open the sub-journal, if it is not already opened. */ - rc = openSubJournal(pPager); assertTruncateConstraint(pPager); } return rc; } Index: test/stmt.test ================================================================== --- test/stmt.test +++ test/stmt.test @@ -48,16 +48,20 @@ } {3} do_test stmt-1.5 { execsql COMMIT set sqlite_open_file_count } {1} -do_test stmt-1.6 { +do_test stmt-1.6.1 { execsql { BEGIN; INSERT INTO t1 SELECT a+2, b+2 FROM t1; } set sqlite_open_file_count +} {2} +do_test stmt-1.6.2 { + execsql { INSERT INTO t1 SELECT a+4, b+4 FROM t1 } + set sqlite_open_file_count } {3} do_test stmt-1.7 { execsql COMMIT set sqlite_open_file_count } {1} @@ -71,15 +75,22 @@ execsql ROLLBACK set ret }] $expected] } -filecount stmt-2.1 { INSERT INTO t1 VALUES(5, 5) } 2 -filecount stmt-2.2 { REPLACE INTO t1 VALUES(5, 5) } 2 -filecount stmt-2.3 { INSERT INTO t1 SELECT 5, 5 } 3 +filecount stmt-2.1 { INSERT INTO t1 VALUES(9, 9) } 2 +filecount stmt-2.2 { REPLACE INTO t1 VALUES(9, 9) } 2 +filecount stmt-2.3 { INSERT INTO t1 SELECT 9, 9 } 2 +filecount stmt-2.4 { + INSERT INTO t1 SELECT 9, 9; + INSERT INTO t1 SELECT 10, 10; +} 3 -do_test stmt-2.4 { +do_test stmt-2.5 { execsql { CREATE INDEX i1 ON t1(b) } } {} -filecount stmt-2.5 { REPLACE INTO t1 VALUES(5, 5) } 3 +filecount stmt-2.6 { + REPLACE INTO t1 VALUES(5, 5); + REPLACE INTO t1 VALUES(5, 5); +} 3 finish_test Index: test/tempdb.test ================================================================== --- test/tempdb.test +++ test/tempdb.test @@ -72,10 +72,11 @@ CREATE TABLE t1 (a PRIMARY KEY, b, c); CREATE TABLE t2 (a, b, c); BEGIN; INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(7, 8, 9); INSERT INTO t2 SELECT * FROM t1; } catchsql { INSERT INTO t1 SELECT * FROM t2 } set sqlite_open_file_count } [expr 1 + (0==$jrnl_in_memory) + (0==$subj_in_memory)] Index: test/walfault.test ================================================================== --- test/walfault.test +++ test/walfault.test @@ -25,12 +25,11 @@ # PRAGMA journal_mode = WAL; # # statement immediately after creating a new database. # do_test walfault-1-pre-1 { - db close - file delete -force test.db test.db-wal test.db-journal + faultsim_delete_and_reopen faultsim_save_and_close } {} do_faultsim_test walfault-1 -prep { faultsim_restore_and_reopen } -body { @@ -107,11 +106,10 @@ INSERT INTO abc VALUES(randomblob(1500)); } db close faultsim_save_and_close } {} - do_faultsim_test walfault-3 -prep { faultsim_restore_and_reopen } -body { db eval { DELETE FROM abc; @@ -119,11 +117,13 @@ } } -test { faultsim_test_result {0 {}} } -file delete -force test.db test.db-wal test.db-journal +#-------------------------------------------------------------------------- +# +faultsim_delete_and_reopen faultsim_save_and_close do_faultsim_test walfault-4 -prep { faultsim_restore_and_reopen } -body { execsql { @@ -136,14 +136,14 @@ } -test { faultsim_test_result {0 {wal a b}} faultsim_integrity_check } +#-------------------------------------------------------------------------- +# do_test walfault-5-pre-1 { - catch { db close } - file delete -force test.db test.db-wal test.db-journal - sqlite3 db test.db + faultsim_delete_and_reopen execsql { PRAGMA page_size = 512; PRAGMA journal_mode = WAL; } faultsim_save_and_close @@ -177,15 +177,14 @@ } -test { faultsim_test_result {0 16384} faultsim_integrity_check } - +#-------------------------------------------------------------------------- +# do_test walfault-6-pre-1 { - catch { db close } - file delete -force test.db test.db-wal test.db-journal - sqlite3 db test.db + faultsim_delete_and_reopen execsql { PRAGMA page_size = 512; PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; CREATE TABLE t1(x); @@ -219,10 +218,12 @@ faultsim_integrity_check set n [db one {SELECT count(*) FROM t1}] if {$n != 16384 && $n != 0} { error "Incorrect number of rows: $n" } } +#-------------------------------------------------------------------------- +# do_test walfault-7-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA page_size = 512; PRAGMA journal_mode = WAL; @@ -244,10 +245,12 @@ faultsim_test_result {0 4} set n [db one {SELECT count(*) FROM t1}] if {$n != 4 && $n != 0} { error "Incorrect number of rows: $n" } } +#-------------------------------------------------------------------------- +# do_test walfault-8-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE abc(a PRIMARY KEY); @@ -277,10 +280,12 @@ set n [db one {SELECT count(*) FROM abc}] if {$n != 1} { error "Incorrect number of rows: $n" } } +#-------------------------------------------------------------------------- +# do_test walfault-9-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE abc(a PRIMARY KEY); @@ -326,11 +331,11 @@ # 4. Do the checkpoint. # 5. Release the lock and mapping. # # This test case tests the outcome of an IO error in step 2. # -proc shmfault_vfs_cb_6 {method args} { +proc walfault_10_vfs_cb {method args} { switch -- $::shm_state { 0 { return SQLITE_OK } 1 { if {$method == "xShmGet"} { set ::wal_index [tvfs shm [lindex $args 0]] @@ -345,14 +350,14 @@ } } } return SQLITE_OK } -do_test walfault-shm-6.1 { +do_test walfault-10.1 { set ::shm_state 0 testvfs tvfs - tvfs script shmfault_vfs_cb_6 + tvfs script walfault_10_vfs_cb sqlite3 db test.db -vfs tvfs sqlite3 db2 test.db -vfs tvfs execsql { @@ -360,11 +365,11 @@ PRAGMA wal_autocheckpoint = 0; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(900)); } } {wal 0} -do_test walfault-shm-6.2 { +do_test walfault-10.2 { execsql { PRAGMA wal_autocheckpoint = 0; BEGIN; INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */ @@ -381,11 +386,11 @@ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ COMMIT; } db2 } {0} -do_test walfault-shm-6.3 { +do_test walfault-10.3 { set ::shm_state 1 catchsql { PRAGMA wal_checkpoint } db2 } {1 {disk I/O error}} set ::shm_state 0 db close