/ Check-in [8c2a769c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge the patch that enables reading a read-only WAL-mode database, without any special query parameters, as long as the -shm and -wal files are on disk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | apple-osx
Files: files | file ages | folders
SHA3-256: 8c2a769c4ac331c20134eb3d0e96f6af21c8ac7529a5eaea4843cce7cf7c936a
User & Date: drh 2017-11-14 20:36:33
Context
2017-11-14
21:06
Fix a typo that prevented successful builds on macs. check-in: adf83060 user: drh tags: apple-osx
20:36
Merge the patch that enables reading a read-only WAL-mode database, without any special query parameters, as long as the -shm and -wal files are on disk. check-in: 8c2a769c user: drh tags: apple-osx
20:00
Merge all changes from trunk prior to the read-only WAL enhancement. check-in: 1754faef user: drh tags: apple-osx
19:34
Add the ability to read from read-only WAL-mode database files as long as the -wal and -shm files are present on disk. check-in: 00ec95fc user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/main.c.

  1317   1317         case SQLITE_BUSY_RECOVERY:      zName = "SQLITE_BUSY_RECOVERY";     break;
  1318   1318         case SQLITE_BUSY_SNAPSHOT:      zName = "SQLITE_BUSY_SNAPSHOT";     break;
  1319   1319         case SQLITE_LOCKED:             zName = "SQLITE_LOCKED";            break;
  1320   1320         case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break;
  1321   1321         case SQLITE_NOMEM:              zName = "SQLITE_NOMEM";             break;
  1322   1322         case SQLITE_READONLY:           zName = "SQLITE_READONLY";          break;
  1323   1323         case SQLITE_READONLY_RECOVERY:  zName = "SQLITE_READONLY_RECOVERY"; break;
  1324         -      case SQLITE_READONLY_CANTLOCK:  zName = "SQLITE_READONLY_CANTLOCK"; break;
         1324  +      case SQLITE_READONLY_CANTINIT:  zName = "SQLITE_READONLY_CANTINIT"; break;
  1325   1325         case SQLITE_READONLY_ROLLBACK:  zName = "SQLITE_READONLY_ROLLBACK"; break;
  1326   1326         case SQLITE_READONLY_DBMOVED:   zName = "SQLITE_READONLY_DBMOVED";  break;
  1327   1327         case SQLITE_INTERRUPT:          zName = "SQLITE_INTERRUPT";         break;
  1328   1328         case SQLITE_IOERR:              zName = "SQLITE_IOERR";             break;
  1329   1329         case SQLITE_IOERR_READ:         zName = "SQLITE_IOERR_READ";        break;
  1330   1330         case SQLITE_IOERR_SHORT_READ:   zName = "SQLITE_IOERR_SHORT_READ";  break;
  1331   1331         case SQLITE_IOERR_WRITE:        zName = "SQLITE_IOERR_WRITE";       break;

Changes to src/os_unix.c.

  4835   4835     unixInodeInfo *pInode;     /* unixInodeInfo that owns this SHM node */
  4836   4836     sqlite3_mutex *mutex;      /* Mutex to access this object */
  4837   4837     char *zFilename;           /* Name of the mmapped file */
  4838   4838     int h;                     /* Open file descriptor */
  4839   4839     int szRegion;              /* Size of shared-memory regions */
  4840   4840     u16 nRegion;               /* Size of array apRegion */
  4841   4841     u8 isReadonly;             /* True if read-only */
         4842  +  u8 isUnlocked;             /* True if no DMS lock held */
  4842   4843     char **apRegion;           /* Array of mapped shared-memory regions */
  4843   4844     int nRef;                  /* Number of unixShm objects pointing to this */
  4844   4845     unixShm *pFirst;           /* All unixShm objects pointing to this */
  4845   4846   #ifdef SQLITE_DEBUG
  4846   4847     u8 exclMask;               /* Mask of exclusive locks held */
  4847   4848     u8 sharedMask;             /* Mask of shared locks held */
  4848   4849     u8 nextShmId;              /* Next available unixShm.id value */
................................................................................
  4996   4997         robust_close(pFd, p->h, __LINE__);
  4997   4998         p->h = -1;
  4998   4999       }
  4999   5000       p->pInode->pShmNode = 0;
  5000   5001       sqlite3_free(p);
  5001   5002     }
  5002   5003   }
         5004  +
         5005  +/*
         5006  +** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
         5007  +** take it now. Return SQLITE_OK if successful, or an SQLite error
         5008  +** code otherwise.
         5009  +**
         5010  +** If the DMS cannot be locked because this is a readonly_shm=1 
         5011  +** connection and no other process already holds a lock, return
         5012  +** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
         5013  +*/
         5014  +static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
         5015  +  struct flock lock;
         5016  +  int rc = SQLITE_OK;
         5017  +
         5018  +  /* Use F_GETLK to determine the locks other processes are holding
         5019  +  ** on the DMS byte. If it indicates that another process is holding
         5020  +  ** a SHARED lock, then this process may also take a SHARED lock
         5021  +  ** and proceed with opening the *-shm file. 
         5022  +  **
         5023  +  ** Or, if no other process is holding any lock, then this process
         5024  +  ** is the first to open it. In this case take an EXCLUSIVE lock on the
         5025  +  ** DMS byte and truncate the *-shm file to zero bytes in size. Then
         5026  +  ** downgrade to a SHARED lock on the DMS byte.
         5027  +  **
         5028  +  ** If another process is holding an EXCLUSIVE lock on the DMS byte,
         5029  +  ** return SQLITE_BUSY to the caller (it will try again). An earlier
         5030  +  ** version of this code attempted the SHARED lock at this point. But
         5031  +  ** this introduced a subtle race condition: if the process holding
         5032  +  ** EXCLUSIVE failed just before truncating the *-shm file, then this
         5033  +  ** process might open and use the *-shm file without truncating it.
         5034  +  ** And if the *-shm file has been corrupted by a power failure or
         5035  +  ** system crash, the database itself may also become corrupt.  */
         5036  +  lock.l_whence = SEEK_SET;
         5037  +  lock.l_start = UNIX_SHM_DMS;
         5038  +  lock.l_len = 1;
         5039  +  lock.l_type = F_WRLCK;
         5040  +  if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) {
         5041  +    rc = SQLITE_IOERR_LOCK;
         5042  +  }else if( lock.l_type==F_UNLCK ){
         5043  +    if( pShmNode->isReadonly ){
         5044  +      pShmNode->isUnlocked = 1;
         5045  +      rc = SQLITE_READONLY_CANTINIT;
         5046  +    }else{
         5047  +      rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
         5048  +      if( rc==SQLITE_OK && robust_ftruncate(pShmNode->h, 0) ){
         5049  +        rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename);
         5050  +      }
         5051  +    }
         5052  +  }else if( lock.l_type==F_WRLCK ){
         5053  +    rc = SQLITE_BUSY;
         5054  +  }
         5055  +
         5056  +  if( rc==SQLITE_OK ){
         5057  +    assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK );
         5058  +    rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
         5059  +  }
         5060  +  return rc;
         5061  +}
  5003   5062   
  5004   5063   #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
  5005   5064   static const char *proxySharedMemoryBasePath(unixFile *);
  5006   5065   #endif
  5007   5066   
  5008   5067   /*
  5009   5068   ** Open a shared-memory area associated with open database file pDbFd.  
................................................................................
  5039   5098   ** that no other processes are able to read or write the database.  In
  5040   5099   ** that case, we do not really need shared memory.  No shared memory
  5041   5100   ** file is created.  The shared memory will be simulated with heap memory.
  5042   5101   */
  5043   5102   static int unixOpenSharedMemory(unixFile *pDbFd){
  5044   5103     struct unixShm *p = 0;          /* The connection to be opened */
  5045   5104     struct unixShmNode *pShmNode;   /* The underlying mmapped file */
  5046         -  int rc;                         /* Result code */
         5105  +  int rc = SQLITE_OK;             /* Result code */
  5047   5106     unixInodeInfo *pInode;          /* The inode of fd */
  5048         -  char *zShmFilename;             /* Name of the file used for SHM */
         5107  +  char *zShm;             /* Name of the file used for SHM */
  5049   5108     int nShmFilename;               /* Size of the SHM filename in bytes */
  5050   5109   
  5051   5110     /* Allocate space for the new unixShm object. */
  5052   5111     p = sqlite3_malloc64( sizeof(*p) );
  5053   5112     if( p==0 ) return SQLITE_NOMEM_BKPT;
  5054   5113     memset(p, 0, sizeof(*p));
  5055   5114     assert( pDbFd->pShm==0 );
................................................................................
  5097   5156   #endif
  5098   5157       pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename );
  5099   5158       if( pShmNode==0 ){
  5100   5159         rc = SQLITE_NOMEM_BKPT;
  5101   5160         goto shm_open_err;
  5102   5161       }
  5103   5162       memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
  5104         -    zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
         5163  +    zShm = pShmNode->zFilename = (char*)&pShmNode[1];
  5105   5164   #ifdef SQLITE_SHM_DIRECTORY
  5106         -    sqlite3_snprintf(nShmFilename, zShmFilename, 
         5165  +    sqlite3_snprintf(nShmFilename, zShm, 
  5107   5166                        SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x",
  5108   5167                        (u32)sStat.st_ino, (u32)sStat.st_dev);
  5109   5168   #else
  5110         -    sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", zBasePath);
  5111         -    sqlite3FileSuffix3(pDbFd->zPath, zShmFilename);
         5169  +    sqlite3_snprintf(nShmFilename, zShm, "%s-shm", zBasePath);
         5170  +    sqlite3FileSuffix3(pDbFd->zPath, zShm);
  5112   5171   #endif
  5113   5172       pShmNode->h = -1;
  5114   5173       pDbFd->pInode->pShmNode = pShmNode;
  5115   5174       pShmNode->pInode = pDbFd->pInode;
  5116   5175       if( sqlite3GlobalConfig.bCoreMutex ){
  5117   5176         pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
  5118   5177         if( pShmNode->mutex==0 ){
  5119   5178           rc = SQLITE_NOMEM_BKPT;
  5120   5179           goto shm_open_err;
  5121   5180         }
  5122   5181       }
  5123   5182   
  5124   5183       if( pInode->bProcessLock==0 ){
  5125         -      int openFlags = O_RDWR | O_CREAT;
  5126         -      int fd;                     /* File descriptor for *-shm file */
  5127         -      if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0)
  5128   5184   #ifdef __APPLE__
  5129         -       /* On MacOS and iOS, avoid even trying to open a read-only SHM file
  5130         -       ** for writing, because doing so generates scary log messages */
  5131         -       || (osAccess(zShmFilename, R_OK|W_OK)!=0
  5132         -            && (errno==EPERM || errno==EACCES))
         5185  +      /* On MacOS and iOS, avoid even trying to open a read-only SHM file
         5186  +      ** for writing, because doing so generates scary log messages */
         5187  +      if( osAccess(zShmFilename, R_OK|W_OK)!=0 && (errno==EPERM || errno==EACCES) ){
         5188  +        pShmNode->h = -1;
         5189  +      }else
  5133   5190   #endif
  5134         -      ){
  5135         -        openFlags = O_RDONLY;
         5191  +      if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
         5192  +        pShmNode->h = robust_open(zShm, O_RDWR|O_CREAT, (sStat.st_mode&0777));
         5193  +      }
         5194  +      if( pShmNode->h<0 ){
         5195  +        pShmNode->h = robust_open(zShm, O_RDONLY, (sStat.st_mode&0777));
         5196  +        if( pShmNode->h<0 ){
         5197  +          rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShm);
         5198  +          goto shm_open_err;
         5199  +        }
  5136   5200           pShmNode->isReadonly = 1;
  5137   5201         }
  5138         -      fd = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
  5139         -
  5140         -      /* If it was not possible to open the *-shm file in read/write mode,
  5141         -      ** and the database file itself has been opened in read-only mode,
  5142         -      ** try to open the *-shm file in read-only mode as well. Even if the
  5143         -      ** database connection is read-only, it is still better to try opening
  5144         -      ** the *-shm file in read/write mode first, as the same file descriptor
  5145         -      ** may be also be used by a read/write database connection.  */
  5146         -#if defined(SQLITE_ENABLE_PERSIST_WAL)&&(SQLITE_ENABLE_LOCKING_STYLE \
  5147         -    || defined(__APPLE__))
  5148         -      if( fd<0 && (errno==EPERM || errno==EACCES) && pShmNode->isReadonly==0 
  5149         -       && (pDbFd->openFlags & O_RDWR)!=O_RDWR
  5150         -      ){
  5151         -        fd = robust_open(zShmFilename, O_RDONLY, (sStat.st_mode&0777));
  5152         -        pShmNode->isReadonly = 1;
  5153         -      }
  5154         -#endif
  5155         -      if( fd<0 ){
  5156         -        rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
  5157         -        goto shm_open_err;
  5158         -      }
  5159         -      pShmNode->h = fd;
  5160   5202   
  5161   5203         /* If this process is running as root, make sure that the SHM file
  5162   5204         ** is owned by the same user that owns the original database.  Otherwise,
  5163   5205         ** the original owner will not be able to connect.
  5164   5206         */
  5165   5207         robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
  5166         -  
  5167         -      /* Check to see if another process is holding the dead-man switch.
  5168         -      ** If not, truncate the file to zero length. 
  5169         -      */
  5170         -      rc = SQLITE_OK;
  5171         -      if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
  5172         -        if( robust_ftruncate(pShmNode->h, 0) ){
  5173         -          rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
  5174         -        }else{
  5175         -          /* If running as root set the uid/gid of the shm file to match
  5176         -          ** the database */
  5177         -          uid_t euid = geteuid();
  5178         -          if( (!pShmNode->isReadonly) && euid==0 && (euid!=sStat.st_uid || getegid()!=sStat.st_gid) ){
  5179         -            if( osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid) ){
  5180         -              rc = SQLITE_IOERR_SHMOPEN;
  5181         -            }
  5182         -          }
  5183         -        }
  5184         -      }
  5185         -      if( rc==SQLITE_OK ){
  5186         -        rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
  5187         -      }
  5188         -      if( rc ) goto shm_open_err;
         5208  +
         5209  +      rc = unixLockSharedMemory(pDbFd, pShmNode);
         5210  +      if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
  5189   5211       }
  5190   5212     }
  5191   5213   
  5192   5214     /* Make the new connection a child of the unixShmNode */
  5193   5215     p->pShmNode = pShmNode;
  5194   5216   #ifdef SQLITE_DEBUG
  5195   5217     p->id = pShmNode->nextShmId++;
