mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Add experimental support for read-only connections to WAL databases.
FossilOrigin-Name: bb59f9862da45d25fb51d7821130854828c91c98
This commit is contained in:
59
src/wal.c
59
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; i<WAL_NREADER; i++){
|
||||
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -2123,7 +2144,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
|
||||
}
|
||||
if( mxI==0 ){
|
||||
assert( rc==SQLITE_BUSY );
|
||||
return WAL_RETRY;
|
||||
assert( rc==SQLITE_BUSY || (pWal->readOnlyShm && 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
|
||||
|
Reference in New Issue
Block a user