From 4edc6bf3eed92fc5a5547406dec6760c9b062fcf Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 10 May 2011 17:31:29 +0000 Subject: [PATCH 1/5] Add experimental support for read-only connections to WAL databases. FossilOrigin-Name: bb59f9862da45d25fb51d7821130854828c91c98 --- manifest | 26 ++++++---- manifest.uuid | 2 +- src/os_unix.c | 51 +++++++++++++----- src/sqlite.h.in | 4 ++ src/test1.c | 3 ++ src/wal.c | 59 +++++++++++++++------ test/lock_common.tcl | 4 +- test/walro.test | 121 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 227 insertions(+), 43 deletions(-) create mode 100644 test/walro.test diff --git a/manifest b/manifest index daee9754e7..50889411e1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Return\sa\ssuitable\serror\smessage\sif\sthe\smode=\sargument\sto\sa\sURI\sspecifies\na\shigher\smode\sthan\swhat\sis\sallowed\sby\scontext.\s\sOther\sminor\scleanups\sfor\nthe\sURI\sparsing\slogic. -D 2011-05-09T19:20:17.775 +C Add\sexperimental\ssupport\sfor\sread-only\sconnections\sto\sWAL\sdatabases. +D 2011-05-10T17:31:29.338 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -163,7 +163,7 @@ F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 -F src/os_unix.c 2c67d126874b78eb427371db4793f0e8fbc7448b +F src/os_unix.c 03630dd062c3d1fb9f25e2a227048b709c5babff F src/os_win.c ff0e14615a5086fa5ba6926e4ec0dc9cfb5a1a84 F src/pager.c 24b689bc3639d534f5fb292d2c68038b1e720527 F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 @@ -179,14 +179,14 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79 -F src/sqlite.h.in 41a0e4bc842917226e170273f64b95717a63270a +F src/sqlite.h.in e7bbcb330ced6b5e25c9db8089c2c77aaefadf7d F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 F src/sqliteInt.h b34bd64a7ade4808fcc301e0bb67ef5051ea49c6 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c 501c9a200fd998a268be475be5858febc90b725b -F src/test1.c f506164085bc4cbbb4b5c14b9c6de153f1aeafc5 +F src/test1.c 6ae026cd9d2b1b1e95a372a7460d091705db645d F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432 F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7 @@ -245,7 +245,7 @@ F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562 F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114 F src/vtab.c 48dcef8bc757c2e7b488f68b5ddebb1650da2450 -F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794 +F src/wal.c 2574b16ee88b1934e97f63ae38025dc48ef45327 F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c 55403ce19c506be6a321c7f129aff693d6103db5 @@ -547,7 +547,7 @@ F test/lock4.test c82268c031d39345d05efa672f80b025481b3ae5 F test/lock5.test b2abb5e711bc59b0eae00f6c97a36ec9f458fada F test/lock6.test ad5b387a3a8096afd3c68a55b9535056431b0cf5 F test/lock7.test 64006c84c1c616657e237c7ad6532b765611cf64 -F test/lock_common.tcl d279887a0ab16cdb6d935c1203e64113c5a000e9 +F test/lock_common.tcl 0c270b121d40959fa2f3add382200c27045b3d95 F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2 F test/main.test 9d7bbfcc1b52c88ba7b2ba6554068ecf9939f252 F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9 @@ -883,6 +883,7 @@ F test/walfault.test 58fce626359c9376fe35101b5c0f2df8040aa839 F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483 F test/walmode.test 22ddccd073c817ac9ead62b88ac446e8dedc7d2c F test/walnoshm.test a074428046408f4eb5c6a00e09df8cc97ff93317 +F test/walro.test 0fb4c79a9dfa1d14f46859e469d3b4844480cd9d F test/walshared.test 6dda2293880c300baf5d791c307f653094585761 F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933 F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c @@ -935,7 +936,10 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P ca3797d4967361e31a8a5ce1ce8190b095f3ed4c -R fbfaa47316ae48b91e92f26cbe2f2742 -U drh -Z 82e3dc394cc5052bfb9a5a6a21e4f298 +P d9bc1c7fe0ca5f6973a85827330958f4d09f8171 +R 864e9c4ee7a6cf5f8240415fb38d1af8 +T *branch * wal-readonly +T *sym-wal-readonly * +T -sym-trunk * +U dan +Z 321776a9f8d852e48652ad186c3a479b diff --git a/manifest.uuid b/manifest.uuid index 2b8787f1e2..e39ce5418f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d9bc1c7fe0ca5f6973a85827330958f4d09f8171 \ No newline at end of file +bb59f9862da45d25fb51d7821130854828c91c98 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index a760e2c147..d463e5917f 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -212,6 +212,7 @@ struct unixFile { UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ + int readOnlyShm; /* True to open shared-memory read-only */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ @@ -3452,6 +3453,10 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ case SQLITE_FCNTL_SIZE_HINT: { return fcntlSizeHint((unixFile *)id, *(i64 *)pArg); } + case SQLITE_FCNTL_READONLY_SHM: { + ((unixFile*)id)->readOnlyShm = (pArg!=0); + return SQLITE_OK; + } #ifndef NDEBUG /* The pager calls this method to signal that it has done ** a rollback and that the database is therefore unchanged and @@ -3541,6 +3546,7 @@ struct unixShmNode { char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ + u8 readOnly; /* True if this is a read-only mapping */ #ifdef SQLITE_DEBUG u8 exclMask; /* Mask of exclusive locks held */ u8 sharedMask; /* Mask of shared locks held */ @@ -3780,29 +3786,48 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ } if( pInode->bProcessLock==0 ){ - pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT, - (sStat.st_mode & 0777)); + int flags = (pDbFd->readOnlyShm ? O_RDONLY : O_RDWR|O_CREAT); + pShmNode->h = robust_open(zShmFilename, flags, (sStat.st_mode & 0777)); if( pShmNode->h<0 ){ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); goto shm_open_err; } + pShmNode->readOnly = pDbFd->readOnlyShm; /* Check to see if another process is holding the dead-man switch. - ** If not, truncate the file to zero length. - */ - rc = SQLITE_OK; - if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ - if( robust_ftruncate(pShmNode->h, 0) ){ - rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); + ** If not, zero the first few bytes of the shared-memory file to make + ** sure it is not mistaken for valid by code in wal.c. Except, if this + ** is a read-only connection to the shared-memory then it is not possible + ** to check check if another process is holding a read-lock on the DMS + ** byte, as we cannot attempt a write-lock via a read-only file + ** descriptor. So in this case, we just assume the shared-memory + ** contents are Ok and proceed. */ + if( pShmNode->readOnly==0 ){ + rc = SQLITE_OK; + if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ + if( pDbFd->readOnlyShm ){ + rc = SQLITE_IOERR_SHMOPEN; + }else if( 4!=osWrite(pShmNode->h, "\00\00\00\00", 4) ){ + rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); + } } + if( rc==SQLITE_OK ){ + rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); + } + if( rc ) goto shm_open_err; } - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); - } - if( rc ) goto shm_open_err; } } + /* If the unixShmNode is read-only, but SQLITE_FCNTL_READONLY_SHM has not + ** been set for file-descriptor pDbFd, return an error. The wal.c module + ** will then call this function again with SQLITE_FCNTL_READONLY_SHM set. + */ + else if( pShmNode->readOnly && !pDbFd->readOnlyShm ){ + rc = SQLITE_IOERR_SHMOPEN; + goto shm_open_err; + } + /* Make the new connection a child of the unixShmNode */ p->pShmNode = pShmNode; #ifdef SQLITE_DEBUG @@ -3923,7 +3948,7 @@ static int unixShmMap( while(pShmNode->nRegion<=iRegion){ void *pMem; if( pShmNode->h>=0 ){ - pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, + pMem = mmap(0, szRegion, PROT_READ|(!pShmNode->readOnly?PROT_WRITE:0), MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion ); if( pMem==MAP_FAILED ){ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index d78daa7672..af88e4e726 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -454,6 +454,9 @@ int sqlite3_exec( #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) +#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) +#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) + /* ** CAPI3REF: Flags For File Open Operations ** @@ -742,6 +745,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 #define SQLITE_FCNTL_SYNC_OMITTED 8 +#define SQLITE_FCNTL_READONLY_SHM 9 /* diff --git a/src/test1.c b/src/test1.c index 8e30123433..c25fd2289d 100644 --- a/src/test1.c +++ b/src/test1.c @@ -163,6 +163,9 @@ const char *sqlite3TestErrorName(int rc){ case SQLITE_IOERR_CHECKRESERVEDLOCK: zName = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; case SQLITE_IOERR_LOCK: zName = "SQLITE_IOERR_LOCK"; break; + + case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break; + case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break; default: zName = "SQLITE_Unknown"; break; } return zName; diff --git a/src/wal.c b/src/wal.c index 51ea18fb21..707fe12976 100644 --- a/src/wal.c +++ b/src/wal.c @@ -420,6 +420,7 @@ struct Wal { u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ u8 readOnly; /* True if the WAL file is open read-only */ + u8 readOnlyShm; /* True if the SHM file is open read-only */ WalIndexHdr hdr; /* Wal-index header for current transaction */ const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ @@ -528,6 +529,16 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); + if( rc==SQLITE_CANTOPEN && iPage==0 ){ + sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_READONLY_SHM, (void*)1); + rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, + pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] + ); + if( rc==SQLITE_OK ){ + pWal->readOnly = pWal->readOnlyShm = 1; + } + sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_READONLY_SHM, (void*)0); + } } } @@ -772,6 +783,7 @@ static void walUnlockShared(Wal *pWal, int lockIdx){ } static int walLockExclusive(Wal *pWal, int lockIdx, int n){ int rc; + assert( pWal->readOnlyShm==0 ); if( pWal->exclusiveMode ) return SQLITE_OK; rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); @@ -781,6 +793,7 @@ static int walLockExclusive(Wal *pWal, int lockIdx, int n){ return rc; } static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ + assert( pWal->readOnlyShm==0 ); if( pWal->exclusiveMode ) return; (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); @@ -1056,6 +1069,7 @@ static int walIndexRecover(Wal *pWal){ assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); assert( pWal->writeLock ); + assert( pWal->readOnlyShm==0 ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; nLock = SQLITE_SHM_NLOCK - iLock; rc = walLockExclusive(pWal, iLock, nLock); @@ -1904,24 +1918,31 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); /* If the first attempt failed, it might have been due to a race - ** with a writer. So get a WRITE lock and try again. + ** with a writer. So lock the WAL_WRITE_LOCK byte and try again. */ assert( badHdr==0 || pWal->writeLock==0 ); - if( badHdr && SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ - pWal->writeLock = 1; - if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ - badHdr = walIndexTryHdr(pWal, pChanged); - if( badHdr ){ - /* If the wal-index header is still malformed even while holding - ** a WRITE lock, it can only mean that the header is corrupted and - ** needs to be reconstructed. So run recovery to do exactly that. - */ - rc = walIndexRecover(pWal); - *pChanged = 1; + if( badHdr ){ + if( pWal->readOnlyShm ){ + if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ + walUnlockShared(pWal, WAL_WRITE_LOCK); + rc = SQLITE_READONLY_RECOVERY; } + }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ + pWal->writeLock = 1; + if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ + badHdr = walIndexTryHdr(pWal, pChanged); + if( badHdr ){ + /* If the wal-index header is still malformed even while holding + ** a WRITE lock, it can only mean that the header is corrupted and + ** needs to be reconstructed. So run recovery to do exactly that. + */ + rc = walIndexRecover(pWal); + *pChanged = 1; + } + } + pWal->writeLock = 0; + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } - pWal->writeLock = 0; - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } /* If the header is read successfully, check the version number to make @@ -2108,7 +2129,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ } /* There was once an "if" here. The extra "{" is to preserve indentation. */ { - if( mxReadMark < pWal->hdr.mxFrame || mxI==0 ){ + if( pWal->readOnlyShm==0 && (mxReadMark < pWal->hdr.mxFrame || mxI==0) ){ for(i=1; ireadOnlyShm && rc==SQLITE_OK) ); + return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK; } rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); @@ -2359,6 +2381,7 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){ if( pWal->readOnly ){ return SQLITE_READONLY; } + assert( pWal->readOnlyShm==0 ); /* Only one writer allowed at a time. Get the write lock. Return ** SQLITE_BUSY if unable. @@ -2748,6 +2771,10 @@ int sqlite3WalCheckpoint( assert( pWal->writeLock==0 ); WALTRACE(("WAL%p: checkpoint begins\n", pWal)); + if( pWal->readOnlyShm ){ + return SQLITE_READONLY; + } + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); if( rc ){ /* Usually this is SQLITE_BUSY meaning that another thread or process diff --git a/test/lock_common.tcl b/test/lock_common.tcl index 21f584264a..bc1eb86bdc 100644 --- a/test/lock_common.tcl +++ b/test/lock_common.tcl @@ -55,8 +55,8 @@ proc do_multiclient_test {varname script} { uplevel set $varname $tn uplevel $script - code2 { db2 close } - code3 { db3 close } + catch { code2 { db2 close } } + catch { code3 { db3 close } } catch { close $::code2_chan } catch { close $::code3_chan } catch { db close } diff --git a/test/walro.test b/test/walro.test new file mode 100644 index 0000000000..b558622cad --- /dev/null +++ b/test/walro.test @@ -0,0 +1,121 @@ +# 2011 May 09 +# +# 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 contains tests for using WAL databases in read-only mode. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +set ::testprefix walro + + +do_multiclient_test tn { + # These tests are only going to work on unix. + # + if {$tcl_platform(platform) != "unix"} continue + + # Do not run tests with the connections in the same process. + # + if {$tn==2} continue + + # Close all connections and delete the database. + # + code1 { db close } + code2 { db2 close } + code3 { db3 close } + forcedelete test.db + forcedelete walro + + file mkdir walro + + do_test 1.1.1 { + code2 { sqlite3 db2 test.db } + sql2 { + PRAGMA journal_mode = WAL; + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES('a', 'b'); + } + file exists test.db-shm + } {1} + + do_test 1.1.2 { + file attributes test.db-shm -permissions r--r--r-- + code1 { sqlite3 db test.db } + } {} + + do_test 1.1.3 { sql1 "SELECT * FROM t1" } {a b} + do_test 1.1.4 { sql2 "INSERT INTO t1 VALUES('c', 'd')" } {} + do_test 1.1.5 { sql1 "SELECT * FROM t1" } {a b c d} + + # Check that the read-only connection cannot write or checkpoint the db. + # + do_test 1.1.6 { + csql1 "INSERT INTO t1 VALUES('e', 'f')" + } {1 {attempt to write a readonly database}} + do_test 1.1.7 { + csql1 "PRAGMA wal_checkpoint" + } {1 {attempt to write a readonly database}} + + do_test 1.1.9 { sql2 "INSERT INTO t1 VALUES('e', 'f')" } {} + do_test 1.1.10 { sql1 "SELECT * FROM t1" } {a b c d e f} + + do_test 1.1.11 { + sql2 { + INSERT INTO t1 VALUES('g', 'h'); + PRAGMA wal_checkpoint; + } + set {} {} + } {} + do_test 1.1.12 { sql1 "SELECT * FROM t1" } {a b c d e f g h} + do_test 1.1.13 { sql2 "INSERT INTO t1 VALUES('i', 'j')" } {} + + do_test 1.2.1 { + code2 { db2 close } + code1 { db close } + list [file exists test.db-wal] [file exists test.db-shm] + } {1 1} + do_test 1.2.2 { + code1 { sqlite3 db test.db } + sql1 { SELECT * FROM t1 } + } {a b c d e f g h i j} + + do_test 1.2.3 { + code1 { db close } + file attributes test.db-shm -permissions rw-r--r-- + hexio_write test.db-shm 0 01020304 + file attributes test.db-shm -permissions r--r--r-- + code1 { sqlite3 db test.db } + csql1 { SELECT * FROM t1 } + } {1 {attempt to write a readonly database}} + do_test 1.2.4 { + code1 { sqlite3_extended_errcode db } + } {SQLITE_READONLY_RECOVERY} + + do_test 1.2.5 { + file attributes test.db-shm -permissions rw-r--r-- + code2 { sqlite3 db2 test.db } + sql2 "SELECT * FROM t1" + } {a b c d e f g h i j} + file attributes test.db-shm -permissions r--r--r-- + do_test 1.2.6 { sql1 "SELECT * FROM t1" } {a b c d e f g h i j} + + do_test 1.2.7 { + sql2 { + PRAGMA wal_checkpoint; + INSERT INTO t1 VALUES('k', 'l'); + } + set {} {} + } {} + do_test 1.2.8 { sql1 "SELECT * FROM t1" } {a b c d e f g h i j k l} +} + +finish_test From b6d2f9c5ef0d6fa71b744bf5ccfb8ac83b5ed060 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 11 May 2011 14:57:33 +0000 Subject: [PATCH 2/5] Only open a read-only connection to shared-memory if the "readonly_shm=1" option is specified as part of the database file URI (and if a read-write connection fails). FossilOrigin-Name: 671ba5fc59f7a958e5a4138d2425b1173a442ad7 --- manifest | 31 ++++++++++++++----------------- manifest.uuid | 2 +- src/attach.c | 6 ++++-- src/btree.h | 1 + src/main.c | 13 +++++++++++-- src/pager.c | 7 +++++-- src/pager.h | 1 + src/sqliteInt.h | 2 +- src/wal.c | 14 +++++++++++--- src/wal.h | 4 ++-- test/walro.test | 13 ++++++++++--- 11 files changed, 61 insertions(+), 33 deletions(-) diff --git a/manifest b/manifest index 50889411e1..3d33b8171b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sexperimental\ssupport\sfor\sread-only\sconnections\sto\sWAL\sdatabases. -D 2011-05-10T17:31:29.338 +C Only\sopen\sa\sread-only\sconnection\sto\sshared-memory\sif\sthe\s"readonly_shm=1"\soption\sis\sspecified\sas\spart\sof\sthe\sdatabase\sfile\sURI\s(and\sif\sa\sread-write\sconnection\sfails). +D 2011-05-11T14:57:33.029 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -117,13 +117,13 @@ F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F src/alter.c 280f5c04b11b492703a342222b3de0a999445280 F src/analyze.c a425d62e8fa9ebcb4359ab84ff0c62c6563d2e2a -F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f +F src/attach.c a87bfb77a720f7aa02791434aacfd9bc8feb50cc F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 986c15232757f2873dff35ee3b35cbf935fc573c F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btree.c 26f8a9d6169413c5682b89b5397d20437b653154 -F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce +F src/btree.h d796dc4030b3cce7f5a0cc4f2f986d2befa6b8ac F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3 F src/build.c 0132bc6631fa617a1d28ef805921f6dbac18a514 F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a @@ -144,7 +144,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 3ae0d52da013a6326310655be6473fd472347b85 -F src/main.c f00cee5a27f5df5ed536e7043cb451b3c85ce65c +F src/main.c 167fb3285720917e4d1a5c633f558c81b751eca3 F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206 @@ -165,8 +165,8 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 F src/os_unix.c 03630dd062c3d1fb9f25e2a227048b709c5babff F src/os_win.c ff0e14615a5086fa5ba6926e4ec0dc9cfb5a1a84 -F src/pager.c 24b689bc3639d534f5fb292d2c68038b1e720527 -F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 +F src/pager.c 4056376f83f85cea9922a11161087c529e39f7dc +F src/pager.h 34c6b029446f06f40847746d22faac0d354dd909 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/pcache.c 09d38c44ab275db581f7a2f6ff8b9bc7f8c0faaa F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 @@ -181,7 +181,7 @@ F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79 F src/sqlite.h.in e7bbcb330ced6b5e25c9db8089c2c77aaefadf7d F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 -F src/sqliteInt.h b34bd64a7ade4808fcc301e0bb67ef5051ea49c6 +F src/sqliteInt.h 798fb09648cefc159ac9b3ce5e00f5ada1377ed1 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -245,8 +245,8 @@ F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562 F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114 F src/vtab.c 48dcef8bc757c2e7b488f68b5ddebb1650da2450 -F src/wal.c 2574b16ee88b1934e97f63ae38025dc48ef45327 -F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840 +F src/wal.c adc6c83b650e67e32159e11d557a46594834853e +F src/wal.h c1e05cdf3d42ed7c9263739ca8f5cdd8761e7de3 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c 55403ce19c506be6a321c7f129aff693d6103db5 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -883,7 +883,7 @@ F test/walfault.test 58fce626359c9376fe35101b5c0f2df8040aa839 F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483 F test/walmode.test 22ddccd073c817ac9ead62b88ac446e8dedc7d2c F test/walnoshm.test a074428046408f4eb5c6a00e09df8cc97ff93317 -F test/walro.test 0fb4c79a9dfa1d14f46859e469d3b4844480cd9d +F test/walro.test c204f62fb6a77839bc6ce5adbdcc9df2aac877ca F test/walshared.test 6dda2293880c300baf5d791c307f653094585761 F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933 F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c @@ -936,10 +936,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P d9bc1c7fe0ca5f6973a85827330958f4d09f8171 -R 864e9c4ee7a6cf5f8240415fb38d1af8 -T *branch * wal-readonly -T *sym-wal-readonly * -T -sym-trunk * +P bb59f9862da45d25fb51d7821130854828c91c98 +R 04ad06f1f1eb20213db6e513d8bb75aa U dan -Z 321776a9f8d852e48652ad186c3a479b +Z 112f0ce43ba9dc957c26d5146ee2fe50 diff --git a/manifest.uuid b/manifest.uuid index e39ce5418f..e0664d4e86 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bb59f9862da45d25fb51d7821130854828c91c98 \ No newline at end of file +671ba5fc59f7a958e5a4138d2425b1173a442ad7 \ No newline at end of file diff --git a/src/attach.c b/src/attach.c index 18f8823b31..4b8ea77917 100644 --- a/src/attach.c +++ b/src/attach.c @@ -76,6 +76,8 @@ static void attachFunc( Db *aNew; char *zErrDyn = 0; sqlite3_vfs *pVfs; + const char *zVfs = db->pVfs->zName; /* Name of default (main) VFS */ + int btflags = 0; UNUSED_PARAMETER(NotUsed); @@ -129,7 +131,7 @@ static void attachFunc( ** or may not be initialised. */ flags = db->openFlags; - rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); + rc = sqlite3ParseUri(zVfs, zFile, &flags, &btflags, &pVfs, &zPath, &zErr); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; sqlite3_result_error(context, zErr, -1); @@ -138,7 +140,7 @@ static void attachFunc( } assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; - rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags); + rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, btflags, flags); sqlite3_free( zPath ); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ diff --git a/src/btree.h b/src/btree.h index 9e3a73b3b6..c5abc9c865 100644 --- a/src/btree.h +++ b/src/btree.h @@ -61,6 +61,7 @@ int sqlite3BtreeOpen( #define BTREE_MEMORY 4 /* This is an in-memory DB */ #define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */ #define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */ +#define BTREE_READONLYSHM 32 /* Read-only SHM access is acceptable */ int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetCacheSize(Btree*,int); diff --git a/src/main.c b/src/main.c index 5b46f4b736..e986f83225 100644 --- a/src/main.c +++ b/src/main.c @@ -1818,6 +1818,7 @@ int sqlite3ParseUri( const char *zDefaultVfs, /* VFS to use if no "vfs=xxx" query option */ const char *zUri, /* Nul-terminated URI to parse */ unsigned int *pFlags, /* IN/OUT: SQLITE_OPEN_XXX flags */ + int *pBtflags, /* IN/OUT: BTREE_XXX flags */ sqlite3_vfs **ppVfs, /* OUT: VFS to use */ char **pzFile, /* OUT: Filename component of URI */ char **pzErrMsg /* OUT: Error message (if rc!=SQLITE_OK) */ @@ -1933,6 +1934,12 @@ int sqlite3ParseUri( if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){ zVfs = zVal; + }else if( nOpt==12 && memcmp("readonly_shm", zOpt, 12)==0 ){ + if( sqlite3Atoi(zVal) ){ + *pBtflags |= BTREE_READONLYSHM; + }else{ + *pBtflags &= ~BTREE_READONLYSHM; + } }else{ struct OpenMode { const char *z; @@ -2036,6 +2043,7 @@ static int openDatabase( int isThreadsafe; /* True for threadsafe connections */ char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ + int btflags = 0; /* Mask of BTREE_XXX flags */ *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT @@ -2163,7 +2171,8 @@ static int openDatabase( nocaseCollatingFunc, 0); /* Parse the filename/URI argument. */ - rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); + rc = sqlite3ParseUri( + zVfs, zFilename, &flags, &btflags, &db->pVfs, &zOpen, &zErrMsg); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg); @@ -2173,7 +2182,7 @@ static int openDatabase( /* Open the backend database driver */ db->openFlags = flags; - rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0, + rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, btflags, flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ diff --git a/src/pager.c b/src/pager.c index 8ad6984f8a..56dae06040 100644 --- a/src/pager.c +++ b/src/pager.c @@ -620,6 +620,7 @@ struct Pager { u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ + u8 readOnlyShm; /* True if read-only shm access is Ok */ /************************************************************************** ** The following block contains those class members that change during @@ -3017,6 +3018,7 @@ static int pagerWalFrames( static int pagerBeginReadTransaction(Pager *pPager){ int rc; /* Return code */ int changed = 0; /* True if cache must be reset */ + Wal *pWal = pPager->pWal; assert( pagerUseWal(pPager) ); assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); @@ -3026,9 +3028,9 @@ static int pagerBeginReadTransaction(Pager *pPager){ ** are in locking_mode=NORMAL and EndRead() was previously called, ** the duplicate call is harmless. */ - sqlite3WalEndReadTransaction(pPager->pWal); + sqlite3WalEndReadTransaction(pWal); - rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); + rc = sqlite3WalBeginReadTransaction(pWal, pPager->readOnlyShm, &changed); if( rc!=SQLITE_OK || changed ){ pager_reset(pPager); } @@ -4414,6 +4416,7 @@ int sqlite3PagerOpen( } pPager->pVfs = pVfs; pPager->vfsFlags = vfsFlags; + pPager->readOnlyShm = (flags & PAGER_READONLYSHM)!=0; /* Open the pager file. */ diff --git a/src/pager.h b/src/pager.h index eab7ddaf80..c6cb8bcb55 100644 --- a/src/pager.h +++ b/src/pager.h @@ -60,6 +60,7 @@ typedef struct PgHdr DbPage; #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ #define PAGER_MEMORY 0x0004 /* In-memory database */ +#define PAGER_READONLYSHM 0x0020 /* Read-only SHM access is acceptable */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4a3e45d7c0..cedd780779 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2673,7 +2673,7 @@ void sqlite3AddColumnType(Parse*,Token*); void sqlite3AddDefaultValue(Parse*,ExprSpan*); void sqlite3AddCollateType(Parse*, Token*); void sqlite3EndTable(Parse*,Token*,Token*,Select*); -int sqlite3ParseUri(const char*,const char*,unsigned int*, +int sqlite3ParseUri(const char*,const char*,unsigned int*,int*, sqlite3_vfs**,char**,char **); Bitvec *sqlite3BitvecCreate(u32); diff --git a/src/wal.c b/src/wal.c index 707fe12976..3d6baf21c8 100644 --- a/src/wal.c +++ b/src/wal.c @@ -529,7 +529,8 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); - if( rc==SQLITE_CANTOPEN && iPage==0 ){ + if( rc==SQLITE_CANTOPEN && pWal->readOnlyShm>1 ){ + assert( iPage==0 ); sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_READONLY_SHM, (void*)1); rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] @@ -541,9 +542,10 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ } } } - *ppPage = pWal->apWiData[iPage]; + assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); + if( pWal->readOnlyShm>1 ) pWal->readOnlyShm = 0; return rc; } @@ -1909,6 +1911,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ return rc; }; assert( page0 || pWal->writeLock==0 ); + assert( pWal->readOnlyShm==0 || pWal->readOnlyShm==1 ); /* If the first page of the wal-index has been mapped, try to read the ** wal-index header immediately, without holding any lock. This usually @@ -2200,13 +2203,18 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** Pager layer will use this to know that is cache is stale and ** needs to be flushed. */ -int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ +int sqlite3WalBeginReadTransaction(Wal *pWal, int readOnlyShm, int *pChanged){ int rc; /* Return code */ int cnt = 0; /* Number of TryBeginRead attempts */ + if( pWal->nWiData==0 || pWal->apWiData[0]==0 ){ + assert( readOnlyShm==0 || readOnlyShm==1 ); + pWal->readOnlyShm = readOnlyShm*2; + } do{ rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); }while( rc==WAL_RETRY ); + assert( rc || pWal->readOnlyShm==0 || (readOnlyShm && pWal->readOnlyShm==1) ); testcase( (rc&0xff)==SQLITE_BUSY ); testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); diff --git a/src/wal.h b/src/wal.h index 2039c701cf..aca5a56a56 100644 --- a/src/wal.h +++ b/src/wal.h @@ -22,7 +22,7 @@ #ifdef SQLITE_OMIT_WAL # define sqlite3WalOpen(x,y,z) 0 # define sqlite3WalClose(w,x,y,z) 0 -# define sqlite3WalBeginReadTransaction(y,z) 0 +# define sqlite3WalBeginReadTransaction(x,y,z) 0 # define sqlite3WalEndReadTransaction(z) # define sqlite3WalRead(v,w,x,y,z) 0 # define sqlite3WalDbsize(y) 0 @@ -56,7 +56,7 @@ int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); ** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the ** transaction and releases the lock. */ -int sqlite3WalBeginReadTransaction(Wal *pWal, int *); +int sqlite3WalBeginReadTransaction(Wal *pWal, int, int *); void sqlite3WalEndReadTransaction(Wal *pWal); /* Read a page from the write-ahead log, if it is present. */ diff --git a/test/walro.test b/test/walro.test index b558622cad..1025f00e33 100644 --- a/test/walro.test +++ b/test/walro.test @@ -35,6 +35,13 @@ do_multiclient_test tn { forcedelete test.db forcedelete walro + foreach c {code1 code2 code3} { + $c { + sqlite3_shutdown + sqlite3_config_uri 1 + } + } + file mkdir walro do_test 1.1.1 { @@ -49,7 +56,7 @@ do_multiclient_test tn { do_test 1.1.2 { file attributes test.db-shm -permissions r--r--r-- - code1 { sqlite3 db test.db } + code1 { sqlite3 db file:test.db?readonly_shm=1 } } {} do_test 1.1.3 { sql1 "SELECT * FROM t1" } {a b} @@ -84,7 +91,7 @@ do_multiclient_test tn { list [file exists test.db-wal] [file exists test.db-shm] } {1 1} do_test 1.2.2 { - code1 { sqlite3 db test.db } + code1 { sqlite3 db file:test.db?readonly_shm=1 } sql1 { SELECT * FROM t1 } } {a b c d e f g h i j} @@ -93,7 +100,7 @@ do_multiclient_test tn { file attributes test.db-shm -permissions rw-r--r-- hexio_write test.db-shm 0 01020304 file attributes test.db-shm -permissions r--r--r-- - code1 { sqlite3 db test.db } + code1 { sqlite3 db file:test.db?readonly_shm=1 } csql1 { SELECT * FROM t1 } } {1 {attempt to write a readonly database}} do_test 1.2.4 { From a96a399ec95667f268c135e74a16bef517b1fca2 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 11 May 2011 17:36:17 +0000 Subject: [PATCH 3/5] Add missing comments associated with readonly shm changes. FossilOrigin-Name: 6a2ea52e6c09a570428161090c2f087c66f714ec --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/main.c | 5 +++++ src/sqlite.h.in | 39 ++++++++++++++++++++++++++++++++++++++- src/wal.c | 8 ++++++++ 5 files changed, 60 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index d15e2a3497..15dda4253a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\schanges.\sAdd\sa\scouple\sof\sreadonly\sshm\stests. -D 2011-05-11T15:53:16.929 +C Add\smissing\scomments\sassociated\swith\sreadonly\sshm\schanges. +D 2011-05-11T17:36:17.276 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -144,7 +144,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 3ae0d52da013a6326310655be6473fd472347b85 -F src/main.c 951a1af49d13083da09be9d2464052e49fc25830 +F src/main.c a145cea130adfe945ab1fa7e9543c492e3f2f419 F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206 @@ -179,7 +179,7 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79 -F src/sqlite.h.in 059514ec27c8f9e11c73695aa57e55d354973e1e +F src/sqlite.h.in adeb2c8019a3851a10e3872f44b34c95c6e409f2 F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 F src/sqliteInt.h 798fb09648cefc159ac9b3ce5e00f5ada1377ed1 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d @@ -245,7 +245,7 @@ F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562 F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114 F src/vtab.c 48dcef8bc757c2e7b488f68b5ddebb1650da2450 -F src/wal.c adc6c83b650e67e32159e11d557a46594834853e +F src/wal.c 6d8ee757f62bca9b0b713724677878dff5bf7e79 F src/wal.h c1e05cdf3d42ed7c9263739ca8f5cdd8761e7de3 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c 55403ce19c506be6a321c7f129aff693d6103db5 @@ -936,7 +936,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 671ba5fc59f7a958e5a4138d2425b1173a442ad7 3e490915301216e242a5cdeb0febaff12ed53cb9 -R 1feba9025b47c541dc5cefaffc74e97e +P cde45a033ee6834900f5f5c272c383408883a74c +R 77f3671cb0a1ec2771484489a2a100be U dan -Z af31082504210cb42f7a67f58917a8ef +Z 826df29e354d14eb64673fd89c39c62e diff --git a/manifest.uuid b/manifest.uuid index 8bb70d0669..68e3f5666d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cde45a033ee6834900f5f5c272c383408883a74c \ No newline at end of file +6a2ea52e6c09a570428161090c2f087c66f714ec \ No newline at end of file diff --git a/src/main.c b/src/main.c index 45ee8fbd5b..f4cf735e3c 100644 --- a/src/main.c +++ b/src/main.c @@ -1803,6 +1803,11 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ ** *pFlags may be updated before returning if the URI filename contains ** "cache=xxx" or "mode=xxx" query parameters. ** +** The third argument, pBtflags, points to an integer containing the flags +** that will be passed as the 5th argument to sqlite3BtreeOpen (BTREE_XXX +** flags). This value will be edited if the URI filename contains a +** "readonly_shm=1" or "readonly_shm=0" query parameter. +** ** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to ** the VFS that should be used to open the database file. *pzFile is set to ** point to a buffer containing the name of the file to open. It is the diff --git a/src/sqlite.h.in b/src/sqlite.h.in index bafd322264..a002a8b4ea 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -736,6 +736,27 @@ struct sqlite3_io_methods { ** Applications should not call [sqlite3_file_control()] with this ** opcode as doing so may disrupt the operation of the specialized VFSes ** that do require it. +** +** The [SQLITE_FCNTL_READONLY_SHM] may be generated internally by SQLite if +** the "readonly_shm=1" URI option is specified when the database is opened. +** The fourth argument passed to the VFS xFileControl methods is a pointer +** to a variable of type "int" containing the value 1 or 0. If the variable +** contains the value 1, then this indicates to the VFS that a read-only +** mapping of the shared-memory region is acceptable. If it is set to 0, then +** this indicates that a read-write mapping is required (as normal). If +** a read-only mapping is returned, then the VFS may also return read-only +** mappings for any subsequent requests via the same file-descriptor - +** regardless of the value most recently configured using +** SQLITE_FCNTL_READONLY_SHM. +** +** In practice, if "readonly_shm=1" is specified and the first attempt to +** map a shared-memory region fails, then this file-control is invoked with +** the argument variable set to 1 and a second attempt to map the shared-memory +** region is made. If this mapping succeeds, then the connection continues +** with the read-only mapping. Otherwise, if it fails, SQLITE_CANTOPEN is +** returned to the caller. Whether or not the second (read-only) mapping +** attempt succeeds, the file-control is invoked again with the argument +** variable set to 0. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 @@ -2444,7 +2465,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ** The query component of a URI may contain parameters that are interpreted ** either by SQLite itself, or by a [sqlite3_vfs | custom VFS implementation]. -** SQLite interprets the following three query parameters: +** SQLite interprets the following four query parameters: ** **
    **
  • vfs: ^The "vfs" parameter may be used to specify the name of @@ -2476,6 +2497,22 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ^If sqlite3_open_v2() is used and the "cache" parameter is present in ** a URI filename, its value overrides any behaviour requested by setting ** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. +** +**
  • readonly_shm: ^The readonly_shm parameter may be set to +** either "1" or "0". Setting it to "1" indicates that if the database +** is in WAL mode and read-write access to the associated shared +** memory region cannot be obtained, then an attempt should be made to open +** the shared-memory in read-only mode instead. If there exist one or +** more other database clients that have read-write connections to the +** database shared-memory, then a read-only shared-memory connection works +** fine. However, if there exist no clients with read-write connections +** to the shared-memory and the most recent such crashed or was interrupted +** by a power failure, then it is possible for a database client using a +** read-only connection to return incorrect data, incorrectly report +** database corruption, or return an SQLITE_READONLY error. Or if the +** most recent read-write connection shut down cleanly, it may not be +** possible to open the shared-memory in read-only mode at all, and SQLite +** will return SQLITE_CANTOPEN. **
** ** ^Specifying an unknown parameter in the query component of a URI is not an diff --git a/src/wal.c b/src/wal.c index 3d6baf21c8..2ee7f59e89 100644 --- a/src/wal.c +++ b/src/wal.c @@ -406,6 +406,14 @@ struct WalCkptInfo { /* ** An open write-ahead log file is represented by an instance of the ** following object. +** +** The readOnlyShm variable is normally set to 0. If it is set to 1, then +** the connection to shared-memory is read-only. This means it cannot +** be written at all (even when read-locking the database). If it is set +** to 2, then the shared-memory region is not yet open, but a read-only +** connection is acceptable. In this case when the shared-memory is opened +** (see function walIndexPage()), readOnlyShm is set to either 0 or 1 as +** appropriate. */ struct Wal { sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ From 66dfec8b7a6052d4b639e5ab1910249aebb8828d Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 1 Jun 2011 20:01:49 +0000 Subject: [PATCH 4/5] Simplify the wal-readonly branch so that it does not require changes to anything other than os_unix.c and wal.c and a couple of new error codes. FossilOrigin-Name: d6b4709de4d1f8af001f58938247f00a652a616e --- manifest | 30 ++++++++++----------- manifest.uuid | 2 +- src/attach.c | 6 ++--- src/btree.h | 1 - src/main.c | 18 ++----------- src/os_unix.c | 69 ++++++++++++++++++++----------------------------- src/pager.c | 7 ++--- src/pager.h | 1 - src/sqlite.h.in | 41 +---------------------------- src/sqliteInt.h | 2 +- src/wal.c | 62 ++++++++++++++------------------------------ src/wal.h | 4 +-- 12 files changed, 74 insertions(+), 169 deletions(-) diff --git a/manifest b/manifest index b3a84d3a3f..8b6e11236e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Pull\sthe\slatest\strunk\schanges\sinto\sthe\swal-readonly\sbranch. -D 2011-06-01T19:44:57.308 +C Simplify\sthe\swal-readonly\sbranch\sso\sthat\sit\sdoes\snot\srequire\schanges\sto\nanything\sother\sthan\sos_unix.c\sand\swal.c\sand\sa\scouple\sof\snew\serror\scodes. +D 2011-06-01T20:01:49.650 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 11dcc00a8d0e5202def00e81732784fb0cc4fe1d F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -117,13 +117,13 @@ F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F src/alter.c 280f5c04b11b492703a342222b3de0a999445280 F src/analyze.c a425d62e8fa9ebcb4359ab84ff0c62c6563d2e2a -F src/attach.c a87bfb77a720f7aa02791434aacfd9bc8feb50cc +F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 986c15232757f2873dff35ee3b35cbf935fc573c F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btree.c 0d3b39dcb79565c053e35fc12713f12d8a74d6a9 -F src/btree.h d796dc4030b3cce7f5a0cc4f2f986d2befa6b8ac +F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3 F src/build.c c10ab9e2c77ade99dee23554787f8acfc0c231fc F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a @@ -144,7 +144,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 3ae0d52da013a6326310655be6473fd472347b85 -F src/main.c 4e55ff4800181e3ad063492740f54ce406de5deb +F src/main.c 059daeed5876b3604f0192f838faf5f4db138901 F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206 @@ -163,10 +163,10 @@ F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 -F src/os_unix.c af3c3f6a0fc6dbde802e55848f1cad7fb2121ff3 +F src/os_unix.c a59c0718021934157f235468788c793cbc0d53de F src/os_win.c 218b899469e570d46eb8147c2383075f7c026230 -F src/pager.c 2cf3bad86e6fbac42d9dbd3b89799185a343b17b -F src/pager.h 34c6b029446f06f40847746d22faac0d354dd909 +F src/pager.c 120550e7ef01dafaa2cbb4a0528c0d87c8f12b41 +F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/pcache.c 49e718c095810c6b3334e3a6d89970aceaddefce F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 @@ -179,9 +179,9 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff F src/shell.c decd04236a7ef26be5ef46d4ea963044bfad9a48 -F src/sqlite.h.in dbe88418628302c0a93671a216346acb5746211a +F src/sqlite.h.in 2f51e4f58b2b4626fcbd9938580e730cb5fb4985 F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 -F src/sqliteInt.h 219b21e80f46eef7cc7bb7477c12de61d00f5607 +F src/sqliteInt.h 6e58c558c57c8f44011736d5fa5295eb3130f9de F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -245,8 +245,8 @@ F src/vdbeblob.c f024f0bf420f36b070143c32b15cc7287341ffd3 F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114 F src/vtab.c 9ba8c7fdb7d39260c033a402f6032d3e7bc5d336 -F src/wal.c 861ea98779dac3867e5f97d13e61cbbb39b310b0 -F src/wal.h 0835021ae243c2f1119e997e8af5d8cc3b991de1 +F src/wal.c fd63d07233203dd3bd29cbe1ae5c8bb2c34e08fc +F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c 55403ce19c506be6a321c7f129aff693d6103db5 F test/8_3_names.test b93687beebd17f6ebf812405a6833bae5d1f4199 @@ -939,7 +939,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 2c6b5a28e3f6b7cb96b944d0a254f3707885f1ce e704e8690ae35decc9769a45cf8d519ccad8b79d -R 5f4edf2ff77d011b562e3dc234f8f21d +P 0b63b71357a65e26ecd3f3bb34a5f14feee322f4 +R 20a045d8e6470f5f17b67b2f15ff8859 U drh -Z 99212444a62258034d706d358ee0a50e +Z f7b3b70c555e3fb5f3360ed8e60c2877 diff --git a/manifest.uuid b/manifest.uuid index 70ad59250f..6f393899aa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0b63b71357a65e26ecd3f3bb34a5f14feee322f4 \ No newline at end of file +d6b4709de4d1f8af001f58938247f00a652a616e \ No newline at end of file diff --git a/src/attach.c b/src/attach.c index 4b8ea77917..18f8823b31 100644 --- a/src/attach.c +++ b/src/attach.c @@ -76,8 +76,6 @@ static void attachFunc( Db *aNew; char *zErrDyn = 0; sqlite3_vfs *pVfs; - const char *zVfs = db->pVfs->zName; /* Name of default (main) VFS */ - int btflags = 0; UNUSED_PARAMETER(NotUsed); @@ -131,7 +129,7 @@ static void attachFunc( ** or may not be initialised. */ flags = db->openFlags; - rc = sqlite3ParseUri(zVfs, zFile, &flags, &btflags, &pVfs, &zPath, &zErr); + rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; sqlite3_result_error(context, zErr, -1); @@ -140,7 +138,7 @@ static void attachFunc( } assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; - rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, btflags, flags); + rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags); sqlite3_free( zPath ); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ diff --git a/src/btree.h b/src/btree.h index c5abc9c865..9e3a73b3b6 100644 --- a/src/btree.h +++ b/src/btree.h @@ -61,7 +61,6 @@ int sqlite3BtreeOpen( #define BTREE_MEMORY 4 /* This is an in-memory DB */ #define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */ #define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */ -#define BTREE_READONLYSHM 32 /* Read-only SHM access is acceptable */ int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetCacheSize(Btree*,int); diff --git a/src/main.c b/src/main.c index cdd54b659d..c99d396546 100644 --- a/src/main.c +++ b/src/main.c @@ -1803,11 +1803,6 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ ** *pFlags may be updated before returning if the URI filename contains ** "cache=xxx" or "mode=xxx" query parameters. ** -** The third argument, pBtflags, points to an integer containing the flags -** that will be passed as the 5th argument to sqlite3BtreeOpen (BTREE_XXX -** flags). This value will be edited if the URI filename contains a -** "readonly_shm=1" or "readonly_shm=0" query parameter. -** ** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to ** the VFS that should be used to open the database file. *pzFile is set to ** point to a buffer containing the name of the file to open. It is the @@ -1823,7 +1818,6 @@ int sqlite3ParseUri( const char *zDefaultVfs, /* VFS to use if no "vfs=xxx" query option */ const char *zUri, /* Nul-terminated URI to parse */ unsigned int *pFlags, /* IN/OUT: SQLITE_OPEN_XXX flags */ - int *pBtflags, /* IN/OUT: BTREE_XXX flags */ sqlite3_vfs **ppVfs, /* OUT: VFS to use */ char **pzFile, /* OUT: Filename component of URI */ char **pzErrMsg /* OUT: Error message (if rc!=SQLITE_OK) */ @@ -1939,12 +1933,6 @@ int sqlite3ParseUri( if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){ zVfs = zVal; - }else if( nOpt==12 && memcmp("readonly_shm", zOpt, 12)==0 ){ - if( sqlite3Atoi(zVal) ){ - *pBtflags |= BTREE_READONLYSHM; - }else{ - *pBtflags &= ~BTREE_READONLYSHM; - } }else{ struct OpenMode { const char *z; @@ -2048,7 +2036,6 @@ static int openDatabase( int isThreadsafe; /* True for threadsafe connections */ char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ - int btflags = 0; /* Mask of BTREE_XXX flags */ *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT @@ -2177,8 +2164,7 @@ static int openDatabase( /* Parse the filename/URI argument. */ db->openFlags = flags; - rc = sqlite3ParseUri( - zVfs, zFilename, &flags, &btflags, &db->pVfs, &zOpen, &zErrMsg); + rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg); @@ -2187,7 +2173,7 @@ static int openDatabase( } /* Open the backend database driver */ - rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, btflags, + rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0, flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ diff --git a/src/os_unix.c b/src/os_unix.c index 947f431aeb..e6d1774a49 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -212,7 +212,6 @@ struct unixFile { UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ - int readOnlyShm; /* True to open shared-memory read-only */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ @@ -3453,10 +3452,6 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ case SQLITE_FCNTL_SIZE_HINT: { return fcntlSizeHint((unixFile *)id, *(i64 *)pArg); } - case SQLITE_FCNTL_READONLY_SHM: { - ((unixFile*)id)->readOnlyShm = (pArg!=0); - return SQLITE_OK; - } #ifndef NDEBUG /* The pager calls this method to signal that it has done ** a rollback and that the database is therefore unchanged and @@ -3542,11 +3537,11 @@ struct unixShmNode { char *zFilename; /* Name of the mmapped file */ int h; /* Open file descriptor */ int szRegion; /* Size of shared-memory regions */ - int nRegion; /* Size of array apRegion */ + u16 nRegion; /* Size of array apRegion */ + u8 isReadonly; /* True if read-only */ char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ - u8 readOnly; /* True if this is a read-only mapping */ #ifdef SQLITE_DEBUG u8 exclMask; /* Mask of exclusive locks held */ u8 sharedMask; /* Mask of shared locks held */ @@ -3787,48 +3782,38 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ } if( pInode->bProcessLock==0 ){ - int flags = (pDbFd->readOnlyShm ? O_RDONLY : O_RDWR|O_CREAT); - pShmNode->h = robust_open(zShmFilename, flags, (sStat.st_mode & 0777)); + pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT, + (sStat.st_mode & 0777)); if( pShmNode->h<0 ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); - goto shm_open_err; + const char *zRO; + zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm"); + if( zRO && (zRO[0]!='0' || zRO[1]!=0) ){ + pShmNode->h = robust_open(zShmFilename, O_RDONLY, + (sStat.st_mode & 0777)); + pShmNode->isReadonly = 1; + } + if( pShmNode->h<0 ){ + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); + goto shm_open_err; + } } - pShmNode->readOnly = pDbFd->readOnlyShm; /* Check to see if another process is holding the dead-man switch. - ** If not, zero the first few bytes of the shared-memory file to make - ** sure it is not mistaken for valid by code in wal.c. Except, if this - ** is a read-only connection to the shared-memory then it is not possible - ** to check check if another process is holding a read-lock on the DMS - ** byte, as we cannot attempt a write-lock via a read-only file - ** descriptor. So in this case, we just assume the shared-memory - ** contents are Ok and proceed. */ - if( pShmNode->readOnly==0 ){ - rc = SQLITE_OK; - if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ - if( pDbFd->readOnlyShm ){ - rc = SQLITE_IOERR_SHMOPEN; - }else if( 4!=osWrite(pShmNode->h, "\00\00\00\00", 4) ){ - rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); - } + ** If not, truncate the file to zero length. + */ + rc = SQLITE_OK; + if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ + if( robust_ftruncate(pShmNode->h, 0) ){ + rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); } - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); - } - if( rc ) goto shm_open_err; } + if( rc==SQLITE_OK ){ + rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); + } + if( rc ) goto shm_open_err; } } - /* If the unixShmNode is read-only, but SQLITE_FCNTL_READONLY_SHM has not - ** been set for file-descriptor pDbFd, return an error. The wal.c module - ** will then call this function again with SQLITE_FCNTL_READONLY_SHM set. - */ - else if( pShmNode->readOnly && !pDbFd->readOnlyShm ){ - rc = SQLITE_IOERR_SHMOPEN; - goto shm_open_err; - } - /* Make the new connection a child of the unixShmNode */ p->pShmNode = pShmNode; #ifdef SQLITE_DEBUG @@ -3949,7 +3934,8 @@ static int unixShmMap( while(pShmNode->nRegion<=iRegion){ void *pMem; if( pShmNode->h>=0 ){ - pMem = mmap(0, szRegion, PROT_READ|(!pShmNode->readOnly?PROT_WRITE:0), + pMem = mmap(0, szRegion, + pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion ); if( pMem==MAP_FAILED ){ @@ -3975,6 +3961,7 @@ shmpage_out: }else{ *pp = 0; } + if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; sqlite3_mutex_leave(pShmNode->mutex); return rc; } diff --git a/src/pager.c b/src/pager.c index 461c735432..7ff9a9a000 100644 --- a/src/pager.c +++ b/src/pager.c @@ -620,7 +620,6 @@ struct Pager { u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ - u8 readOnlyShm; /* True if read-only shm access is Ok */ /************************************************************************** ** The following block contains those class members that change during @@ -3018,7 +3017,6 @@ static int pagerWalFrames( static int pagerBeginReadTransaction(Pager *pPager){ int rc; /* Return code */ int changed = 0; /* True if cache must be reset */ - Wal *pWal = pPager->pWal; assert( pagerUseWal(pPager) ); assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); @@ -3028,9 +3026,9 @@ static int pagerBeginReadTransaction(Pager *pPager){ ** are in locking_mode=NORMAL and EndRead() was previously called, ** the duplicate call is harmless. */ - sqlite3WalEndReadTransaction(pWal); + sqlite3WalEndReadTransaction(pPager->pWal); - rc = sqlite3WalBeginReadTransaction(pWal, pPager->readOnlyShm, &changed); + rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); if( rc!=SQLITE_OK || changed ){ pager_reset(pPager); } @@ -4418,7 +4416,6 @@ int sqlite3PagerOpen( } pPager->pVfs = pVfs; pPager->vfsFlags = vfsFlags; - pPager->readOnlyShm = (flags & PAGER_READONLYSHM)!=0; /* Open the pager file. */ diff --git a/src/pager.h b/src/pager.h index c6cb8bcb55..eab7ddaf80 100644 --- a/src/pager.h +++ b/src/pager.h @@ -60,7 +60,6 @@ typedef struct PgHdr DbPage; #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ #define PAGER_MEMORY 0x0004 /* In-memory database */ -#define PAGER_READONLYSHM 0x0020 /* Read-only SHM access is acceptable */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 45aec0e1c2..593e9148fe 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -454,7 +454,6 @@ int sqlite3_exec( #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) - #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) @@ -737,27 +736,6 @@ struct sqlite3_io_methods { ** Applications should not call [sqlite3_file_control()] with this ** opcode as doing so may disrupt the operation of the specialized VFSes ** that do require it. -** -** The [SQLITE_FCNTL_READONLY_SHM] may be generated internally by SQLite if -** the "readonly_shm=1" URI option is specified when the database is opened. -** The fourth argument passed to the VFS xFileControl methods is a pointer -** to a variable of type "int" containing the value 1 or 0. If the variable -** contains the value 1, then this indicates to the VFS that a read-only -** mapping of the shared-memory region is acceptable. If it is set to 0, then -** this indicates that a read-write mapping is required (as normal). If -** a read-only mapping is returned, then the VFS may also return read-only -** mappings for any subsequent requests via the same file-descriptor - -** regardless of the value most recently configured using -** SQLITE_FCNTL_READONLY_SHM. -** -** In practice, if "readonly_shm=1" is specified and the first attempt to -** map a shared-memory region fails, then this file-control is invoked with -** the argument variable set to 1 and a second attempt to map the shared-memory -** region is made. If this mapping succeeds, then the connection continues -** with the read-only mapping. Otherwise, if it fails, SQLITE_CANTOPEN is -** returned to the caller. Whether or not the second (read-only) mapping -** attempt succeeds, the file-control is invoked again with the argument -** variable set to 0. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 @@ -767,7 +745,6 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 #define SQLITE_FCNTL_SYNC_OMITTED 8 -#define SQLITE_FCNTL_READONLY_SHM 9 /* @@ -2481,7 +2458,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** [[core URI query parameters]] ** The query component of a URI may contain parameters that are interpreted ** either by SQLite itself, or by a [VFS | custom VFS implementation]. -** SQLite interprets the following four query parameters: +** SQLite interprets the following three query parameters: ** **
    **
  • vfs: ^The "vfs" parameter may be used to specify the name of @@ -2513,22 +2490,6 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ^If sqlite3_open_v2() is used and the "cache" parameter is present in ** a URI filename, its value overrides any behaviour requested by setting ** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. -** -**
  • readonly_shm: ^The readonly_shm parameter may be set to -** either "1" or "0". Setting it to "1" indicates that if the database -** is in WAL mode and read-write access to the associated shared -** memory region cannot be obtained, then an attempt should be made to open -** the shared-memory in read-only mode instead. If there exist one or -** more other database clients that have read-write connections to the -** database shared-memory, then a read-only shared-memory connection works -** fine. However, if there exist no clients with read-write connections -** to the shared-memory and the most recent such crashed or was interrupted -** by a power failure, then it is possible for a database client using a -** read-only connection to return incorrect data, incorrectly report -** database corruption, or return an SQLITE_READONLY error. Or if the -** most recent read-write connection shut down cleanly, it may not be -** possible to open the shared-memory in read-only mode at all, and SQLite -** will return SQLITE_CANTOPEN. **
** ** ^Specifying an unknown parameter in the query component of a URI is not an diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 8b0aa855d8..fd86158e00 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2673,7 +2673,7 @@ void sqlite3AddColumnType(Parse*,Token*); void sqlite3AddDefaultValue(Parse*,ExprSpan*); void sqlite3AddCollateType(Parse*, Token*); void sqlite3EndTable(Parse*,Token*,Token*,Select*); -int sqlite3ParseUri(const char*,const char*,unsigned int*,int*, +int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); Bitvec *sqlite3BitvecCreate(u32); diff --git a/src/wal.c b/src/wal.c index 87d3fcc968..9f9e1115fa 100644 --- a/src/wal.c +++ b/src/wal.c @@ -406,14 +406,6 @@ struct WalCkptInfo { /* ** An open write-ahead log file is represented by an instance of the ** following object. -** -** The readOnlyShm variable is normally set to 0. If it is set to 1, then -** the connection to shared-memory is read-only. This means it cannot -** be written at all (even when read-locking the database). If it is set -** to 2, then the shared-memory region is not yet open, but a read-only -** connection is acceptable. In this case when the shared-memory is opened -** (see function walIndexPage()), readOnlyShm is set to either 0 or 1 as -** appropriate. */ struct Wal { sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ @@ -428,8 +420,7 @@ struct Wal { u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ - u8 readOnly; /* True if the WAL file is open read-only */ - u8 readOnlyShm; /* True if the SHM file is open read-only */ + u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */ WalIndexHdr hdr; /* Wal-index header for current transaction */ const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ @@ -445,6 +436,13 @@ struct Wal { #define WAL_EXCLUSIVE_MODE 1 #define WAL_HEAPMEMORY_MODE 2 +/* +** Possible values for WAL.readOnly +*/ +#define WAL_RDWR 0 /* Normal read/write connection */ +#define WAL_RDONLY 1 /* The WAL file is readonly */ +#define WAL_SHM_RDONLY 2 /* The SHM file is readonly */ + /* ** Each page of the wal-index mapping contains a hash-table made up of ** an array of HASHTABLE_NSLOT elements of the following type. @@ -538,23 +536,15 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); - if( rc==SQLITE_CANTOPEN && pWal->readOnlyShm>1 ){ - assert( iPage==0 ); - sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_READONLY_SHM, (void*)1); - rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, - pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] - ); - if( rc==SQLITE_OK ){ - pWal->readOnly = pWal->readOnlyShm = 1; - } - sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_READONLY_SHM, (void*)0); + if( rc==SQLITE_READONLY ){ + pWal->readOnly |= WAL_SHM_RDONLY; + rc = SQLITE_OK; } } } - *ppPage = pWal->apWiData[iPage]; + *ppPage = pWal->apWiData[iPage]; assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); - if( pWal->readOnlyShm>1 ) pWal->readOnlyShm = 0; return rc; } @@ -794,7 +784,6 @@ static void walUnlockShared(Wal *pWal, int lockIdx){ } static int walLockExclusive(Wal *pWal, int lockIdx, int n){ int rc; - assert( pWal->readOnlyShm==0 ); if( pWal->exclusiveMode ) return SQLITE_OK; rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); @@ -804,7 +793,6 @@ static int walLockExclusive(Wal *pWal, int lockIdx, int n){ return rc; } static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ - assert( pWal->readOnlyShm==0 ); if( pWal->exclusiveMode ) return; (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); @@ -1080,7 +1068,6 @@ static int walIndexRecover(Wal *pWal){ assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); assert( pWal->writeLock ); - assert( pWal->readOnlyShm==0 ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; nLock = SQLITE_SHM_NLOCK - iLock; rc = walLockExclusive(pWal, iLock, nLock); @@ -1300,7 +1287,7 @@ int sqlite3WalOpen( flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ - pRet->readOnly = 1; + pRet->readOnly = WAL_RDONLY; } if( rc!=SQLITE_OK ){ @@ -1929,7 +1916,6 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ return rc; }; assert( page0 || pWal->writeLock==0 ); - assert( pWal->readOnlyShm==0 || pWal->readOnlyShm==1 ); /* If the first page of the wal-index has been mapped, try to read the ** wal-index header immediately, without holding any lock. This usually @@ -1939,11 +1925,11 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); /* If the first attempt failed, it might have been due to a race - ** with a writer. So lock the WAL_WRITE_LOCK byte and try again. + ** with a writer. So get a WRITE lock and try again. */ assert( badHdr==0 || pWal->writeLock==0 ); if( badHdr ){ - if( pWal->readOnlyShm ){ + if( pWal->readOnly & WAL_SHM_RDONLY ){ if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; @@ -2150,7 +2136,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ } /* There was once an "if" here. The extra "{" is to preserve indentation. */ { - if( pWal->readOnlyShm==0 && (mxReadMark < pWal->hdr.mxFrame || mxI==0) ){ + if( (pWal->readOnly & WAL_SHM_RDONLY)==0 + && (mxReadMarkhdr.mxFrame || mxI==0) + ){ for(i=1; ireadOnlyShm && rc==SQLITE_OK) ); return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK; } @@ -2221,18 +2208,13 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** Pager layer will use this to know that is cache is stale and ** needs to be flushed. */ -int sqlite3WalBeginReadTransaction(Wal *pWal, int readOnlyShm, int *pChanged){ +int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ int rc; /* Return code */ int cnt = 0; /* Number of TryBeginRead attempts */ - if( pWal->nWiData==0 || pWal->apWiData[0]==0 ){ - assert( readOnlyShm==0 || readOnlyShm==1 ); - pWal->readOnlyShm = readOnlyShm*2; - } do{ rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); }while( rc==WAL_RETRY ); - assert( rc || pWal->readOnlyShm==0 || (readOnlyShm && pWal->readOnlyShm==1) ); testcase( (rc&0xff)==SQLITE_BUSY ); testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); @@ -2407,7 +2389,6 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){ if( pWal->readOnly ){ return SQLITE_READONLY; } - assert( pWal->readOnlyShm==0 ); /* Only one writer allowed at a time. Get the write lock. Return ** SQLITE_BUSY if unable. @@ -2814,11 +2795,8 @@ int sqlite3WalCheckpoint( assert( pWal->ckptLock==0 ); assert( pWal->writeLock==0 ); + if( pWal->readOnly ) return SQLITE_READONLY; WALTRACE(("WAL%p: checkpoint begins\n", pWal)); - if( pWal->readOnlyShm ){ - return SQLITE_READONLY; - } - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); if( rc ){ /* Usually this is SQLITE_BUSY meaning that another thread or process diff --git a/src/wal.h b/src/wal.h index df09beffe7..a62b23bbdc 100644 --- a/src/wal.h +++ b/src/wal.h @@ -23,7 +23,7 @@ # define sqlite3WalOpen(x,y,z) 0 # define sqlite3WalLimit(x,y) # define sqlite3WalClose(w,x,y,z) 0 -# define sqlite3WalBeginReadTransaction(x,y,z) 0 +# define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) # define sqlite3WalRead(v,w,x,y,z) 0 # define sqlite3WalDbsize(y) 0 @@ -60,7 +60,7 @@ void sqlite3WalLimit(Wal*, i64); ** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the ** transaction and releases the lock. */ -int sqlite3WalBeginReadTransaction(Wal *pWal, int, int *); +int sqlite3WalBeginReadTransaction(Wal *pWal, int *); void sqlite3WalEndReadTransaction(Wal *pWal); /* Read a page from the write-ahead log, if it is present. */ From fd019ef7ceaf17d41a47c3be1ef7309deb9be7eb Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 1 Jun 2011 20:13:36 +0000 Subject: [PATCH 5/5] Make use of the sqlite3GetBoolean() interface for more robust processing of the readonly_shm query parameter inside of unixShmMap(). FossilOrigin-Name: 1f930d7e04cd4a5ff3d91a0e9f1b62114f1cebd2 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/os_unix.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 8b6e11236e..b9fd02df11 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplify\sthe\swal-readonly\sbranch\sso\sthat\sit\sdoes\snot\srequire\schanges\sto\nanything\sother\sthan\sos_unix.c\sand\swal.c\sand\sa\scouple\sof\snew\serror\scodes. -D 2011-06-01T20:01:49.650 +C Make\suse\sof\sthe\ssqlite3GetBoolean()\sinterface\sfor\smore\srobust\sprocessing\nof\sthe\sreadonly_shm\squery\sparameter\sinside\sof\sunixShmMap(). +D 2011-06-01T20:13:36.269 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 11dcc00a8d0e5202def00e81732784fb0cc4fe1d F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -163,7 +163,7 @@ F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 -F src/os_unix.c a59c0718021934157f235468788c793cbc0d53de +F src/os_unix.c fd4e9588ff0ce09720721ce739ab2682202875ae F src/os_win.c 218b899469e570d46eb8147c2383075f7c026230 F src/pager.c 120550e7ef01dafaa2cbb4a0528c0d87c8f12b41 F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 @@ -939,7 +939,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 0b63b71357a65e26ecd3f3bb34a5f14feee322f4 -R 20a045d8e6470f5f17b67b2f15ff8859 +P d6b4709de4d1f8af001f58938247f00a652a616e +R fdd310892b7cea51b352170f4a152593 U drh -Z f7b3b70c555e3fb5f3360ed8e60c2877 +Z cc8ff93f2b92f997226013b652879169 diff --git a/manifest.uuid b/manifest.uuid index 6f393899aa..203442f1d4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d6b4709de4d1f8af001f58938247f00a652a616e \ No newline at end of file +1f930d7e04cd4a5ff3d91a0e9f1b62114f1cebd2 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index e6d1774a49..5860ae71a9 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3787,7 +3787,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ if( pShmNode->h<0 ){ const char *zRO; zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm"); - if( zRO && (zRO[0]!='0' || zRO[1]!=0) ){ + if( zRO && sqlite3GetBoolean(zRO) ){ pShmNode->h = robust_open(zShmFilename, O_RDONLY, (sStat.st_mode & 0777)); pShmNode->isReadonly = 1;