................................................................................
  5205   5227     ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex 
  5206   5228     ** mutex.
  5207   5229     */
  5208   5230     sqlite3_mutex_enter(pShmNode->mutex);
  5209   5231     p->pNext = pShmNode->pFirst;
  5210   5232     pShmNode->pFirst = p;
  5211   5233     sqlite3_mutex_leave(pShmNode->mutex);
  5212         -  return SQLITE_OK;
         5234  +  return rc;
  5213   5235   
  5214   5236     /* Jump here on any error */
  5215   5237   shm_open_err:
  5216   5238     unixShmPurge(pDbFd);       /* This call frees pShmNode if required */
  5217   5239     sqlite3_free(p);
  5218   5240     unixLeaveMutex();
  5219   5241     return rc;
................................................................................
  5257   5279       rc = unixOpenSharedMemory(pDbFd);
  5258   5280       if( rc!=SQLITE_OK ) return rc;
  5259   5281     }
  5260   5282   
  5261   5283     p = pDbFd->pShm;
  5262   5284     pShmNode = p->pShmNode;
  5263   5285     sqlite3_mutex_enter(pShmNode->mutex);
         5286  +  if( pShmNode->isUnlocked ){
         5287  +    rc = unixLockSharedMemory(pDbFd, pShmNode);
         5288  +    if( rc!=SQLITE_OK ) goto shmpage_out;
         5289  +    pShmNode->isUnlocked = 0;
         5290  +  }
  5264   5291     assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
  5265   5292     assert( pShmNode->pInode==pDbFd->pInode );
  5266   5293     assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
  5267   5294     assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
  5268   5295   
  5269   5296     /* Minimum number of regions required to be mapped. */
  5270   5297     nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;

Changes to src/os_win.c.

  3678   3678   struct winShmNode {
  3679   3679     sqlite3_mutex *mutex;      /* Mutex to access this object */
  3680   3680     char *zFilename;           /* Name of the file */
  3681   3681     winFile hFile;             /* File handle from winOpen */
  3682   3682   
  3683   3683     int szRegion;              /* Size of shared-memory regions */
  3684   3684     int nRegion;               /* Size of array apRegion */
         3685  +  u8 isReadonly;             /* True if read-only */
         3686  +  u8 isUnlocked;             /* True if no DMS lock held */
         3687  +
  3685   3688     struct ShmRegion {
  3686   3689       HANDLE hMap;             /* File handle from CreateFileMapping */
  3687   3690       void *pMap;
  3688   3691     } *aRegion;
  3689   3692     DWORD lastErrno;           /* The Windows errno from the last I/O error */
  3690   3693   
  3691   3694     int nRef;                  /* Number of winShm objects pointing to this */
................................................................................
  3824   3827         sqlite3_free(p->aRegion);
  3825   3828         sqlite3_free(p);
  3826   3829       }else{
  3827   3830         pp = &p->pNext;
  3828   3831       }
  3829   3832     }
  3830   3833   }
         3834  +
         3835  +/*
         3836  +** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
         3837  +** take it now. Return SQLITE_OK if successful, or an SQLite error
         3838  +** code otherwise.
         3839  +**
         3840  +** If the DMS cannot be locked because this is a readonly_shm=1
         3841  +** connection and no other process already holds a lock, return
         3842  +** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
         3843  +*/
         3844  +static int winLockSharedMemory(winShmNode *pShmNode){
         3845  +  int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1);
         3846  +
         3847  +  if( rc==SQLITE_OK ){
         3848  +    if( pShmNode->isReadonly ){
         3849  +      pShmNode->isUnlocked = 1;
         3850  +      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
         3851  +      return SQLITE_READONLY_CANTINIT;
         3852  +    }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){
         3853  +      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
         3854  +      return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
         3855  +                         "winLockSharedMemory", pShmNode->zFilename);
         3856  +    }
         3857  +  }
         3858  +
         3859  +  if( rc==SQLITE_OK ){
         3860  +    winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
         3861  +  }
         3862  +
         3863  +  return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
         3864  +}
  3831   3865   
  3832   3866   /*
  3833   3867   ** Open the shared-memory area associated with database file pDbFd.
  3834   3868   **
  3835   3869   ** When opening a new shared-memory file, if no other instances of that
  3836   3870   ** file are currently open, in this process or in other processes, then
  3837   3871   ** the file must be truncated to zero length or have its header cleared.
  3838   3872   */
  3839   3873   static int winOpenSharedMemory(winFile *pDbFd){
  3840   3874     struct winShm *p;                  /* The connection to be opened */
  3841         -  struct winShmNode *pShmNode = 0;   /* The underlying mmapped file */
  3842         -  int rc;                            /* Result code */
  3843         -  struct winShmNode *pNew;           /* Newly allocated winShmNode */
         3875  +  winShmNode *pShmNode = 0;          /* The underlying mmapped file */
         3876  +  int rc = SQLITE_OK;                /* Result code */
         3877  +  int rc2 = SQLITE_ERROR;            /* winOpen result code */
         3878  +  winShmNode *pNew;                  /* Newly allocated winShmNode */
  3844   3879     int nName;                         /* Size of zName in bytes */
  3845   3880   
  3846   3881     assert( pDbFd->pShm==0 );    /* Not previously opened */
  3847   3882   
  3848   3883     /* Allocate space for the new sqlite3_shm object.  Also speculatively
  3849   3884     ** allocate space for a new winShmNode and filename.
  3850   3885     */
................................................................................
  3883   3918         pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
  3884   3919         if( pShmNode->mutex==0 ){
  3885   3920           rc = SQLITE_IOERR_NOMEM_BKPT;
  3886   3921           goto shm_open_err;
  3887   3922         }
  3888   3923       }
  3889   3924   
  3890         -    rc = winOpen(pDbFd->pVfs,
  3891         -                 pShmNode->zFilename,             /* Name of the file (UTF-8) */
  3892         -                 (sqlite3_file*)&pShmNode->hFile,  /* File handle here */
  3893         -                 SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
  3894         -                 0);
  3895         -    if( SQLITE_OK!=rc ){
  3896         -      goto shm_open_err;
  3897         -    }
  3898         -
  3899         -    /* Check to see if another process is holding the dead-man switch.
  3900         -    ** If not, truncate the file to zero length.
  3901         -    */
  3902         -    if( winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
  3903         -      rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
  3904         -      if( rc!=SQLITE_OK ){
  3905         -        rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
  3906         -                         "winOpenShm", pDbFd->zPath);
  3907         -      }
  3908         -    }
  3909         -    if( rc==SQLITE_OK ){
  3910         -      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
  3911         -      rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
  3912         -    }
  3913         -    if( rc ) goto shm_open_err;
         3925  +    if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
         3926  +      rc2 = winOpen(pDbFd->pVfs,
         3927  +                    pShmNode->zFilename,
         3928  +                    (sqlite3_file*)&pShmNode->hFile,
         3929  +                    SQLITE_OPEN_WAL|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,
         3930  +                    0);
         3931  +    }
         3932  +    if( rc2!=SQLITE_OK ){
         3933  +      rc2 = winOpen(pDbFd->pVfs,
         3934  +                    pShmNode->zFilename,
         3935  +                    (sqlite3_file*)&pShmNode->hFile,
         3936  +                    SQLITE_OPEN_WAL|SQLITE_OPEN_READONLY,
         3937  +                    0);
         3938  +      if( rc2!=SQLITE_OK ){
         3939  +        rc = winLogError(rc2, osGetLastError(), "winOpenShm",
         3940  +                         pShmNode->zFilename);
         3941  +        goto shm_open_err;
         3942  +      }
         3943  +      pShmNode->isReadonly = 1;
         3944  +    }
         3945  +
         3946  +    rc = winLockSharedMemory(pShmNode);
         3947  +    if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
  3914   3948     }
  3915   3949   
  3916   3950     /* Make the new connection a child of the winShmNode */
  3917   3951     p->pShmNode = pShmNode;
  3918   3952   #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
  3919   3953     p->id = pShmNode->nextShmId++;
  3920   3954   #endif
