From: dan Date: Tue, 20 Apr 2010 18:53:15 +0000 (+0000) Subject: Use the read and write version fields of the database header to mark a database as... X-Git-Tag: version-3.7.2~455^2~68 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e04dc88be5436c47e49d46217ab1ce4dd2e27e11;p=thirdparty%2Fsqlite.git Use the read and write version fields of the database header to mark a database as operating in wal-mode. FossilOrigin-Name: 96bef18c1411c3e0348295886f105e1646c46320 --- diff --git a/manifest b/manifest index a4e1d8ff29..f648a1e977 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\ssqlite3_log_hook()\sinterface\sfor\sscheduling\scheckpoints. -D 2010-04-19T18:03:52 +C Use\sthe\sread\sand\swrite\sversion\sfields\sof\sthe\sdatabase\sheader\sto\smark\sa\sdatabase\sas\soperating\sin\swal-mode. +D 2010-04-20T18:53:15 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -109,8 +109,8 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c e86634da8c48357a759694c9c7c471125cd8d5a8 F src/bitvec.c 06ad2c36a9c3819c0b9cbffec7b15f58d5d834e0 F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff -F src/btree.c 01559397cbd4a5aa62a822e8ca9ac94b6db14743 -F src/btree.h ad6cff92286f9b02ec32f0b97136e9a544249f37 +F src/btree.c 013cf019484e309e909a667654612b38a02dc2b6 +F src/btree.h dd83041eda10c17daf023257c1fc883b5f71f85a F src/btreeInt.h 22447d259639271774a931cbf66aa55112846681 F src/build.c 11100b66fb97638d2d874c1d34d8db90650bb1d7 F src/callback.c 908f3e0172c3d4058f4ca0acd42c637c52e9669f @@ -154,13 +154,13 @@ F src/os_common.h 240c88b163b02c21a9f21f87d49678a0aa21ff30 F src/os_os2.c 75a8c7b9a00a2cf1a65f9fa4afbc27d46634bb2f F src/os_unix.c 5bf0015cebe2f21635da2af983c348eb88b3b4c1 F src/os_win.c 1c7453c2df4dab26d90ff6f91272aea18bcf7053 -F src/pager.c f9ad51dfa617131240ae77545c0083118324fc81 -F src/pager.h aeaee9ceb4a4a6d15c2163ef4e11a7590fd54b59 +F src/pager.c 1dcf76bead8f1b51809bd8189ef9b02f2c94dc7f +F src/pager.h cee4487ab4f0911dd9f22a40e3cd55afdb7ef444 F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e F src/pcache.c ace8f6a5ecd4711cc66a1b23053be7109bd437cf F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 F src/pcache1.c 6dc1871ce8ead9187161c370a58cd06c84221f76 -F src/pragma.c f12cb58a8aa0d80cfed282ef87a285ed71beb793 +F src/pragma.c bae3ce82ca6d0ac5d3ca3e46b286856d1f0ddd70 F src/prepare.c fd1398cb1da54385ba5bd68d93928f10d10a1d9c F src/printf.c 5f5b65a83e63f2096a541a340722a509fa0240a7 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -214,7 +214,7 @@ F src/update.c c0dc6b75ad28b76b619042d934f337b02acee208 F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685 F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b F src/vacuum.c b1d542c8919d4d11119f78069e1906a1ad07e0ee -F src/vdbe.c 2e2aaa765de667dd15e0462cf853efd1b2f97998 +F src/vdbe.c 304851b32c5f918a7ce92e4e9f94fbde0b604b21 F src/vdbe.h 471f6a3dcec4817ca33596fe7f6654d56c0e75f3 F src/vdbeInt.h 19ebc8c2a2e938340051ee65af3f377fb99102d1 F src/vdbeapi.c 466044df5bc916f778833e927165fd02cdef6086 @@ -758,9 +758,10 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61 F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d -F test/wal.test a56ff378f58b145fd3bf38c277fbfe792cd47bdd +F test/wal.test 273c0006e75c99cb34d0659851e7103fd4748c20 F test/walcrash.test 45cfbab30bb7cbe0b2e9d5cabe90dbcad10cb89b F test/walhook.test 76a559e262f0715c470bade4a8d8333035f8ee47 +F test/walmode.test cd6ee20f08af2d81fddc049f8d7e387a807f067e F test/walslow.test 38076d5fad49e3678027be0f8110e6a32d531dc2 F test/walthread.test 27e44ee6fd02f1f494a24f999c97086af3ab739d F test/where.test de337a3fe0a459ec7c93db16a519657a90552330 @@ -806,7 +807,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 9d51c3b754f0b94fea5ef3d669ad583b93b2b024 -R 8eb45c9f56fe0d743248bffdfb606bc1 +P 9bda601455705475075e33bfa85687bce34b15ff +R b30f3db6dd942a33d471d74ec2e33c70 U dan -Z e82f118f370901abf724c6ac84b89351 +Z d82da3172ce11d2f9fc95cc996cdb180 diff --git a/manifest.uuid b/manifest.uuid index aa29f9aa71..8af34827b8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9bda601455705475075e33bfa85687bce34b15ff \ No newline at end of file +96bef18c1411c3e0348295886f105e1646c46320 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index c8810543a1..069c2d0327 100644 --- a/src/btree.c +++ b/src/btree.c @@ -2261,13 +2261,25 @@ static int lockBtree(BtShared *pBt){ if( memcmp(page1, zMagicHeader, 16)!=0 ){ goto page1_init_failed; } - if( page1[18]>1 ){ + if( page1[18]>2 ){ pBt->readOnly = 1; } - if( page1[19]>1 ){ + if( page1[19]>2 ){ goto page1_init_failed; } + /* If the write version is set to 2, turn on write-ahead logging mode. */ + if( page1[19]==2 ){ + int isOpen = 0; + rc = sqlite3PagerOpenLog(pBt->pPager, &isOpen); + if( rc!=SQLITE_OK ){ + goto page1_init_failed; + }else if( isOpen==0 ){ + releasePage(pPage1); + return SQLITE_OK; + } + } + /* The maximum embedded fraction must be exactly 25%. And the minimum ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data. ** The original design allowed these amounts to vary, but as of @@ -7963,3 +7975,26 @@ void sqlite3BtreeCacheOverflow(BtCursor *pCur){ pCur->isIncrblobHandle = 1; } #endif + +/* +** Set both the "read version" (single byte at byte offset 18) and +** "write version" (single byte at byte offset 19) fields in the database +** header to iVersion. +*/ +int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ + BtShared *pBt = pBtree->pBt; + int rc; /* Return code */ + + assert( pBtree->inTrans==TRANS_WRITE ); + assert( pBt->pPage1 ); + assert( iVersion==1 || iVersion==2 ); + + rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); + if( rc==SQLITE_OK ){ + u8 *aData = pBt->pPage1->aData; + aData[18] = (u8)iVersion; + aData[19] = (u8)iVersion; + } + + return rc; +} diff --git a/src/btree.h b/src/btree.h index aba06c965e..584b463384 100644 --- a/src/btree.h +++ b/src/btree.h @@ -186,6 +186,8 @@ int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); +int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); + #ifndef NDEBUG int sqlite3BtreeCursorIsValid(BtCursor*); #endif diff --git a/src/pager.c b/src/pager.c index d46350afd2..9d1fd0479c 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1405,9 +1405,11 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ /* This branch may be executed with Pager.journalMode==MEMORY if ** a hot-journal was just rolled back. In this case the journal ** file should be closed and deleted. If this connection writes to - ** the database file, it will do so using an in-memory journal. */ + ** the database file, it will do so using an in-memory journal. + */ assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || pPager->journalMode==PAGER_JOURNALMODE_MEMORY + || pPager->journalMode==PAGER_JOURNALMODE_WAL ); sqlite3OsClose(pPager->jfd); if( !pPager->tempFile ){ @@ -3724,54 +3726,6 @@ static int hasHotJournal(Pager *pPager, int *pExists){ return rc; } -/* -** Open a connection to the write-ahead log file for pager pPager. If -** the log connection is already open, this function is a no-op. -*/ -static int pagerOpenLog(Pager *pPager){ - if( !pPager->pLog ){ - int rc; /* Return code */ - - /* Before opening the log file, obtain a SHARED lock on the database - ** file. This lock will not be released until after the log file - ** connection has been closed. The purpose of this lock is to stop - ** any other process from unlinking the log or log-summary files while - ** this connection still has them open. An EXCLUSIVE lock on the - ** database file is required to unlink either of those two files. - */ - assert( pPager->state==PAGER_UNLOCK ); - rc = pager_wait_on_lock(pPager, SHARED_LOCK); - if( rc!=SQLITE_OK ){ - assert( pPager->state==PAGER_UNLOCK ); - return pager_error(pPager, rc); - } - assert( pPager->state>=SHARED_LOCK ); - - /* Open the connection to the log file. If this operation fails, - ** (e.g. due to malloc() failure), unlock the database file and - ** return an error code. - */ - rc = sqlite3LogOpen(pPager->pVfs, pPager->zFilename, &pPager->pLog); - if( rc!=SQLITE_OK ){ - osUnlock(pPager->fd, SQLITE_LOCK_NONE); - pPager->state = PAGER_UNLOCK; - return rc; - } - }else{ - /* If the log file was already open, check that the pager is still holding - ** the required SHARED lock on the database file. - */ -#ifdef SQLITE_DEBUG - int locktype; - sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype); - assert( locktype==SQLITE_LOCK_SHARED ); -#endif - pPager->state = PAGER_SHARED; - } - - return SQLITE_OK; -} - /* ** This function is called to obtain a shared lock on the database file. @@ -3826,16 +3780,9 @@ int sqlite3PagerSharedLock(Pager *pPager){ pager_reset(pPager); } - - if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ + if( pagerUseLog(pPager) ){ int changed = 0; /* True if the cache must be flushed */ - /* Open the log file, if it is not already open. */ - rc = pagerOpenLog(pPager); - if( rc!=SQLITE_OK ){ - return rc; - } - /* Open a log snapshot to read from. */ rc = sqlite3LogOpenSnapshot(pPager->pLog, &changed); if( rc==SQLITE_OK ){ @@ -3846,6 +3793,8 @@ int sqlite3PagerSharedLock(Pager *pPager){ } rc = sqlite3PagerPagecount(pPager, &dummy); } + pPager->state = PAGER_SHARED; + }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){ sqlite3_vfs * const pVfs = pPager->pVfs; int isHotJournal = 0; @@ -4002,6 +3951,10 @@ int sqlite3PagerSharedLock(Pager *pPager){ } } assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED ); + + if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ + pPager->journalMode = PAGER_JOURNALMODE_DELETE; + } } failed: @@ -4384,6 +4337,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ if( rc==SQLITE_OK ){ pPager->dbOrigSize = pPager->dbSize; pPager->state = PAGER_RESERVED; + pPager->journalOff = 0; } }else{ /* Obtain a RESERVED lock on the database file. If the exFlag parameter @@ -4485,7 +4439,7 @@ static int pager_write(PgHdr *pPg){ } if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF - && pPager->journalMode!=PAGER_JOURNALMODE_WAL + && !pagerUseLog(pPager) ){ assert( pPager->useJournal ); rc = pager_open_journal(pPager); @@ -5621,7 +5575,7 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){ ** PAGER_JOURNALMODE_WAL ** ** If the parameter is not _QUERY, then the journal_mode is set to the -** value specified if the change is allowed. The change is disallowed +** value specified if the change is allowed. The change may be disallowed ** for the following reasons: ** ** * An in-memory database can only have its journal_mode set to _OFF @@ -5640,7 +5594,12 @@ int sqlite3PagerJournalMode(Pager *pPager, int eMode){ || eMode==PAGER_JOURNALMODE_WAL || eMode==PAGER_JOURNALMODE_MEMORY ); assert( PAGER_JOURNALMODE_QUERY<0 ); - if( eMode>=0 + + if( eMode==PAGER_JOURNALMODE_WAL + && pPager->journalMode==PAGER_JOURNALMODE_DELETE + ){ + pPager->journalMode = PAGER_JOURNALMODE_WAL; + }else if( eMode>=0 && (pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL) && (!MEMDB || eMode==PAGER_JOURNALMODE_MEMORY||eMode==PAGER_JOURNALMODE_OFF) && !pPager->dbModified @@ -5659,13 +5618,6 @@ int sqlite3PagerJournalMode(Pager *pPager, int eMode){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } - /* Switching into WAL mode can only take place when no - ** locks are held on the database file. - */ - if( eMode==PAGER_JOURNALMODE_WAL && pPager->state!=PAGER_UNLOCK ){ - return (int)pPager->journalMode; - } - pPager->journalMode = (u8)eMode; } return (int)pPager->journalMode; @@ -5713,4 +5665,77 @@ int sqlite3PagerLogCallback(Pager *pPager){ return sqlite3LogCallback(pPager->pLog); } +/* +** Open a connection to the write-ahead log file for pager pPager. If +** the log connection is already open, this function is a no-op. +** +** The caller must be holding a SHARED lock on the database file to call +** this function. +*/ +int sqlite3PagerOpenLog(Pager *pPager, int *pisOpen){ + int rc = SQLITE_OK; /* Return code */ + +#ifdef SQLITE_DEBUG + int locktype; + sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype); + assert( locktype==SQLITE_LOCK_SHARED ); + assert( pPager->state>=PAGER_SHARED ); +#endif + + if( !pPager->pLog ){ + + /* Open the connection to the log file. If this operation fails, + ** (e.g. due to malloc() failure), unlock the database file and + ** return an error code. + */ + rc = sqlite3LogOpen(pPager->pVfs, pPager->zFilename, &pPager->pLog); + if( rc==SQLITE_OK ){ + pPager->journalMode = PAGER_JOURNALMODE_WAL; + } + }else{ + *pisOpen = 1; + } + + return rc; +} + + +/* +** This function is called to close the connection to the log file prior +** to switching from WAL to rollback mode. +** +** Before closing the log file, this function attempts to take an +** EXCLUSIVE lock on the database file. If this cannot be obtained, an +** error (SQLITE_BUSY) is returned and the log connection is not closed. +** If successful, the EXCLUSIVE lock is not released before returning. +*/ +int sqlite3PagerCloseLog(Pager *pPager){ + int rc = SQLITE_OK; + if( pPager->pLog ){ + + /* Try to obtain an EXCLUSIVE lock on the database file. */ + rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); + + /* If the EXCLUSIVE lock was obtained, checkpoint and close the log. */ + if( rc==SQLITE_OK ){ + rc = sqlite3LogClose(pPager->pLog, pPager->fd, + (pPager->noSync ? 0 : pPager->sync_flags), + (u8*)pPager->pTmpSpace + ); + pPager->pLog = 0; + } + + /* Make sure the EXCLUSIVE lock has not been lost somehow */ +#ifdef SQLITE_DEBUG + { + int locktype; + sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype); + assert( locktype==SQLITE_LOCK_EXCLUSIVE ); + } +#endif + + } + return rc; +} + #endif /* SQLITE_OMIT_DISKIO */ diff --git a/src/pager.h b/src/pager.h index 17d61a500f..7b760c9b1a 100644 --- a/src/pager.h +++ b/src/pager.h @@ -136,6 +136,8 @@ int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager); int sqlite3PagerLogCallback(Pager *pPager); +int sqlite3PagerOpenLog(Pager *pPager, int *pisOpen); +int sqlite3PagerCloseLog(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); diff --git a/src/pragma.c b/src/pragma.c index 137ff510d9..1dfc90d2b6 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -257,6 +257,28 @@ static const char *actionName(u8 action){ } #endif + +/* +** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants +** defined in pager.h. This function returns the associated lowercase +** journal-mode name. +*/ +const char *sqlite3JournalModename(int eMode){ + static char * const azModeName[] = { + "delete", "persist", "off", "truncate", "memory", "wal" + }; + assert( PAGER_JOURNALMODE_DELETE==0 ); + assert( PAGER_JOURNALMODE_PERSIST==1 ); + assert( PAGER_JOURNALMODE_OFF==2 ); + assert( PAGER_JOURNALMODE_TRUNCATE==3 ); + assert( PAGER_JOURNALMODE_MEMORY==4 ); + assert( PAGER_JOURNALMODE_WAL==5 ); + assert( eMode>=0 && eMode<=ArraySize(azModeName) ); + + if( eMode==ArraySize(azModeName) ) return 0; + return azModeName[eMode]; +} + /* ** Process a pragma statement. ** @@ -513,18 +535,21 @@ void sqlite3Pragma( ** PRAGMA [database.]journal_mode = (delete|persist|off|truncate|memory) */ if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){ - int eMode; - static char * const azModeName[] = { - "delete", "persist", "off", "truncate", "memory", "wal" - }; + int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */ + + sqlite3VdbeSetNumCols(v, 1); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC); if( zRight==0 ){ eMode = PAGER_JOURNALMODE_QUERY; }else{ + const char *zMode; int n = sqlite3Strlen30(zRight); - eMode = sizeof(azModeName)/sizeof(azModeName[0]) - 1; - while( eMode>=0 && sqlite3StrNICmp(zRight, azModeName[eMode], n)!=0 ){ - eMode--; + for(eMode=0; (zMode = sqlite3JournalModename(eMode)); eMode++){ + if( sqlite3StrNICmp(zRight, zMode, n)==0 ) break; + } + if( !zMode ){ + eMode = PAGER_JOURNALMODE_QUERY; } } if( pId2->n==0 && eMode==PAGER_JOURNALMODE_QUERY ){ @@ -533,40 +558,32 @@ void sqlite3Pragma( ** the journal-mode of the main database). */ eMode = db->dfltJournalMode; + sqlite3VdbeAddOp2(v, OP_String8, 0, 1); + sqlite3VdbeChangeP4(v, -1, sqlite3JournalModename(eMode), P4_STATIC); }else{ - Pager *pPager; - if( pId2->n==0 ){ + int ii; + + if( pId2->n==0 && eMode!=PAGER_JOURNALMODE_WAL ){ /* This indicates that no database name was specified as part ** of the PRAGMA command. In this case the journal-mode must be ** set on all attached databases, as well as the main db file. ** ** Also, the sqlite3.dfltJournalMode variable is set so that ** any subsequently attached databases also use the specified - ** journal mode. + ** journal mode. Except, the default journal mode is never set + ** to WAL. */ - int ii; - assert(pDb==&db->aDb[0]); - for(ii=1; iinDb; ii++){ - if( db->aDb[ii].pBt ){ - pPager = sqlite3BtreePager(db->aDb[ii].pBt); - sqlite3PagerJournalMode(pPager, eMode); - } - } db->dfltJournalMode = (u8)eMode; } - pPager = sqlite3BtreePager(pDb->pBt); - eMode = sqlite3PagerJournalMode(pPager, eMode); + + for(ii=db->nDb-1; ii>=0; ii--){ + if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){ + sqlite3VdbeUsesBtree(v, ii); + sqlite3VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode); + } + } } - assert( eMode==PAGER_JOURNALMODE_DELETE - || eMode==PAGER_JOURNALMODE_TRUNCATE - || eMode==PAGER_JOURNALMODE_PERSIST - || eMode==PAGER_JOURNALMODE_OFF - || eMode==PAGER_JOURNALMODE_WAL - || eMode==PAGER_JOURNALMODE_MEMORY ); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC); - sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, - azModeName[eMode], P4_STATIC); + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); }else diff --git a/src/vdbe.c b/src/vdbe.c index 42562cee09..cc53c9bff1 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -5187,6 +5187,9 @@ case OP_AggFinal: { } /* Opcode: Checkpoint P1 * * * * +** +** Checkpoint database P1. This is a no-op if P1 is not currently in +** WAL mode. */ case OP_Checkpoint: { Btree *pBt; /* Btree to checkpoint */ @@ -5198,6 +5201,84 @@ case OP_Checkpoint: { break; }; +/* Opcode: JournalMode P1 P2 P3 * * +** +** Change the journal mode of database P1 to P3. P3 must be one of the +** PAGER_JOURNALMODE_XXX values. If changing between the various rollback +** modes (delete, truncate, persist, off and memory), this is a simple +** operation. No IO is required. +** +** If changing into or out of WAL mode the procedure is more complicated. +** +** Write a string containing the final journal-mode to register P2. +*/ +case OP_JournalMode: { + Btree *pBt; /* Btree to change journal mode of */ + Pager *pPager; /* Pager associated with pBt */ + int eNew = pOp->p3; /* New journal mode */ + + assert( eNew==PAGER_JOURNALMODE_DELETE + || eNew==PAGER_JOURNALMODE_TRUNCATE + || eNew==PAGER_JOURNALMODE_PERSIST + || eNew==PAGER_JOURNALMODE_OFF + || eNew==PAGER_JOURNALMODE_MEMORY + || eNew==PAGER_JOURNALMODE_WAL + || eNew==PAGER_JOURNALMODE_QUERY + ); + assert( pOp->p1>=0 && pOp->p1nDb ); + assert( (p->btreeMask & (1<p1))!=0 ); + + pBt = db->aDb[pOp->p1].pBt; + pPager = sqlite3BtreePager(pBt); + + if( eNew!=PAGER_JOURNALMODE_QUERY ){ + int eOld = sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_QUERY); + if( (eNew!=eOld) + && (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL) + ){ + if( !db->autoCommit || db->activeVdbeCnt>1 ){ + rc = SQLITE_ERROR; + sqlite3SetString(&p->zErrMsg, db, + "cannot change %s wal mode from within a transaction", + (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") + ); + }else{ + + /* If leaving WAL mode, close the log file. If successful, the call to + ** PagerCloseLog() checkpoints and deletes the write-ahead-log file. + ** An EXCLUSIVE lock is still held on the database file after returning. + */ + if( eOld==PAGER_JOURNALMODE_WAL ){ + rc = sqlite3PagerCloseLog(pPager); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + sqlite3PagerJournalMode(pPager, eNew); + }else{ + sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_DELETE); + } + + /* Open a transaction on the database file. Regardless of the journal + ** mode, this transaction always uses a rollback journal. + */ + assert( sqlite3BtreeIsInTrans(pBt)==0 ); + rc = sqlite3BtreeBeginTrans(pBt, 2); + assert( rc==SQLITE_OK || eOld!=PAGER_JOURNALMODE_WAL ); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + } + } + } + + eNew = sqlite3PagerJournalMode(pPager, eNew); + pOut = &aMem[pOp->p2]; + pOut->flags = MEM_Str|MEM_Static|MEM_Term; + pOut->z = sqlite3JournalModename(eNew); + pOut->n = sqlite3Strlen30(pOut->z); + pOut->enc = SQLITE_UTF8; + sqlite3VdbeChangeEncoding(pOut, encoding); + break; +}; + #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) /* Opcode: Vacuum * * * * * ** diff --git a/test/wal.test b/test/wal.test index fb21d820f9..bb538b120f 100644 --- a/test/wal.test +++ b/test/wal.test @@ -31,6 +31,7 @@ proc blob {nByte} { proc sqlite3_wal {args} { eval sqlite3 $args + [lindex $args 0] eval { PRAGMA page_size = 1024 } [lindex $args 0] eval { PRAGMA journal_mode = wal } [lindex $args 0] eval { PRAGMA synchronous = normal } [lindex $args 0] function blob blob @@ -60,14 +61,19 @@ do_test wal-0.1 { execsql { PRAGMA synchronous = normal } execsql { PRAGMA journal_mode = wal } } {wal} +do_test wal-0.2 { + file size test.db +} {1024} do_test wal-1.0 { execsql { BEGIN; CREATE TABLE t1(a, b); } - list [file exists test.db-journal] [file exists test.db-wal] -} {0 1} + list [file exists test.db-journal] \ + [file exists test.db-wal] \ + [file size test.db] +} {0 1 1024} do_test wal-1.1 { execsql COMMIT list [file exists test.db-journal] [file exists test.db-wal] @@ -197,9 +203,10 @@ foreach sector {512 4096} { foreach pgsz {512 1024 2048 4096} { file delete -force test.db test.db-wal do_test wal-6.$sector.$pgsz.1 { - sqlite3_wal db test.db -vfs devsym + sqlite3 db test.db -vfs devsym execsql " - PRAGMA page_size = $pgsz ; + PRAGMA page_size = $pgsz; + PRAGMA journal_mode = wal; " execsql " CREATE TABLE t1(a, b); @@ -224,7 +231,7 @@ do_test wal-7.1 { INSERT INTO t1 VALUES(1, 2); } list [file size test.db] [file size test.db-wal] -} [list 0 [log_file_size 3 1024]] +} [list 1024 [log_file_size 3 1024]] do_test wal-7.2 { execsql { PRAGMA checkpoint } list [file size test.db] [file size test.db-wal] @@ -235,11 +242,17 @@ do_test wal-7.2 { # do_test wal-8.1 { reopen_db + catch { db close } + file delete -force test.db test.db-wal + + sqlite3 db test.db + db function blob blob execsql { PRAGMA auto_vacuum = 1; + PRAGMA journal_mode = wal; PRAGMA auto_vacuum; } -} {1} +} {wal 1} do_test wal-8.2 { execsql { PRAGMA page_size = 1024; @@ -269,7 +282,6 @@ do_test wal-8.3 { do_test wal-9.1 { reopen_db execsql { - PRAGMA page_size = 1024; CREATE TABLE t1(x PRIMARY KEY); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 VALUES(blob(900)); @@ -282,7 +294,7 @@ do_test wal-9.1 { INSERT INTO t1 SELECT blob(900) FROM t1; /* 256 */ } file size test.db -} 0 +} 1024 do_test wal-9.2 { sqlite3_wal db2 test.db execsql {PRAGMA integrity_check } db2 @@ -560,7 +572,7 @@ do_test wal-11.1 { CREATE TABLE t1(x PRIMARY KEY); } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] -} {0 3} +} {1 3} do_test wal-11.2 { execsql { PRAGMA checkpoint } list [expr [file size test.db]/1024] [file size test.db-wal] @@ -613,7 +625,7 @@ do_test wal-11.10 { SELECT count(*) FROM t1; } list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [log_file_size 35 1024]] +} [list 37 [log_file_size 37 1024]] do_test wal-11.11 { execsql { SELECT count(*) FROM t1; @@ -623,7 +635,7 @@ do_test wal-11.11 { } {32 16} do_test wal-11.12 { list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [log_file_size 35 1024]] +} [list 37 [log_file_size 37 1024]] do_test wal-11.13 { execsql { INSERT INTO t1 VALUES( blob(900) ); @@ -633,7 +645,7 @@ do_test wal-11.13 { } {17 ok} do_test wal-11.14 { list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [log_file_size 35 1024]] +} [list 37 [log_file_size 37 1024]] #------------------------------------------------------------------------- @@ -650,11 +662,12 @@ do_test wal-12.1 { INSERT INTO t1 VALUES('A', 1); } list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 0 [log_file_size 5 1024]] +} [list 1 [log_file_size 5 1024]] do_test wal-12.2 { db close - sqlite3_wal db test.db + sqlite3 db test.db execsql { + PRAGMA synchronous = normal; UPDATE t1 SET y = 0 WHERE x = 'A'; } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] @@ -668,7 +681,6 @@ do_test wal-12.4 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db -breakpoint execsql { SELECT * FROM t2 } db2 } {B 1} db2 close @@ -695,6 +707,5 @@ do_test wal-12.4 { } {B 2} db2 close - finish_test diff --git a/test/walmode.test b/test/walmode.test new file mode 100644 index 0000000000..1a3af3a705 --- /dev/null +++ b/test/walmode.test @@ -0,0 +1,25 @@ +# 2010 April 19 +# +# 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. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the operation of the library in +# "PRAGMA journal_mode=WAL" mode. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test walmode-1.1 { + execsql { + PRAGMA journal_mode = wal; + } +} {wal} + +finish_test