From: dan Date: Tue, 11 May 2010 12:19:26 +0000 (+0000) Subject: Changes so that WAL and exclusive-locking mode work together. X-Git-Tag: version-3.7.2~388 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5543759b1b27d9c954673bba3f032f2aa2e10a69;p=thirdparty%2Fsqlite.git Changes so that WAL and exclusive-locking mode work together. FossilOrigin-Name: 71e7b1cf9f4cd02a2a9bc8a3e58acd7a7e3c7e60 --- diff --git a/manifest b/manifest index 332497bed4..07ee6bc4b4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Updates\sfor\sto\sWAL\sTCL\stest\sscripts\sto\ssupport\srunning\son\sWindows. -D 2010-05-11T02:46:17 +C Changes\sso\sthat\sWAL\sand\sexclusive-locking\smode\swork\stogether. +D 2010-05-11T12:19:27 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in a5cad1f8f3e021356bfcc6c77dc16f6f1952bbc3 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -154,7 +154,7 @@ F src/os_common.h 0d6ee583b6ee3185eb9d951f890c6dd03021a08d F src/os_os2.c 8ad77a418630d7dee91d1bb04f79c2096301d3a0 F src/os_unix.c 34fe71c67fce72360411d60fe069c7f0dc612dd0 F src/os_win.c a8fc01d8483be472e495793c01064fd87e56a5c1 -F src/pager.c a47af9c2c9ca425bd68642d61764266331a3323f +F src/pager.c 871ccb53e901dabf92b9b2806f0fbe4a2c039d99 F src/pager.h 934b598583a9d936bb13c37d62a2fe68ac48781c F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e F src/pcache.c ace8f6a5ecd4711cc66a1b23053be7109bd437cf @@ -224,8 +224,8 @@ F src/vdbeblob.c 5327132a42a91e8b7acfb60b9d2c3b1c5c863e0e F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda -F src/wal.c 65d1376c8d5ce500ac1fd1bf49e2287ad04cf6c4 -F src/wal.h b4c42014b5fa3b4e6244ac8c65de7ff67adeb27c +F src/wal.c 7042647fd4c89b789da6dc934550effdf573a290 +F src/wal.h 32f36b2a827b78373658dac5521291485dfa52b6 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c 75fee9e255b62f773fcadd1d1f25b6f63ac7a356 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -762,7 +762,7 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d F test/wal.test 7a100918c45872fa19cfb4413299b9afb08727b6 -F test/wal2.test 0f53c711d6530d3c7aba46752aef9fd44b708c6c +F test/wal2.test 913fc65e533593e3b5dfb193340ac32368da1914 F test/walbak.test a0e45187c7d8928df035dfea29b99b016b21ca3c F test/walcrash.test f6d5fb2bb108876f04848720a488065d9deef69f F test/walfault.test 98df47444944a6db2161eed5cef71d6c00bcb8c3 @@ -813,7 +813,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 6e3735f72cb7d2f4d16c8f9bc59ff159c75243e5 -R 6271f8fbb605ce59c9bcaa6c5c92af66 -U shaneh -Z 6c6a591f5b1ea69fe4a6d39da0e8aca9 +P 6a5630806c87b0f4e5632c37c357f98effd9608a +R ed8f197401fc52de24ef292a838d5248 +U dan +Z ca9adf09cbf258f90f9e808c81428995 diff --git a/manifest.uuid b/manifest.uuid index a244475a7b..9d9a0656d7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6a5630806c87b0f4e5632c37c357f98effd9608a \ No newline at end of file +71e7b1cf9f4cd02a2a9bc8a3e58acd7a7e3c7e60 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index 221668c9fb..47dee68f8b 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1404,8 +1404,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ } pPager->journalOff = 0; pPager->journalStarted = 0; - }else if( pPager->exclusiveMode - || pPager->journalMode==PAGER_JOURNALMODE_PERSIST + }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST + || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) ){ rc = zeroJournalHdr(pPager, hasMaster); pager_error(pPager, rc); @@ -1439,6 +1439,17 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ if( pagerUseWal(pPager) ){ rc2 = sqlite3WalWriteLock(pPager->pWal, 0); pPager->state = PAGER_SHARED; + + /* If the connection was in locking_mode=exclusive mode but is no longer, + ** drop the EXCLUSIVE lock held on the database file. + */ + if( rc2==SQLITE_OK + && !pPager->exclusiveMode + && sqlite3WalExclusiveMode(pPager->pWal, -1) + ){ + sqlite3WalExclusiveMode(pPager->pWal, 0); + rc2 = osUnlock(pPager->fd, SHARED_LOCK); + } }else if( !pPager->exclusiveMode ){ rc2 = osUnlock(pPager->fd, SHARED_LOCK); pPager->state = PAGER_SHARED; @@ -4509,15 +4520,34 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; assert( pPager->state!=PAGER_UNLOCK ); pPager->subjInMemory = (u8)subjInMemory; + if( pPager->state==PAGER_SHARED ){ assert( pPager->pInJournal==0 ); assert( !MEMDB && !pPager->tempFile ); if( pagerUseWal(pPager) ){ + /* If the pager is configured to use locking_mode=exclusive, and an + ** exclusive lock on the database is not already held, obtain it now. + */ + if( pPager->exclusiveMode && !sqlite3WalExclusiveMode(pPager->pWal, -1) ){ + rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK); + pPager->state = PAGER_SHARED; + if( rc!=SQLITE_OK ){ + return rc; + } + sqlite3WalExclusiveMode(pPager->pWal, 1); + } + /* Grab the write lock on the log file. If successful, upgrade to - ** PAGER_EXCLUSIVE state. Otherwise, return an error code to the caller. + ** PAGER_RESERVED state. Otherwise, return an error code to the caller. ** The busy-handler is not invoked if another connection already ** holds the write-lock. If possible, the upper layer will call it. + ** + ** WAL mode sets Pager.state to PAGER_RESERVED when it has an open + ** transaction, but never to PAGER_EXCLUSIVE. This is because in + ** PAGER_EXCLUSIVE state the code to roll back savepoint transactions + ** may copy data from the sub-journal into the database file as well + ** as into the page cache. Which would be incorrect in WAL mode. */ rc = sqlite3WalWriteLock(pPager->pWal, 1); if( rc==SQLITE_OK ){ @@ -4525,6 +4555,9 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ pPager->state = PAGER_RESERVED; pPager->journalOff = 0; } + + assert( rc!=SQLITE_OK || pPager->state==PAGER_RESERVED ); + assert( rc==SQLITE_OK || pPager->state==PAGER_SHARED ); }else{ /* Obtain a RESERVED lock on the database file. If the exFlag parameter ** is true, then immediately upgrade this to an EXCLUSIVE lock. The diff --git a/src/wal.c b/src/wal.c index 94b332041c..8e3c0c6469 100644 --- a/src/wal.c +++ b/src/wal.c @@ -132,6 +132,7 @@ struct Wal { u32 *pWiData; /* Pointer to wal-index content in memory */ u8 lockState; /* SQLITE_SHM_xxxx constant showing lock state */ u8 readerType; /* SQLITE_SHM_READ or SQLITE_SHM_READ_FULL */ + u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ WalIndexHdr hdr; /* Wal-index for current snapshot */ char *zName; /* Name of underlying storage */ }; @@ -217,14 +218,17 @@ static void walChecksumBytes(u8 *aByte, int nByte, u32 *aCksum){ ** in pWal->readerType. */ static int walSetLock(Wal *pWal, int desiredStatus){ - int rc, got; - if( pWal->lockState==desiredStatus ) return SQLITE_OK; - got = pWal->lockState; - rc = pWal->pVfs->xShmLock(pWal->pVfs, pWal->pWIndex, desiredStatus, &got); - pWal->lockState = got; - if( got==SQLITE_SHM_READ_FULL || got==SQLITE_SHM_READ ){ - pWal->readerType = got; - pWal->lockState = SQLITE_SHM_READ; + int rc = SQLITE_OK; /* Return code */ + if( pWal->exclusiveMode || pWal->lockState==desiredStatus ){ + pWal->lockState = desiredStatus; + }else{ + int got = pWal->lockState; + rc = pWal->pVfs->xShmLock(pWal->pVfs, pWal->pWIndex, desiredStatus, &got); + pWal->lockState = got; + if( got==SQLITE_SHM_READ_FULL || got==SQLITE_SHM_READ ){ + pWal->readerType = got; + pWal->lockState = SQLITE_SHM_READ; + } } return rc; } @@ -1231,7 +1235,7 @@ int sqlite3WalWriteLock(Wal *pWal, int op){ walSetLock(pWal, SQLITE_SHM_READ); } } - }else if( ALWAYS( pWal->lockState==SQLITE_SHM_WRITE ) ){ + }else if( pWal->lockState==SQLITE_SHM_WRITE ){ rc = walSetLock(pWal, SQLITE_SHM_READ); } return rc; @@ -1250,18 +1254,20 @@ int sqlite3WalWriteLock(Wal *pWal, int op){ ** function returns SQLITE_OK. */ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ - int unused; - int rc; - Pgno iMax = pWal->hdr.iLastPg; - Pgno iFrame; - - assert( pWal->pWiData==0 ); - rc = walIndexReadHdr(pWal, &unused); - for(iFrame=pWal->hdr.iLastPg+1; rc==SQLITE_OK && iFrame<=iMax; iFrame++){ - assert( pWal->lockState==SQLITE_SHM_WRITE ); - rc = xUndo(pUndoCtx, pWal->pWiData[walIndexEntry(iFrame)]); + int rc = SQLITE_OK; + if( pWal->lockState==SQLITE_SHM_WRITE ){ + int unused; + Pgno iMax = pWal->hdr.iLastPg; + Pgno iFrame; + + assert( pWal->pWiData==0 ); + rc = walIndexReadHdr(pWal, &unused); + for(iFrame=pWal->hdr.iLastPg+1; rc==SQLITE_OK && iFrame<=iMax; iFrame++){ + assert( pWal->lockState==SQLITE_SHM_WRITE ); + rc = xUndo(pUndoCtx, pWal->pWiData[walIndexEntry(iFrame)]); + } + walIndexUnmap(pWal); } - walIndexUnmap(pWal); return rc; } @@ -1500,4 +1506,35 @@ int sqlite3WalCallback(Wal *pWal){ } return (int)ret; } + +/* +** This function is called to set or query the exclusive-mode flag +** associated with the WAL connection passed as the first argument. The +** exclusive-mode flag should be set to indicate that the caller is +** holding an EXCLUSIVE lock on the database file (it does this in +** locking_mode=exclusive mode). If the EXCLUSIVE lock is to be dropped, +** the flag set by this function should be cleared before doing so. +** +** The value of the exclusive-mode flag may only be modified when +** the WAL connection is in READ state. +** +** When the flag is set, this module does not call the VFS xShmLock() +** method to obtain any locks on the wal-index (as it assumes it +** has exclusive access to the wal and wal-index files anyhow). It +** continues to hold (and does not drop) the existing READ lock on +** the wal-index. +** +** To set or clear the flag, the "op" parameter is passed 1 or 0, +** respectively. To query the flag, pass -1. In all cases, the value +** returned is the value of the exclusive-mode flag (after its value +** has been modified, if applicable). +*/ +int sqlite3WalExclusiveMode(Wal *pWal, int op){ + if( op>=0 ){ + assert( pWal->lockState==SQLITE_SHM_READ ); + pWal->exclusiveMode = (u8)op; + } + return pWal->exclusiveMode; +} + #endif /* #ifndef SQLITE_OMIT_WAL */ diff --git a/src/wal.h b/src/wal.h index c1b662d09b..89e94869c1 100644 --- a/src/wal.h +++ b/src/wal.h @@ -96,5 +96,10 @@ int sqlite3WalCheckpoint( */ int sqlite3WalCallback(Wal *pWal); +/* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) +** by the pager layer on the database file. +*/ +int sqlite3WalExclusiveMode(Wal *pWal, int op); + #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ diff --git a/test/wal2.test b/test/wal2.test index 81efbce82e..e5ef2e0780 100644 --- a/test/wal2.test +++ b/test/wal2.test @@ -362,8 +362,219 @@ do_test wal2-5.1 { execsql { PRAGMA wal_checkpoint } set ::locks } {CHECKPOINT UNLOCK} - db close tvfs delete +#------------------------------------------------------------------------- +# This block, test cases wal2-6.*, tests the operation of WAL with +# "PRAGMA locking_mode=EXCLUSIVE" set. +# +# wal2-6.1.*: Changing to WAL mode before setting locking_mode=exclusive. +# +# wal2-6.2.*: Changing to WAL mode after setting locking_mode=exclusive. +# +# wal2-6.3.*: Changing back to rollback mode from WAL mode after setting +# locking_mode=exclusive. +# +# wal2-6.4.*: Check that xShmLock calls are omitted in exclusive locking +# mode. +# +do_test wal2-6.1.1 { + file delete -force test.db test.db-wal test.db-journal + sqlite3 db test.db + execsql { + Pragma Journal_Mode = Wal; + Pragma Locking_Mode = Exclusive; + } +} {wal exclusive} +do_test wal2-6.1.2 { + execsql { PRAGMA lock_status } +} {main unlocked temp closed} +do_test wal2-6.1.3 { + execsql { + BEGIN; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + COMMIT; + PRAGMA lock_status; + } +} {main exclusive temp closed} +do_test wal2-6.1.4 { + execsql { + PRAGMA locking_mode = normal; + PRAGMA lock_status; + } +} {normal main exclusive temp closed} +do_test wal2-6.1.5 { + execsql { + SELECT * FROM t1; + PRAGMA lock_status; + } +} {1 2 main exclusive temp closed} +do_test wal2-6.1.6 { + execsql { + INSERT INTO t1 VALUES(3, 4); + PRAGMA lock_status; + } +} {main shared temp closed} +db close + +do_test wal2-6.2.1 { + file delete -force test.db test.db-wal test.db-journal + sqlite3 db test.db + execsql { + Pragma Locking_Mode = Exclusive; + Pragma Journal_Mode = Wal; + Pragma Lock_Status; + } +} {exclusive wal main exclusive temp closed} +do_test wal2-6.2.2 { + execsql { + BEGIN; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + COMMIT; + Pragma loCK_STATus; + } +} {main exclusive temp closed} +do_test wal2-6.2.3 { + db close + sqlite3 db test.db + execsql { PRAGMA LOCKING_MODE = EXCLUSIVE } +} {exclusive} +do_test wal2-6.2.4 { + execsql { + SELECT * FROM t1; + pragma lock_status; + } +} {1 2 main shared temp closed} +do_test wal2-6.2.5 { + execsql { + INSERT INTO t1 VALUES(3, 4); + pragma lock_status; + } +} {main exclusive temp closed} +do_test wal2-6.2.6 { + execsql { + PRAGMA locking_mode = NORMAL; + pragma lock_status; + } +} {normal main exclusive temp closed} +do_test wal2-6.2.7 { + execsql { + BEGIN IMMEDIATE; COMMIT; + pragma lock_status; + } +} {main shared temp closed} +do_test wal2-6.2.8 { + execsql { + PRAGMA locking_mode = EXCLUSIVE; + BEGIN IMMEDIATE; COMMIT; + PRAGMA locking_mode = NORMAL; + } + execsql { + SELECT * FROM t1; + pragma lock_status; + } +} {1 2 3 4 main exclusive temp closed} +do_test wal2-6.2.9 { + execsql { + INSERT INTO t1 VALUES(5, 6); + SELECT * FROM t1; + pragma lock_status; + } +} {1 2 3 4 5 6 main shared temp closed} +db close + +do_test wal2-6.3.1 { + file delete -force test.db test.db-wal test.db-journal + sqlite3 db test.db + execsql { + PRAGMA journal_mode = WAL; + PRAGMA locking_mode = exclusive; + BEGIN; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('Chico'); + INSERT INTO t1 VALUES('Harpo'); + COMMIT; + } + list [file exists test.db-wal] [file exists test.db-journal] +} {1 0} +do_test wal2-6.3.2 { + execsql { PRAGMA journal_mode = DELETE } + file exists test.db-wal +} {0} +do_test wal2-6.3.3 { + execsql { PRAGMA lock_status } +} {main exclusive temp closed} +do_test wal2-6.3.4 { + execsql { + BEGIN; + INSERT INTO t1 VALUES('Groucho'); + } + list [file exists test.db-wal] [file exists test.db-journal] +} {0 1} +do_test wal2-6.3.5 { + execsql { PRAGMA lock_status } +} {main exclusive temp closed} +do_test wal2-6.3.6 { + execsql { COMMIT } + list [file exists test.db-wal] [file exists test.db-journal] +} {0 1} +do_test wal2-6.3.7 { + execsql { PRAGMA lock_status } +} {main exclusive temp closed} +db close + +do_test wal2-6.4.1 { + file delete -force test.db test.db-wal test.db-journal + proc tvfs_cb {method args} { + set ::shm_file [lindex $args 0] + if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] } + return "SQLITE_OK" + } + testvfs tvfs tvfs_cb + sqlite3 db test.db -vfs tvfs + + execsql { + PRAGMA journal_mode = WAL; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('Leonard'); + INSERT INTO t1 VALUES('Arthur'); + } + + set ::locks [list] + execsql { PRAGMA locking_mode = exclusive } + set ::locks +} {} +do_test wal2-6.4.2 { + execsql { SELECT * FROM t1 } +} {Leonard Arthur} +do_test wal2-6.4.3 { + set ::locks +} {READ} +do_test wal2-6.4.4 { + execsql { + INSERT INTO t1 VALUES('Julius Henry'); + SELECT * FROM t1; + } +} {Leonard Arthur {Julius Henry}} +do_test wal2-6.4.5 { + set ::locks +} {READ} +do_test wal2-6.4.6 { + execsql { + PRAGMA locking_mode = NORMAL; + DELETE FROM t1; + } + set ::locks +} {READ UNLOCK} +do_test wal2-6.4.7 { + set ::locks [list] + execsql { INSERT INTO t1 VALUES('Karl') } + set ::locks +} {READ WRITE READ UNLOCK} +db close + finish_test +