................................................................................
  3929   3963     ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
  3930   3964     ** mutex.
  3931   3965     */
  3932   3966     sqlite3_mutex_enter(pShmNode->mutex);
  3933   3967     p->pNext = pShmNode->pFirst;
  3934   3968     pShmNode->pFirst = p;
  3935   3969     sqlite3_mutex_leave(pShmNode->mutex);
  3936         -  return SQLITE_OK;
         3970  +  return rc;
  3937   3971   
  3938   3972     /* Jump here on any error */
  3939   3973   shm_open_err:
  3940   3974     winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
  3941   3975     winShmPurge(pDbFd->pVfs, 0);      /* This call frees pShmNode if required */
  3942   3976     sqlite3_free(p);
  3943   3977     sqlite3_free(pNew);
................................................................................
  4133   4167     int szRegion,                   /* Size of regions */
  4134   4168     int isWrite,                    /* True to extend file if necessary */
  4135   4169     void volatile **pp              /* OUT: Mapped memory */
  4136   4170   ){
  4137   4171     winFile *pDbFd = (winFile*)fd;
  4138   4172     winShm *pShm = pDbFd->pShm;
  4139   4173     winShmNode *pShmNode;
         4174  +  DWORD protect = PAGE_READWRITE;
         4175  +  DWORD flags = FILE_MAP_WRITE | FILE_MAP_READ;
  4140   4176     int rc = SQLITE_OK;
  4141   4177   
  4142   4178     if( !pShm ){
  4143   4179       rc = winOpenSharedMemory(pDbFd);
  4144   4180       if( rc!=SQLITE_OK ) return rc;
  4145   4181       pShm = pDbFd->pShm;
  4146   4182     }
  4147   4183     pShmNode = pShm->pShmNode;
  4148   4184   
  4149   4185     sqlite3_mutex_enter(pShmNode->mutex);
         4186  +  if( pShmNode->isUnlocked ){
         4187  +    rc = winLockSharedMemory(pShmNode);
         4188  +    if( rc!=SQLITE_OK ) goto shmpage_out;
         4189  +    pShmNode->isUnlocked = 0;
         4190  +  }
  4150   4191     assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
  4151   4192   
  4152   4193     if( pShmNode->nRegion<=iRegion ){
  4153   4194       struct ShmRegion *apNew;           /* New aRegion[] array */
  4154   4195       int nByte = (iRegion+1)*szRegion;  /* Minimum required file size */
  4155   4196       sqlite3_int64 sz;                  /* Current size of wal-index file */
  4156   4197   
................................................................................
  4188   4229           pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
  4189   4230       );
  4190   4231       if( !apNew ){
  4191   4232         rc = SQLITE_IOERR_NOMEM_BKPT;
  4192   4233         goto shmpage_out;
  4193   4234       }
  4194   4235       pShmNode->aRegion = apNew;
         4236  +
         4237  +    if( pShmNode->isReadonly ){
         4238  +      protect = PAGE_READONLY;
         4239  +      flags = FILE_MAP_READ;
         4240  +    }
  4195   4241   
  4196   4242       while( pShmNode->nRegion<=iRegion ){
  4197   4243         HANDLE hMap = NULL;         /* file-mapping handle */
  4198   4244         void *pMap = 0;             /* Mapped memory region */
  4199   4245   
  4200   4246   #if SQLITE_OS_WINRT
  4201   4247         hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
  4202         -          NULL, PAGE_READWRITE, nByte, NULL
         4248  +          NULL, protect, nByte, NULL
  4203   4249         );
  4204   4250   #elif defined(SQLITE_WIN32_HAS_WIDE)
  4205   4251         hMap = osCreateFileMappingW(pShmNode->hFile.h,
  4206         -          NULL, PAGE_READWRITE, 0, nByte, NULL
         4252  +          NULL, protect, 0, nByte, NULL
  4207   4253         );
  4208   4254   #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
  4209   4255         hMap = osCreateFileMappingA(pShmNode->hFile.h,
  4210         -          NULL, PAGE_READWRITE, 0, nByte, NULL
         4256  +          NULL, protect, 0, nByte, NULL
  4211   4257         );
  4212   4258   #endif
  4213   4259         OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
  4214   4260                  osGetCurrentProcessId(), pShmNode->nRegion, nByte,
  4215   4261                  hMap ? "ok" : "failed"));
  4216   4262         if( hMap ){
  4217   4263           int iOffset = pShmNode->nRegion*szRegion;
  4218   4264           int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
  4219   4265   #if SQLITE_OS_WINRT
  4220         -        pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
         4266  +        pMap = osMapViewOfFileFromApp(hMap, flags,
  4221   4267               iOffset - iOffsetShift, szRegion + iOffsetShift
  4222   4268           );
  4223   4269   #else
  4224         -        pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
         4270  +        pMap = osMapViewOfFile(hMap, flags,
  4225   4271               0, iOffset - iOffsetShift, szRegion + iOffsetShift
  4226   4272           );
  4227   4273   #endif
  4228   4274           OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n",
  4229   4275                    osGetCurrentProcessId(), pShmNode->nRegion, iOffset,
  4230   4276                    szRegion, pMap ? "ok" : "failed"));
  4231   4277         }
................................................................................
  4248   4294       int iOffset = iRegion*szRegion;
  4249   4295       int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
  4250   4296       char *p = (char *)pShmNode->aRegion[iRegion].pMap;
  4251   4297       *pp = (void *)&p[iOffsetShift];
  4252   4298     }else{
  4253   4299       *pp = 0;
  4254   4300     }
         4301  +  if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
  4255   4302     sqlite3_mutex_leave(pShmNode->mutex);
  4256   4303     return rc;
  4257   4304   }
  4258   4305   
  4259   4306   #else
  4260   4307   # define winShmMap     0
  4261   4308   # define winShmLock    0

Changes to src/printf.c.

  1088   1088     StrAccum acc;
  1089   1089     char zBuf[500];
  1090   1090     sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
  1091   1091     va_start(ap,zFormat);
  1092   1092     sqlite3VXPrintf(&acc, zFormat, ap);
  1093   1093     va_end(ap);
  1094   1094     sqlite3StrAccumFinish(&acc);
         1095  +#ifdef SQLITE_OS_TRACE_PROC
         1096  +  {
         1097  +    extern void SQLITE_OS_TRACE_PROC(const char *zBuf, int nBuf);
         1098  +    SQLITE_OS_TRACE_PROC(zBuf, sizeof(zBuf));
         1099  +  }
         1100  +#else
  1095   1101     fprintf(stdout,"%s", zBuf);
  1096   1102     fflush(stdout);
         1103  +#endif
  1097   1104   }
  1098   1105   #endif
  1099   1106   
  1100   1107   
  1101   1108   /*
  1102   1109   ** variable-argument wrapper around sqlite3VXPrintf().  The bFlags argument
  1103   1110   ** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.

Changes to src/sqlite.h.in.

   504    504   #define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))
   505    505   #define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))
   506    506   #define SQLITE_BUSY_SNAPSHOT           (SQLITE_BUSY   |  (2<<8))
   507    507   #define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))
   508    508   #define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))
   509    509   #define SQLITE_CANTOPEN_FULLPATH       (SQLITE_CANTOPEN | (3<<8))
   510    510   #define SQLITE_CANTOPEN_CONVPATH       (SQLITE_CANTOPEN | (4<<8))
          511  +#define SQLITE_CANTOPEN_DIRTYWAL       (SQLITE_CANTOPEN | (5<<8))
   511    512   #define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))
   512    513   #define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))
   513    514   #define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))
   514    515   #define SQLITE_READONLY_ROLLBACK       (SQLITE_READONLY | (3<<8))
   515    516   #define SQLITE_READONLY_DBMOVED        (SQLITE_READONLY | (4<<8))
          517  +#define SQLITE_READONLY_CANTINIT       (SQLITE_READONLY | (5<<8))
   516    518   #define SQLITE_ABORT_ROLLBACK          (SQLITE_ABORT | (2<<8))
   517    519   #define SQLITE_CONSTRAINT_CHECK        (SQLITE_CONSTRAINT | (1<<8))
   518    520   #define SQLITE_CONSTRAINT_COMMITHOOK   (SQLITE_CONSTRAINT | (2<<8))
   519    521   #define SQLITE_CONSTRAINT_FOREIGNKEY   (SQLITE_CONSTRAINT | (3<<8))
   520    522   #define SQLITE_CONSTRAINT_FUNCTION     (SQLITE_CONSTRAINT | (4<<8))
   521    523   #define SQLITE_CONSTRAINT_NOTNULL      (SQLITE_CONSTRAINT | (5<<8))
   522    524   #define SQLITE_CONSTRAINT_PRIMARYKEY   (SQLITE_CONSTRAINT | (6<<8))

Changes to src/wal.c.

   451    451     u8 exclusiveMode;          /* Non-zero if connection is in exclusive mode */
   452    452     u8 writeLock;              /* True if in a write transaction */
   453    453     u8 ckptLock;               /* True if holding a checkpoint lock */
   454    454     u8 readOnly;               /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
   455    455     u8 truncateOnCommit;       /* True to truncate WAL file on commit */
   456    456     u8 syncHeader;             /* Fsync the WAL header if true */
   457    457     u8 padToSectorBoundary;    /* Pad transactions out to the next sector */
          458  +  u8 bShmUnreliable;         /* SHM content is read-only and unreliable */
   458    459     WalIndexHdr hdr;           /* Wal-index header for current transaction */
   459    460     u32 minFrame;              /* Ignore wal frames before this one */
   460    461     u32 iReCksum;              /* On commit, recalculate checksums from here */
   461    462     const char *zWalName;      /* Name of WAL file */
   462    463     u32 nCkpt;                 /* Checkpoint sequence counter in the wal-header */
   463    464   #ifdef SQLITE_DEBUG
   464    465     u8 lockError;              /* True if a locking error has occurred */
................................................................................
   539    540       sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \
   540    541   )
   541    542   
   542    543   /*
   543    544   ** Obtain a pointer to the iPage'th page of the wal-index. The wal-index
   544    545   ** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are
   545    546   ** numbered from zero.
          547  +**
          548  +** If the wal-index is currently smaller the iPage pages then the size
          549  +** of the wal-index might be increased, but only if it is safe to do
          550  +** so.  It is safe to enlarge the wal-index if pWal->writeLock is true
          551  +** or pWal->exclusiveMode==WAL_HEAPMEMORY_MODE.
   546    552   **
   547    553   ** If this call is successful, *ppPage is set to point to the wal-index
   548    554   ** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs,
   549    555   ** then an SQLite error code is returned and *ppPage is set to 0.
   550    556   */
   551    557   static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
   552    558     int rc = SQLITE_OK;
................................................................................
   571    577       if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
   572    578         pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
   573    579         if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT;
   574    580       }else{
   575    581         rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, 
   576    582             pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
   577    583         );
   578         -      if( rc==SQLITE_READONLY ){
          584  +      assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 );
          585  +      testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK );
          586  +      if( (rc&0xff)==SQLITE_READONLY ){
   579    587           pWal->readOnly |= WAL_SHM_RDONLY;
   580         -        rc = SQLITE_OK;
          588  +        if( rc==SQLITE_READONLY ){
          589  +          rc = SQLITE_OK;
          590  +        }
   581    591         }
   582    592       }
   583    593     }
   584    594   
   585    595     *ppPage = pWal->apWiData[iPage];
   586    596     assert( iPage==0 || *ppPage || rc!=SQLITE_OK );
   587    597     return rc;
................................................................................
  1095   1105   ** the necessary locks, this routine returns SQLITE_BUSY.
  1096   1106   */
  1097   1107   static int walIndexRecover(Wal *pWal){
  1098   1108     int rc;                         /* Return Code */
  1099   1109     i64 nSize;                      /* Size of log file */
  1100   1110     u32 aFrameCksum[2] = {0, 0};
  1101   1111     int iLock;                      /* Lock offset to lock for checkpoint */
  1102         -  int nLock;                      /* Number of locks to hold */
  1103   1112   
  1104   1113     /* Obtain an exclusive lock on all byte in the locking range not already
  1105   1114     ** locked by the caller. The caller is guaranteed to have locked the
  1106   1115     ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte.
  1107   1116     ** If successful, the same bytes that are locked here are unlocked before
  1108   1117     ** this function returns.
  1109   1118     */
  1110   1119     assert( pWal->ckptLock==1 || pWal->ckptLock==0 );
  1111   1120     assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 );
  1112   1121     assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE );
  1113   1122     assert( pWal->writeLock );
  1114   1123     iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock;
  1115         -  nLock = SQLITE_SHM_NLOCK - iLock;
  1116         -  rc = walLockExclusive(pWal, iLock, nLock);
         1124  +  rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
         1125  +  if( rc==SQLITE_OK ){
         1126  +    rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
         1127  +    if( rc!=SQLITE_OK ){
         1128  +      walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
         1129  +    }
         1130  +  }
  1117   1131     if( rc ){
  1118   1132       return rc;
  1119   1133     }
         1134  +
  1120   1135     WALTRACE(("WAL%p: recovery begin...\n", pWal));
  1121   1136   
  1122   1137     memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
  1123   1138   
  1124   1139     rc = sqlite3OsFileSize(pWal->pWalFd, &nSize);
  1125   1140     if( rc!=SQLITE_OK ){
  1126   1141       goto recovery_error;
................................................................................
  1250   1265             pWal->hdr.mxFrame, pWal->zWalName
  1251   1266         );
  1252   1267       }
  1253   1268     }
  1254   1269   
  1255   1270   recovery_error:
  1256   1271     WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
  1257         -  walUnlockExclusive(pWal, iLock, nLock);
         1272  +  walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
         1273  +  walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
  1258   1274     return rc;
  1259   1275   }
  1260   1276   
  1261   1277   /*
  1262   1278   ** Close an open wal-index.
  1263   1279   */
  1264   1280   static void walIndexClose(Wal *pWal, int isDelete){
  1265         -  if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
         1281  +  if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE || pWal->bShmUnreliable ){
  1266   1282       int i;
  1267   1283       for(i=0; i<pWal->nWiData; i++){
  1268   1284         sqlite3_free((void *)pWal->apWiData[i]);
  1269   1285         pWal->apWiData[i] = 0;
  1270   1286       }
  1271         -  }else{
         1287  +  }
         1288  +  if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){
  1272   1289       sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
  1273   1290     }
  1274   1291   }
  1275   1292   
  1276   1293   /* 
  1277   1294   ** Open a connection to the WAL file zWalName. The database file must 
  1278   1295   ** already be opened on connection pDbFd. The buffer that zWalName points
................................................................................
  2065   2082       testcase( pWal->szPage>=65536 );
  2066   2083     }
  2067   2084   
  2068   2085     /* The header was successfully read. Return zero. */
  2069   2086     return 0;
  2070   2087   }
  2071   2088   
         2089  +/*
         2090  +** This is the value that walTryBeginRead returns when it needs to
         2091  +** be retried.
         2092  +*/
         2093  +#define WAL_RETRY  (-1)
         2094  +
  2072   2095   /*
  2073   2096   ** Read the wal-index header from the wal-index and into pWal->hdr.
  2074   2097   ** If the wal-header appears to be corrupt, try to reconstruct the
  2075   2098   ** wal-index from the WAL before returning.
  2076   2099   **
  2077   2100   ** Set *pChanged to 1 if the wal-index header value in pWal->hdr is
  2078   2101   ** changed by this operation.  If pWal->hdr is unchanged, set *pChanged
................................................................................
  2088   2111   
  2089   2112     /* Ensure that page 0 of the wal-index (the page that contains the 
  2090   2113     ** wal-index header) is mapped. Return early if an error occurs here.
  2091   2114     */
  2092   2115     assert( pChanged );
  2093   2116     rc = walIndexPage(pWal, 0, &page0);
  2094   2117     if( rc!=SQLITE_OK ){
  2095         -    return rc;
  2096         -  };
  2097         -  assert( page0 || pWal->writeLock==0 );
         2118  +    assert( rc!=SQLITE_READONLY ); /* READONLY changed to OK in walIndexPage */
         2119  +    if( rc==SQLITE_READONLY_CANTINIT ){
         2120  +      /* The SQLITE_READONLY_CANTINIT return means that the shared-memory
         2121  +      ** was openable but is not writable, and this thread is unable to
         2122  +      ** confirm that another write-capable connection has the shared-memory
         2123  +      ** open, and hence the content of the shared-memory is unreliable,
         2124  +      ** since the shared-memory might be inconsistent with the WAL file
         2125  +      ** and there is no writer on hand to fix it. */
         2126  +      assert( page0==0 );
         2127  +      assert( pWal->writeLock==0 );
         2128  +      assert( pWal->readOnly & WAL_SHM_RDONLY );
         2129  +      pWal->bShmUnreliable = 1;
         2130  +      pWal->exclusiveMode = WAL_HEAPMEMORY_MODE;
         2131  +      *pChanged = 1;
         2132  +    }else{
         2133  +      return rc; /* Any other non-OK return is just an error */
         2134  +    }
         2135  +  }else{
         2136  +    /* page0 can be NULL if the SHM is zero bytes in size and pWal->writeLock
         2137  +    ** is zero, which prevents the SHM from growing */
         2138  +    testcase( page0!=0 );
         2139  +  }
         2140  +  assert( page0!=0 || pWal->writeLock==0 );
  2098   2141   
  2099   2142     /* If the first page of the wal-index has been mapped, try to read the
  2100   2143     ** wal-index header immediately, without holding any lock. This usually
  2101   2144     ** works, but may fail if the wal-index header is corrupt or currently 
  2102   2145     ** being modified by another thread or process.
  2103   2146     */
  2104   2147     badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
  2105   2148   
  2106   2149     /* If the first attempt failed, it might have been due to a race
  2107   2150     ** with a writer.  So get a WRITE lock and try again.
  2108   2151     */
  2109   2152     assert( badHdr==0 || pWal->writeLock==0 );
  2110   2153     if( badHdr ){
  2111         -    if( pWal->readOnly & WAL_SHM_RDONLY ){
         2154  +    if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
  2112   2155         if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
  2113   2156           walUnlockShared(pWal, WAL_WRITE_LOCK);
  2114   2157           rc = SQLITE_READONLY_RECOVERY;
  2115   2158         }
  2116   2159       }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
  2117   2160         pWal->writeLock = 1;
  2118   2161         if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
................................................................................
  2134   2177     /* If the header is read successfully, check the version number to make
  2135   2178     ** sure the wal-index was not constructed with some future format that
  2136   2179     ** this version of SQLite cannot understand.
  2137   2180     */
  2138   2181     if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){
  2139   2182       rc = SQLITE_CANTOPEN_BKPT;
  2140   2183     }
  2141         -
  2142         -  return rc;
  2143         -}
  2144         -
  2145         -/*
  2146         -** This is the value that walTryBeginRead returns when it needs to
  2147         -** be retried.
  2148         -*/
  2149         -#define WAL_RETRY  (-1)
         2184  +  if( pWal->bShmUnreliable ){
         2185  +    if( rc!=SQLITE_OK ){
         2186  +      walIndexClose(pWal, 0);
         2187  +      pWal->bShmUnreliable = 0;
         2188  +      assert( pWal->nWiData>0 && pWal->apWiData[0]==0 );
         2189  +      /* walIndexRecover() might have returned SHORT_READ if a concurrent
         2190  +      ** writer truncated the WAL out from under it.  If that happens, it
         2191  +      ** indicates that a writer has fixed the SHM file for us, so retry */
         2192  +      if( rc==SQLITE_IOERR_SHORT_READ ) rc = WAL_RETRY;
         2193  +    }
         2194  +    pWal->exclusiveMode = WAL_NORMAL_MODE;
         2195  +  }
         2196  +
         2197  +  return rc;
         2198  +}
         2199  +
         2200  +/*
         2201  +** Open a transaction in a connection where the shared-memory is read-only
         2202  +** and where we cannot verify that there is a separate write-capable connection
         2203  +** on hand to keep the shared-memory up-to-date with the WAL file.
         2204  +**
         2205  +** This can happen, for example, when the shared-memory is implemented by
         2206  +** memory-mapping a *-shm file, where a prior writer has shut down and
         2207  +** left the *-shm file on disk, and now the present connection is trying
         2208  +** to use that database but lacks write permission on the *-shm file.
         2209  +** Other scenarios are also possible, depending on the VFS implementation.
         2210  +**
         2211  +** Precondition:
         2212  +**
         2213  +**    The *-wal file has been read and an appropriate wal-index has been
         2214  +**    constructed in pWal->apWiData[] using heap memory instead of shared
         2215  +**    memory. 
         2216  +**
         2217  +** If this function returns SQLITE_OK, then the read transaction has
         2218  +** been successfully opened. In this case output variable (*pChanged) 
         2219  +** is set to true before returning if the caller should discard the
         2220  +** contents of the page cache before proceeding. Or, if it returns 
         2221  +** WAL_RETRY, then the heap memory wal-index has been discarded and 
         2222  +** the caller should retry opening the read transaction from the 
         2223  +** beginning (including attempting to map the *-shm file). 
         2224  +**
         2225  +** If an error occurs, an SQLite error code is returned.
         2226  +*/
         2227  +static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
         2228  +  i64 szWal;                      /* Size of wal file on disk in bytes */
         2229  +  i64 iOffset;                    /* Current offset when reading wal file */
         2230  +  u8 aBuf[WAL_HDRSIZE];           /* Buffer to load WAL header into */
         2231  +  u8 *aFrame = 0;                 /* Malloc'd buffer to load entire frame */
         2232  +  int szFrame;                    /* Number of bytes in buffer aFrame[] */
         2233  +  u8 *aData;                      /* Pointer to data part of aFrame buffer */
         2234  +  volatile void *pDummy;          /* Dummy argument for xShmMap */
         2235  +  int rc;                         /* Return code */
         2236  +  u32 aSaveCksum[2];              /* Saved copy of pWal->hdr.aFrameCksum */
         2237  +
         2238  +  assert( pWal->bShmUnreliable );
         2239  +  assert( pWal->readOnly & WAL_SHM_RDONLY );
         2240  +  assert( pWal->nWiData>0 && pWal->apWiData[0] );
         2241  +
         2242  +  /* Take WAL_READ_LOCK(0). This has the effect of preventing any
         2243  +  ** writers from running a checkpoint, but does not stop them
         2244  +  ** from running recovery.  */
         2245  +  rc = walLockShared(pWal, WAL_READ_LOCK(0));
         2246  +  if( rc!=SQLITE_OK ){
         2247  +    if( rc==SQLITE_BUSY ) rc = WAL_RETRY;
         2248  +    goto begin_unreliable_shm_out;
         2249  +  }
         2250  +  pWal->readLock = 0;
         2251  +
         2252  +  /* Check to see if a separate writer has attached to the shared-memory area,
         2253  +  ** thus making the shared-memory "reliable" again.  Do this by invoking
         2254  +  ** the xShmMap() routine of the VFS and looking to see if the return
         2255  +  ** is SQLITE_READONLY instead of SQLITE_READONLY_CANTINIT.
         2256  +  **
         2257  +  ** If the shared-memory is now "reliable" return WAL_RETRY, which will
         2258  +  ** cause the heap-memory WAL-index to be discarded and the actual
         2259  +  ** shared memory to be used in its place.
         2260  +  **
         2261  +  ** This step is important because, even though this connection is holding
         2262  +  ** the WAL_READ_LOCK(0) which prevents a checkpoint, a writer might
         2263  +  ** have already checkpointed the WAL file and, while the current
         2264  +  ** is active, wrap the WAL and start overwriting frames that this
         2265  +  ** process wants to use.
         2266  +  **
         2267  +  ** Once sqlite3OsShmMap() has been called for an sqlite3_file and has
         2268  +  ** returned any SQLITE_READONLY value, it must return only SQLITE_READONLY
         2269  +  ** or SQLITE_READONLY_CANTINIT or some error for all subsequent invocations,
         2270  +  ** even if some external agent does a "chmod" to make the shared-memory
         2271  +  ** writable by us, until sqlite3OsShmUnmap() has been called.
         2272  +  ** This is a requirement on the VFS implementation.
         2273  +   */
         2274  +  rc = sqlite3OsShmMap(pWal->pDbFd, 0, WALINDEX_PGSZ, 0, &pDummy);
         2275  +  assert( rc!=SQLITE_OK ); /* SQLITE_OK not possible for read-only connection */
         2276  +  if( rc!=SQLITE_READONLY_CANTINIT ){
         2277  +    rc = (rc==SQLITE_READONLY ? WAL_RETRY : rc);
         2278  +    goto begin_unreliable_shm_out;
         2279  +  }
         2280  +
         2281  +  /* We reach this point only if the real shared-memory is still unreliable.
         2282  +  ** Assume the in-memory WAL-index substitute is correct and load it
         2283  +  ** into pWal->hdr.
         2284  +  */
         2285  +  memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
         2286  +
         2287  +  /* Make sure some writer hasn't come in and changed the WAL file out
         2288  +  ** from under us, then disconnected, while we were not looking.
         2289  +  */
         2290  +  rc = sqlite3OsFileSize(pWal->pWalFd, &szWal);
         2291  +  if( rc!=SQLITE_OK ){
         2292  +    goto begin_unreliable_shm_out;
         2293  +  }
         2294  +  if( szWal<WAL_HDRSIZE ){
         2295  +    /* If the wal file is too small to contain a wal-header and the
         2296  +    ** wal-index header has mxFrame==0, then it must be safe to proceed
         2297  +    ** reading the database file only. However, the page cache cannot
         2298  +    ** be trusted, as a read/write connection may have connected, written
         2299  +    ** the db, run a checkpoint, truncated the wal file and disconnected
         2300  +    ** since this client's last read transaction.  */
         2301  +    *pChanged = 1;
         2302  +    rc = (pWal->hdr.mxFrame==0 ? SQLITE_OK : WAL_RETRY);
         2303  +    goto begin_unreliable_shm_out;
         2304  +  }
         2305  +
         2306  +  /* Check the salt keys at the start of the wal file still match. */
         2307  +  rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
         2308  +  if( rc!=SQLITE_OK ){
         2309  +    goto begin_unreliable_shm_out;
         2310  +  }
         2311  +  if( memcmp(&pWal->hdr.aSalt, &aBuf[16], 8) ){
         2312  +    /* Some writer has wrapped the WAL file while we were not looking.
         2313  +    ** Return WAL_RETRY which will cause the in-memory WAL-index to be
         2314  +    ** rebuilt. */
         2315  +    rc = WAL_RETRY;
         2316  +    goto begin_unreliable_shm_out;
         2317  +  }
         2318  +
         2319  +  /* Allocate a buffer to read frames into */
         2320  +  szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE;
         2321  +  aFrame = (u8 *)sqlite3_malloc64(szFrame);
         2322  +  if( aFrame==0 ){
         2323  +    rc = SQLITE_NOMEM_BKPT;
         2324  +    goto begin_unreliable_shm_out;
         2325  +  }
         2326  +  aData = &aFrame[WAL_FRAME_HDRSIZE];
         2327  +
         2328  +  /* Check to see if a complete transaction has been appended to the
         2329  +  ** wal file since the heap-memory wal-index was created. If so, the
         2330  +  ** heap-memory wal-index is discarded and WAL_RETRY returned to
         2331  +  ** the caller.  */
         2332  +  aSaveCksum[0] = pWal->hdr.aFrameCksum[0];
         2333  +  aSaveCksum[1] = pWal->hdr.aFrameCksum[1];
         2334  +  for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage); 
         2335  +      iOffset+szFrame<=szWal; 
         2336  +      iOffset+=szFrame
         2337  +  ){
         2338  +    u32 pgno;                   /* Database page number for frame */
         2339  +    u32 nTruncate;              /* dbsize field from frame header */
         2340  +
         2341  +    /* Read and decode the next log frame. */
         2342  +    rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
         2343  +    if( rc!=SQLITE_OK ) break;
         2344  +    if( !walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame) ) break;
         2345  +
         2346  +    /* If nTruncate is non-zero, then a complete transaction has been
         2347  +    ** appended to this wal file. Set rc to WAL_RETRY and break out of
         2348  +    ** the loop.  */
         2349  +    if( nTruncate ){
         2350  +      rc = WAL_RETRY;
         2351  +      break;
         2352  +    }
         2353  +  }
         2354  +  pWal->hdr.aFrameCksum[0] = aSaveCksum[0];
         2355  +  pWal->hdr.aFrameCksum[1] = aSaveCksum[1];
         2356  +
         2357  + begin_unreliable_shm_out:
         2358  +  sqlite3_free(aFrame);
         2359  +  if( rc!=SQLITE_OK ){
         2360  +    int i;
         2361  +    for(i=0; i<pWal->nWiData; i++){
         2362  +      sqlite3_free((void*)pWal->apWiData[i]);
         2363  +      pWal->apWiData[i] = 0;
         2364  +    }
         2365  +    pWal->bShmUnreliable = 0;
         2366  +    sqlite3WalEndReadTransaction(pWal);
         2367  +    *pChanged = 1;
         2368  +  }
         2369  +  return rc;
         2370  +}
  2150   2371   
  2151   2372   /*
  2152   2373   ** Attempt to start a read transaction.  This might fail due to a race or
  2153   2374   ** other transient condition.  When that happens, it returns WAL_RETRY to
  2154   2375   ** indicate to the caller that it is safe to retry immediately.
  2155   2376   **
  2156   2377   ** On success return SQLITE_OK.  On a permanent failure (such an
................................................................................
  2204   2425     int mxI;                        /* Index of largest aReadMark[] value */
  2205   2426     int i;                          /* Loop counter */
  2206   2427     int rc = SQLITE_OK;             /* Return code  */
  2207   2428     u32 mxFrame;                    /* Wal frame to lock to */
  2208   2429   
  2209   2430     assert( pWal->readLock<0 );     /* Not currently locked */
  2210   2431   
         2432  +  /* useWal may only be set for read/write connections */
         2433  +  assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 );
         2434  +
  2211   2435     /* Take steps to avoid spinning forever if there is a protocol error.
  2212   2436     **
  2213   2437     ** Circumstances that cause a RETRY should only last for the briefest
  2214   2438     ** instances of time.  No I/O or other system calls are done while the
  2215   2439     ** locks are held, so the locks should not be held for very long. But 
  2216   2440     ** if we are unlucky, another process that is holding a lock might get
  2217   2441     ** paged out or take a page-fault that is time-consuming to resolve, 
................................................................................
  2232   2456         return SQLITE_PROTOCOL;
  2233   2457       }
  2234   2458       if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
  2235   2459       sqlite3OsSleep(pWal->pVfs, nDelay);
  2236   2460     }
  2237   2461   
  2238   2462     if( !useWal ){
  2239         -    rc = walIndexReadHdr(pWal, pChanged);
         2463  +    assert( rc==SQLITE_OK );
         2464  +    if( pWal->bShmUnreliable==0 ){
         2465  +      rc = walIndexReadHdr(pWal, pChanged);
         2466  +    }
  2240   2467       if( rc==SQLITE_BUSY ){
  2241   2468         /* If there is not a recovery running in another thread or process
  2242   2469         ** then convert BUSY errors to WAL_RETRY.  If recovery is known to
  2243   2470         ** be running, convert BUSY to BUSY_RECOVERY.  There is a race here
  2244   2471         ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY
  2245   2472         ** would be technically correct.  But the race is benign since with
  2246   2473         ** WAL_RETRY this routine will be called again and will probably be
................................................................................
  2261   2488         }else if( rc==SQLITE_BUSY ){
  2262   2489           rc = SQLITE_BUSY_RECOVERY;
  2263   2490         }
  2264   2491       }
  2265   2492       if( rc!=SQLITE_OK ){
  2266   2493         return rc;
  2267   2494       }
         2495  +    else if( pWal->bShmUnreliable ){
         2496  +      return walBeginShmUnreliable(pWal, pChanged);
         2497  +    }
  2268   2498     }
  2269   2499   
         2500  +  assert( pWal->nWiData>0 );
         2501  +  assert( pWal->apWiData[0]!=0 );
  2270   2502     pInfo = walCkptInfo(pWal);
  2271         -  if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame 
         2503  +  if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
  2272   2504   #ifdef SQLITE_ENABLE_SNAPSHOT
  2273   2505      && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0
  2274   2506        || 0==memcmp(&pWal->hdr, pWal->pSnapshot, sizeof(WalIndexHdr)))
  2275   2507   #endif
  2276   2508     ){
  2277   2509       /* The WAL has been completely backfilled (or it is empty).
  2278   2510       ** and can be safely ignored.
................................................................................
  2338   2570         }else if( rc!=SQLITE_BUSY ){
  2339   2571           return rc;
  2340   2572         }
  2341   2573       }
  2342   2574     }
  2343   2575     if( mxI==0 ){
  2344   2576       assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
  2345         -    return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
         2577  +    return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
  2346   2578     }
  2347   2579   
  2348   2580     rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
  2349   2581     if( rc ){
  2350   2582       return rc==SQLITE_BUSY ? WAL_RETRY : rc;
  2351   2583     }
  2352   2584     /* Now that the read-lock has been obtained, check that neither the
................................................................................
  2610   2842   
  2611   2843     /* If the "last page" field of the wal-index header snapshot is 0, then
  2612   2844     ** no data will be read from the wal under any circumstances. Return early
  2613   2845     ** in this case as an optimization.  Likewise, if pWal->readLock==0, 
  2614   2846     ** then the WAL is ignored by the reader so return early, as if the 
  2615   2847     ** WAL were empty.
  2616   2848     */
  2617         -  if( iLast==0 || pWal->readLock==0 ){
         2849  +  if( iLast==0 || (pWal->readLock==0 && pWal->bShmUnreliable==0) ){
  2618   2850       *piRead = 0;
  2619   2851       return SQLITE_OK;
  2620   2852     }
  2621   2853   
  2622   2854     /* Search the hash table or tables for an entry matching page number
  2623   2855     ** pgno. Each iteration of the following for() loop searches one
  2624   2856     ** hash table (each hash table indexes up to HASHTABLE_NPAGE frames).
................................................................................
  2673   2905   #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
  2674   2906     /* If expensive assert() statements are available, do a linear search
  2675   2907     ** of the wal-index file content. Make sure the results agree with the
  2676   2908     ** result obtained using the hash indexes above.  */
  2677   2909     {
  2678   2910       u32 iRead2 = 0;
  2679   2911       u32 iTest;
  2680         -    assert( pWal->minFrame>0 );
  2681         -    for(iTest=iLast; iTest>=pWal->minFrame; iTest--){
         2912  +    assert( pWal->bShmUnreliable || pWal->minFrame>0 );
         2913  +    for(iTest=iLast; iTest>=pWal->minFrame && iTest>0; iTest--){
  2682   2914         if( walFramePgno(pWal, iTest)==pgno ){
  2683   2915           iRead2 = iTest;
  2684   2916           break;
  2685   2917         }
  2686   2918       }
  2687   2919       assert( iRead==iRead2 );
  2688   2920     }
................................................................................
  3485   3717     ** locks are taken in this case). Nor should the pager attempt to
  3486   3718     ** upgrade to exclusive-mode following such an error.
  3487   3719     */
  3488   3720     assert( pWal->readLock>=0 || pWal->lockError );
  3489   3721     assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) );
  3490   3722   
  3491   3723     if( op==0 ){
  3492         -    if( pWal->exclusiveMode ){
  3493         -      pWal->exclusiveMode = 0;
         3724  +    if( pWal->exclusiveMode!=WAL_NORMAL_MODE ){
         3725  +      pWal->exclusiveMode = WAL_NORMAL_MODE;
  3494   3726         if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){
  3495         -        pWal->exclusiveMode = 1;
         3727  +        pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
  3496   3728         }
  3497         -      rc = pWal->exclusiveMode==0;
         3729  +      rc = pWal->exclusiveMode==WAL_NORMAL_MODE;
  3498   3730       }else{
  3499   3731         /* Already in locking_mode=NORMAL */
  3500   3732         rc = 0;
  3501   3733       }
  3502   3734     }else if( op>0 ){
  3503         -    assert( pWal->exclusiveMode==0 );
         3735  +    assert( pWal->exclusiveMode==WAL_NORMAL_MODE );
  3504   3736       assert( pWal->readLock>=0 );
  3505   3737       walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  3506         -    pWal->exclusiveMode = 1;
         3738  +    pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
  3507   3739       rc = 1;
  3508   3740     }else{
  3509         -    rc = pWal->exclusiveMode==0;
         3741  +    rc = pWal->exclusiveMode==WAL_NORMAL_MODE;
  3510   3742     }
  3511   3743     return rc;
  3512   3744   }
  3513   3745   
  3514   3746   /* 
  3515   3747   ** Return true if the argument is non-NULL and the WAL module is using
  3516   3748   ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the

Changes to test/wal2.test.

   126    126     }
   127    127   } {4 10}
   128    128   do_test wal2-1.1 {
   129    129     execsql { SELECT count(a), sum(a) FROM t1 } db2
   130    130   } {4 10}
   131    131   
   132    132   set RECOVER [list                                      \
   133         -  {0 1 lock exclusive}   {1 7 lock exclusive}          \
   134         -  {1 7 unlock exclusive} {0 1 unlock exclusive}        \
          133  +  {0 1 lock exclusive}   {1 2 lock exclusive} {4 4 lock exclusive} \
          134  +  {1 2 unlock exclusive} {4 4 unlock exclusive} {0 1 unlock exclusive}  \
   135    135   ]
   136    136   set READ [list                                         \
   137    137     {4 1 lock shared}    {4 1 unlock shared}             \
   138    138   ]
   139    139   set INITSLOT [list                                     \
   140    140     {4 1 lock exclusive} {4 1 unlock exclusive}          \
   141    141   ]
................................................................................
   397    397   # UPDATE: This has now changed. When running a checkpoint, if recovery is
   398    398   # required the client grabs all exclusive locks (just as it would for a
   399    399   # recovery performed as a pre-cursor to a normal database transaction).
   400    400   #
   401    401   set expected_locks [list]
   402    402   lappend expected_locks {1 1 lock exclusive}   ;# Lock checkpoint
   403    403   lappend expected_locks {0 1 lock exclusive}   ;# Lock writer
   404         -lappend expected_locks {2 6 lock exclusive}   ;# Lock recovery & all aReadMark[]
   405         -lappend expected_locks {2 6 unlock exclusive} ;# Unlock recovery & aReadMark[]
          404  +lappend expected_locks {2 1 lock exclusive}   ;# Lock recovery
          405  +lappend expected_locks {4 4 lock exclusive}   ;# Lock all aReadMark[]
          406  +lappend expected_locks {2 1 unlock exclusive} ;# Unlock recovery 
          407  +lappend expected_locks {4 4 unlock exclusive} ;# Unlock all aReadMark[] 
   406    408   lappend expected_locks {0 1 unlock exclusive} ;# Unlock writer
   407    409   lappend expected_locks {3 1 lock exclusive}   ;# Lock aReadMark[0]
   408    410   lappend expected_locks {3 1 unlock exclusive} ;# Unlock aReadMark[0]
   409    411   lappend expected_locks {1 1 unlock exclusive} ;# Unlock checkpoint
   410    412   do_test wal2-5.1 {
   411    413     proc tvfs_cb {method args} {
   412    414       set ::shm_file [lindex $args 0]
................................................................................
   625    627     testvfs tvfs
   626    628     tvfs script tvfs_cb
   627    629     sqlite3 db test.db -vfs tvfs
   628    630     set {} {}
   629    631   } {}
   630    632   
   631    633   set RECOVERY {
   632         -  {0 1 lock exclusive} {1 7 lock exclusive} 
   633         -  {1 7 unlock exclusive} {0 1 unlock exclusive}
          634  +  {0 1 lock exclusive} {1 2 lock exclusive} {4 4 lock exclusive}
          635  +  {1 2 unlock exclusive} {4 4 unlock exclusive} {0 1 unlock exclusive}
   634    636   }
   635    637   set READMARK0_READ {
   636    638     {3 1 lock shared} {3 1 unlock shared}
   637    639   }
   638    640   set READMARK0_WRITE {
   639    641     {3 1 lock shared} 
   640    642     {0 1 lock exclusive} {3 1 unlock shared} 
................................................................................
  1158   1160       forcecopy $shmpath proxysv_test.db-shm 
  1159   1161     }
  1160   1162     faultsim_save_and_close
  1161   1163   
  1162   1164     foreach {tn db_perm wal_perm shm_perm can_open can_read can_write} {
  1163   1165       2   00644   00644   00644   1   1   1
  1164   1166       3   00644   00400   00644   1   1   0
  1165         -    4   00644   00644   00400   1   0   0
         1167  +    4   00644   00644   00400   1   1   0
  1166   1168       5   00400   00644   00644   1   1   0
  1167   1169   
  1168   1170       7   00644   00000   00644   1   0   0
  1169   1171       8   00644   00644   00000   1   0   0
  1170   1172       9   00000   00644   00644   0   0   0
  1171   1173     } {
  1172   1174       faultsim_restore

Changes to test/walro.test.

   105    105     do_test 1.1.13  { sql2 "INSERT INTO t1 VALUES('i', 'j')" } {}
   106    106   
   107    107     do_test 1.2.1 {
   108    108       code2 { db2 close }
   109    109       code1 { db close }
   110    110       list [file exists test.db-wal] [file exists $shmpath]
   111    111     } {1 1}
          112  +
   112    113     do_test 1.2.2 {
   113    114       code1 { sqlite3 db file:test.db?readonly_shm=1 }
   114         -    sql1 { SELECT * FROM t1 }
   115         -  } {a b c d e f g h i j}
          115  +    list [catch { sql1 { SELECT * FROM t1 } } msg] $msg
          116  +  } {0 {a b c d e f g h i j}}
   116    117   
   117    118     do_test 1.2.3 {
   118    119       code1 { db close }
   119    120       file attributes $shmpath -permissions rw-r--r--
   120    121       hexio_write $shmpath 0 01020304
   121    122       file attributes $shmpath -permissions r--r--r--
   122    123       code1 { sqlite3 db file:test.db?readonly_shm=1 }
   123    124       csql1 { SELECT * FROM t1 }
   124         -  } {1 {attempt to write a readonly database}}
          125  +  } {0 {a b c d e f g h i j}}
   125    126     do_test 1.2.4 {
   126    127       code1 { sqlite3_extended_errcode db } 
   127         -  } {SQLITE_READONLY_RECOVERY}
          128  +  } {SQLITE_OK}
   128    129   
   129    130     do_test 1.2.5 {
   130    131       file attributes $shmpath -permissions rw-r--r--
   131    132       code2 { sqlite3 db2 test.db }
   132    133       sql2 "SELECT * FROM t1" 
   133    134     } {a b c d e f g h i j}
   134    135     file attributes $shmpath -permissions r--r--r--
................................................................................
   142    143       set {} {}
   143    144     } {}
   144    145     do_test 1.2.8 { sql1 "SELECT * FROM t1" } {a b c d e f g h i j k l}
   145    146   
   146    147     # Now check that if the readonly_shm option is not supplied, or if it
   147    148     # is set to zero, it is not possible to connect to the database without
   148    149     # read-write access to the shm.
          150  +  # 
          151  +  # UPDATE: os_unix.c now opens the *-shm file in readonly mode 
          152  +  # automatically.
          153  +  #
   149    154     do_test 1.3.1 {
   150    155       code1 { db close }
   151    156       code1 { sqlite3 db test.db }
   152    157       csql1 { SELECT * FROM t1 }
   153         -  } {1 {unable to open database file}}
          158  +  } {0 {a b c d e f g h i j k l}}
   154    159   
   155    160     # Also test that if the -shm file can be opened for read/write access,
   156    161     # it is not if readonly_shm=1 is present in the URI.
   157    162     do_test 1.3.2.1 {
   158    163       ifcapable enable_persist_wal {
   159    164         code1 { file_control_persist_wal db 0 }
   160    165         code2 { file_control_persist_wal db2 0 }
................................................................................
   169    174     } {1 {unable to open database file}}
   170    175     do_test 1.3.2.3 {
   171    176       code1 { db close }
   172    177       close [open $shmpath w]
   173    178       file attributes $shmpath -permissions r--r--r--
   174    179       code1 { sqlite3 db file:test.db?readonly_shm=1 }
   175    180       csql1 { SELECT * FROM t1 }
   176         -  } {1 {attempt to write a readonly database}}
          181  +  } {0 {a b c d e f g h i j k l}}
   177    182     do_test 1.3.2.4 {
   178    183       code1 { sqlite3_extended_errcode db } 
   179         -  } {SQLITE_READONLY_RECOVERY}
          184  +  } {SQLITE_OK}
   180    185   
   181    186     #-----------------------------------------------------------------------
   182    187     # Test cases 1.4.* check that checkpoints and log wraps don't prevent
   183    188     # read-only connections from reading the database.
   184    189     do_test 1.4.1 {
   185    190       code1 { db close }
   186    191       forcedelete test.db-shm

Added test/walro2.test.

            1  +# 2011 May 09
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +# This file contains tests for using WAL databases in read-only mode.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +source $testdir/lock_common.tcl
           18  +source $testdir/wal_common.tcl
           19  +set ::testprefix walro2
           20  +
           21  +# And only if the build is WAL-capable.
           22  +#
           23  +ifcapable !wal {
           24  +  finish_test
           25  +  return
           26  +}
           27  +
           28  +proc copy_to_test2 {bZeroShm} {
           29  +  forcecopy test.db test.db2
           30  +  forcecopy test.db-wal test.db2-wal
           31  +  if {$bZeroShm} {
           32  +    forcedelete test.db2-shm
           33  +    set fd [open test.db2-shm w]
           34  +    seek $fd [expr [file size test.db-shm]-1]
           35  +    puts -nonewline $fd "\0"
           36  +    close $fd
           37  +  } else {
           38  +    forcecopy test.db-shm test.db2-shm
           39  +  }
           40  +}
           41  +
           42  +foreach bZeroShm {0 1} {
           43  +set TN [expr $bZeroShm+1]
           44  +do_multiclient_test tn {
           45  +  
           46  +  # Close all connections and delete the database.
           47  +  #
           48  +  code1 { db close  }
           49  +  code2 { db2 close }
           50  +  code3 { db3 close }
           51  +  forcedelete test.db
           52  +  
           53  +  # Do not run tests with the connections in the same process.
           54  +  #
           55  +  if {$tn==2} continue
           56  +
           57  +  foreach c {code1 code2 code3} {
           58  +    $c {
           59  +      sqlite3_shutdown
           60  +      sqlite3_config_uri 1
           61  +    }
           62  +  }
           63  +
           64  +  do_test $TN.1.1 {
           65  +    code2 { sqlite3 db2 test.db }
           66  +    sql2 { 
           67  +      CREATE TABLE t1(x, y);
           68  +      PRAGMA journal_mode = WAL;
           69  +      INSERT INTO t1 VALUES('a', 'b');
           70  +      INSERT INTO t1 VALUES('c', 'd');
           71  +    }
           72  +    file exists test.db-shm
           73  +  } {1}
           74  +
           75  +  do_test $TN.1.2.1 {
           76  +    copy_to_test2 $bZeroShm
           77  +    code1 {
           78  +      sqlite3 db file:test.db2?readonly_shm=1
           79  +    }
           80  +
           81  +    sql1 { SELECT * FROM t1 }
           82  +  } {a b c d}
           83  +  do_test $TN.1.2.2 {
           84  +    sql1 { SELECT * FROM t1 }
           85  +  } {a b c d}
           86  +
           87  +  do_test $TN.1.3.1 {
           88  +    code3 { sqlite3 db3 test.db2 }
           89  +    sql3 { SELECT * FROM t1 }
           90  +  } {a b c d}
           91  +
           92  +  do_test $TN.1.3.2 {
           93  +    sql1 { SELECT * FROM t1 }
           94  +  } {a b c d}
           95  +
           96  +  code1 { db close  }
           97  +  code2 { db2 close }
           98  +  code3 { db3 close }
           99  +
          100  +  do_test $TN.2.1 {
          101  +    code2 { sqlite3 db2 test.db }
          102  +    sql2 { 
          103  +      INSERT INTO t1 VALUES('e', 'f');
          104  +      INSERT INTO t1 VALUES('g', 'h');
          105  +    }
          106  +    file exists test.db-shm
          107  +  } {1}
          108  +
          109  +  do_test $TN.2.2 {
          110  +    copy_to_test2 $bZeroShm
          111  +    code1 {
          112  +      sqlite3 db file:test.db2?readonly_shm=1
          113  +    }
          114  +    sql1 { 
          115  +      BEGIN;
          116  +      SELECT * FROM t1;
          117  +    }
          118  +  } {a b c d e f g h}
          119  +
          120  +  do_test $TN.2.3.1 {
          121  +    code3 { sqlite3 db3 test.db2 }
          122  +    sql3 { SELECT * FROM t1 }
          123  +  } {a b c d e f g h}
          124  +  do_test $TN.2.3.2 {
          125  +    sql3 { INSERT INTO t1 VALUES('i', 'j') }
          126  +    code3 { db3 close }
          127  +    sql1 { COMMIT } 
          128  +  } {}
          129  +  do_test $TN.2.3.3 {
          130  +    sql1 { SELECT * FROM t1 }
          131  +  } {a b c d e f g h i j}
          132  +
          133  +
          134  +  #-----------------------------------------------------------------------
          135  +  # 3.1.*: That a readonly_shm connection can read a database file if both
          136  +  #        the *-wal and *-shm files are zero bytes in size.
          137  +  #
          138  +  # 3.2.*: That it flushes the cache if, between transactions on a db with a
          139  +  #        zero byte *-wal file, some other connection modifies the db, then
          140  +  #        does "PRAGMA wal_checkpoint=truncate" to truncate the wal file
          141  +  #        back to zero bytes in size.
          142  +  #
          143  +  # 3.3.*: That, if between transactions some other process wraps the wal
          144  +  #        file, the readonly_shm client reruns recovery.
          145  +  #
          146  +  catch { code1 { db close } }
          147  +  catch { code2 { db2 close } }
          148  +  catch { code3 { db3 close } }
          149  +  do_test $TN.3.1.0 {
          150  +    list [file exists test.db-wal] [file exists test.db-shm]
          151  +  } {0 0}
          152  +  do_test $TN.3.1.1 {
          153  +    close [open test.db-wal w]
          154  +    close [open test.db-shm w]
          155  +    code1 {
          156  +      sqlite3 db file:test.db?readonly_shm=1
          157  +    }
          158  +    sql1 { SELECT * FROM t1 }
          159  +  } {a b c d e f g h}
          160  +
          161  +  do_test $TN.3.2.0 {
          162  +    list [file size test.db-wal] [file size test.db-shm]
          163  +  } {0 0}
          164  +  do_test $TN.3.2.1 {
          165  +    code2 { sqlite3 db2 test.db }
          166  +    sql2 { INSERT INTO t1 VALUES(1, 2) ; PRAGMA wal_checkpoint=truncate }
          167  +    code2 { db2 close }
          168  +    sql1 { SELECT * FROM t1 }
          169  +  } {a b c d e f g h 1 2}
          170  +  do_test $TN.3.2.2 {
          171  +    list [file size test.db-wal] [file size test.db-shm]
          172  +  } {0 32768}
          173  +
          174  +  do_test $TN.3.3.0 {
          175  +    code2 { sqlite3 db2 test.db }
          176  +    sql2 { 
          177  +      INSERT INTO t1 VALUES(3, 4);
          178  +      INSERT INTO t1 VALUES(5, 6);
          179  +      INSERT INTO t1 VALUES(7, 8);
          180  +      INSERT INTO t1 VALUES(9, 10);
          181  +    }
          182  +    code2 { db2 close }
          183  +    code1 { db close }
          184  +    list [file size test.db-wal] [file size test.db-shm]
          185  +  } [list [wal_file_size 4 1024] 32768]
          186  +  do_test $TN.3.3.1 {
          187  +    code1 { sqlite3 db file:test.db?readonly_shm=1 }
          188  +    sql1 { SELECT * FROM t1 }
          189  +  } {a b c d e f g h 1 2 3 4 5 6 7 8 9 10}
          190  +  do_test $TN.3.3.2 {
          191  +    code2 { sqlite3 db2 test.db }
          192  +    sql2 { 
          193  +      PRAGMA wal_checkpoint; 
          194  +      DELETE FROM t1;
          195  +      INSERT INTO t1 VALUES('i', 'ii');
          196  +    }
          197  +    code2 { db2 close }
          198  +    list [file size test.db-wal] [file size test.db-shm]
          199  +  } [list [wal_file_size 4 1024] 32768]
          200  +  do_test $TN.3.3.3 {
          201  +    sql1 { SELECT * FROM t1 }
          202  +  } {i ii}
          203  +
          204  +  #-----------------------------------------------------------------------
          205  +  #
          206  +  #
          207  +  catch { code1 { db close } }
          208  +  catch { code2 { db2 close } }
          209  +  catch { code3 { db3 close } }
          210  +
          211  +  do_test $TN.4.0 {
          212  +    code1 { forcedelete test.db }
          213  +    code1 { sqlite3 db test.db }
          214  +    sql1 {
          215  +      PRAGMA journal_mode = wal;
          216  +      CREATE TABLE t1(x);
          217  +      INSERT INTO t1 VALUES('hello');
          218  +      INSERT INTO t1 VALUES('world');
          219  +    }
          220  +
          221  +    copy_to_test2 $bZeroShm
          222  +
          223  +    code1 { db close }
          224  +  } {}
          225  +
          226  +  do_test $TN.4.1.1 {
          227  +    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
          228  +    sql2 { SELECT * FROM t1 }
          229  +  } {hello world}
          230  +
          231  +  do_test $TN.4.1.2 {
          232  +    code3 { sqlite3 db3 test.db2 }
          233  +    sql3 {
          234  +      INSERT INTO t1 VALUES('!');
          235  +      PRAGMA wal_checkpoint = truncate;
          236  +    }
          237  +    code3 { db3 close }
          238  +  } {}
          239  +  do_test $TN.4.1.3 {
          240  +    sql2 { SELECT * FROM t1 }
          241  +  } {hello world !}
          242  +
          243  +  catch { code1 { db close } }
          244  +  catch { code2 { db2 close } }
          245  +  catch { code3 { db3 close } }
          246  +
          247  +  do_test $TN.4.2.1 {
          248  +    code1 { sqlite3 db test.db }
          249  +    sql1 {
          250  +      INSERT INTO t1 VALUES('!');
          251  +      INSERT INTO t1 VALUES('!');
          252  +
          253  +      PRAGMA cache_size = 10;
          254  +      CREATE TABLE t2(x);
          255  +
          256  +      BEGIN;
          257  +        WITH s(i) AS (
          258  +          SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500
          259  +          )
          260  +        INSERT INTO t2 SELECT randomblob(500) FROM s;
          261  +        SELECT count(*) FROM t2;
          262  +    } 
          263  +  } {500}
          264  +  do_test $TN.4.2.2 {
          265  +    file size test.db-wal
          266  +  } {461152}
          267  +  do_test $TN.4.2.4 {
          268  +    file_control_persist_wal db 1; db close
          269  +
          270  +    copy_to_test2 $bZeroShm
          271  +    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
          272  +    sql2 {
          273  +      SELECT * FROM t1;
          274  +      SELECT count(*) FROM t2;
          275  +    }
          276  +  } {hello world ! ! 0}
          277  +
          278  +  #-----------------------------------------------------------------------
          279  +  #
          280  +  #
          281  +  catch { code1 { db close } }
          282  +  catch { code2 { db2 close } }
          283  +  catch { code3 { db3 close } }
          284  +
          285  +  do_test $TN.5.0 {
          286  +    code1 { forcedelete test.db }
          287  +    code1 { sqlite3 db test.db }
          288  +    sql1 {
          289  +      PRAGMA journal_mode = wal;
          290  +      CREATE TABLE t1(x);
          291  +      INSERT INTO t1 VALUES('hello');
          292  +      INSERT INTO t1 VALUES('world');
          293  +      INSERT INTO t1 VALUES('!');
          294  +      INSERT INTO t1 VALUES('world');
          295  +      INSERT INTO t1 VALUES('hello');
          296  +    }
          297  +
          298  +    copy_to_test2 $bZeroShm
          299  +    
          300  +    code1 { db close }
          301  +  } {}
          302  +
          303  +  do_test $TN.5.1 {
          304  +    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
          305  +    sql2 {
          306  +      SELECT * FROM t1;
          307  +    }
          308  +  } {hello world ! world hello}
          309  +
          310  +  do_test $TN.5.2 {
          311  +    code1 {
          312  +      proc handle_read {op args} {
          313  +        if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
          314  +          set ::res2 [sql2 { SELECT * FROM t1 }]
          315  +        }
          316  +        puts "$msg xRead $args"
          317  +        return "SQLITE_OK"
          318  +      }
          319  +      testvfs tvfs -fullshm 1
          320  +
          321  +      sqlite3 db file:test.db2?vfs=tvfs
          322  +      db eval { SELECT * FROM sqlite_master }
          323  +
          324  +      tvfs filter xRead
          325  +      tvfs script handle_read
          326  +    }
          327  +    sql1 {
          328  +      PRAGMA wal_checkpoint = truncate;
          329  +    }
          330  +    code1 { set ::res2 }
          331  +  } {hello world ! world hello}
          332  +
          333  +  do_test $TN.5.3 {
          334  +    code1 { db close }
          335  +    code1 { tvfs delete }
          336  +  } {}
          337  +
          338  +  #-----------------------------------------------------------------------
          339  +  #
          340  +  #
          341  +  catch { code1 { db close } }
          342  +  catch { code2 { db2 close } }
          343  +  catch { code3 { db3 close } }
          344  +
          345  +  do_test $TN.6.1 {
          346  +    code1 { forcedelete test.db }
          347  +    code1 { sqlite3 db test.db }
          348  +    sql1 {
          349  +      PRAGMA journal_mode = wal;
          350  +      CREATE TABLE t1(x);
          351  +      INSERT INTO t1 VALUES('hello');
          352  +      INSERT INTO t1 VALUES('world');
          353  +      INSERT INTO t1 VALUES('!');
          354  +      INSERT INTO t1 VALUES('world');
          355  +      INSERT INTO t1 VALUES('hello');
          356  +    }
          357  +
          358  +    copy_to_test2 $bZeroShm
          359  +    
          360  +    code1 { db close }
          361  +  } {}
          362  +
          363  +  do_test $TN.6.2 {
          364  +    code1 {
          365  +      set ::nRem 5
          366  +      proc handle_read {op args} {
          367  +        if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
          368  +          incr ::nRem -1
          369  +          if {$::nRem==0} {
          370  +            code2 { sqlite3 db2 test.db2 }
          371  +            sql2  { PRAGMA wal_checkpoint = truncate }
          372  +          }
          373  +        }
          374  +        return "SQLITE_OK"
          375  +      }
          376  +      testvfs tvfs -fullshm 1
          377  +
          378  +      tvfs filter xRead
          379  +      tvfs script handle_read
          380  +
          381  +      sqlite3 db file:test.db2?readonly_shm=1&vfs=tvfs
          382  +      db eval { SELECT * FROM t1 }
          383  +    }
          384  +  } {hello world ! world hello}
          385  +
          386  +  do_test $TN.6.3 {
          387  +    code1 { db close }
          388  +    code1 { tvfs delete }
          389  +  } {}
          390  +}
          391  +} ;# foreach bZeroShm
          392  +
          393  +finish_test

Added test/walrofault.test.

            1  +# 2011 May 09
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +# This file contains tests for using WAL databases in read-only mode.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +source $testdir/malloc_common.tcl
           18  +set ::testprefix walro2
           19  +
           20  +# And only if the build is WAL-capable.
           21  +#
           22  +ifcapable !wal {
           23  +  finish_test
           24  +  return
           25  +}
           26  +
           27  +db close
           28  +sqlite3_shutdown
           29  +sqlite3_config_uri 1
           30  +sqlite3 db test.db
           31  +
           32  +do_execsql_test 1.0 {
           33  +  CREATE TABLE t1(b);
           34  +  PRAGMA journal_mode = wal;
           35  +  INSERT INTO t1 VALUES('hello');
           36  +  INSERT INTO t1 VALUES('world');
           37  +  INSERT INTO t1 VALUES('!');
           38  +  INSERT INTO t1 VALUES('world');
           39  +  INSERT INTO t1 VALUES('hello');
           40  +  PRAGMA cache_size = 10;
           41  +  BEGIN;
           42  +    WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<30 ) 
           43  +    INSERT INTO t1(b) SELECT randomblob(800) FROM s;
           44  +} {wal}
           45  +file_control_persist_wal db 1; db close
           46  +faultsim_save_and_close
           47  +
           48  +do_faultsim_test 1 -faults oom* -prep {
           49  +  catch { db close }
           50  +  faultsim_restore
           51  +  sqlite3 db file:test.db?readonly_shm=1
           52  +} -body {
           53  +  execsql { SELECT * FROM t1 }
           54  +} -test {
           55  +  faultsim_test_result {0 {hello world ! world hello}}
           56  +}
           57  +
           58  +
           59  +
           60  +finish_test