/ Check-in [671ba5fc]
Login

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

Overview
Comment:Only open a read-only connection to shared-memory if the "readonly_shm=1" option is specified as part of the database file URI (and if a read-write connection fails).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wal-readonly
Files: files | file ages | folders
SHA1: 671ba5fc59f7a958e5a4138d2425b1173a442ad7
User & Date: dan 2011-05-11 14:57:33
Context
2011-05-11
15:53
Merge latest trunk changes. Add a couple of readonly shm tests. check-in: cde45a03 user: dan tags: wal-readonly
14:57
Only open a read-only connection to shared-memory if the "readonly_shm=1" option is specified as part of the database file URI (and if a read-write connection fails). check-in: 671ba5fc user: dan tags: wal-readonly
2011-05-10
17:31
Add experimental support for read-only connections to WAL databases. check-in: bb59f986 user: dan tags: wal-readonly
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/attach.c.

    72     72     const char *zFile;
    73     73     char *zPath = 0;
    74     74     char *zErr = 0;
    75     75     unsigned int flags;
    76     76     Db *aNew;
    77     77     char *zErrDyn = 0;
    78     78     sqlite3_vfs *pVfs;
           79  +  const char *zVfs = db->pVfs->zName;       /* Name of default (main) VFS */
           80  +  int btflags = 0;
    79     81   
    80     82     UNUSED_PARAMETER(NotUsed);
    81     83   
    82     84     zFile = (const char *)sqlite3_value_text(argv[0]);
    83     85     zName = (const char *)sqlite3_value_text(argv[1]);
    84     86     if( zFile==0 ) zFile = "";
    85     87     if( zName==0 ) zName = "";
................................................................................
   125    127     memset(aNew, 0, sizeof(*aNew));
   126    128   
   127    129     /* Open the database file. If the btree is successfully opened, use
   128    130     ** it to obtain the database schema. At this point the schema may
   129    131     ** or may not be initialised.
   130    132     */
   131    133     flags = db->openFlags;
   132         -  rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
          134  +  rc = sqlite3ParseUri(zVfs, zFile, &flags, &btflags, &pVfs, &zPath, &zErr);
   133    135     if( rc!=SQLITE_OK ){
   134    136       if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
   135    137       sqlite3_result_error(context, zErr, -1);
   136    138       sqlite3_free(zErr);
   137    139       return;
   138    140     }
   139    141     assert( pVfs );
   140    142     flags |= SQLITE_OPEN_MAIN_DB;
   141         -  rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags);
          143  +  rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, btflags, flags);
   142    144     sqlite3_free( zPath );
   143    145     db->nDb++;
   144    146     if( rc==SQLITE_CONSTRAINT ){
   145    147       rc = SQLITE_ERROR;
   146    148       zErrDyn = sqlite3MPrintf(db, "database is already attached");
   147    149     }else if( rc==SQLITE_OK ){
   148    150       Pager *pPager;

Changes to src/btree.h.

    57     57   ** pager.h.
    58     58   */
    59     59   #define BTREE_OMIT_JOURNAL  1  /* Do not create or use a rollback journal */
    60     60   #define BTREE_NO_READLOCK   2  /* Omit readlocks on readonly files */
    61     61   #define BTREE_MEMORY        4  /* This is an in-memory DB */
    62     62   #define BTREE_SINGLE        8  /* The file contains at most 1 b-tree */
    63     63   #define BTREE_UNORDERED    16  /* Use of a hash implementation is OK */
           64  +#define BTREE_READONLYSHM  32  /* Read-only SHM access is acceptable */
    64     65   
    65     66   int sqlite3BtreeClose(Btree*);
    66     67   int sqlite3BtreeSetCacheSize(Btree*,int);
    67     68   int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
    68     69   int sqlite3BtreeSyncDisabled(Btree*);
    69     70   int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
    70     71   int sqlite3BtreeGetPageSize(Btree*);

Changes to src/main.c.

  1814   1814   ** message. It is the responsibility of the caller to eventually release
  1815   1815   ** this buffer by calling sqlite3_free().
  1816   1816   */
  1817   1817   int sqlite3ParseUri(
  1818   1818     const char *zDefaultVfs,        /* VFS to use if no "vfs=xxx" query option */
  1819   1819     const char *zUri,               /* Nul-terminated URI to parse */
  1820   1820     unsigned int *pFlags,           /* IN/OUT: SQLITE_OPEN_XXX flags */
         1821  +  int *pBtflags,                  /* IN/OUT: BTREE_XXX flags */
  1821   1822     sqlite3_vfs **ppVfs,            /* OUT: VFS to use */ 
  1822   1823     char **pzFile,                  /* OUT: Filename component of URI */
  1823   1824     char **pzErrMsg                 /* OUT: Error message (if rc!=SQLITE_OK) */
  1824   1825   ){
  1825   1826     int rc = SQLITE_OK;
  1826   1827     unsigned int flags = *pFlags;
  1827   1828     const char *zVfs = zDefaultVfs;
................................................................................
  1929   1930       while( zOpt[0] ){
  1930   1931         int nOpt = sqlite3Strlen30(zOpt);
  1931   1932         char *zVal = &zOpt[nOpt+1];
  1932   1933         int nVal = sqlite3Strlen30(zVal);
  1933   1934   
  1934   1935         if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){
  1935   1936           zVfs = zVal;
         1937  +      }else if( nOpt==12 && memcmp("readonly_shm", zOpt, 12)==0 ){
         1938  +        if( sqlite3Atoi(zVal) ){
         1939  +          *pBtflags |= BTREE_READONLYSHM;
         1940  +        }else{
         1941  +          *pBtflags &= ~BTREE_READONLYSHM;
         1942  +        }
  1936   1943         }else{
  1937   1944           struct OpenMode {
  1938   1945             const char *z;
  1939   1946             int mode;
  1940   1947           } *aMode = 0;
  1941   1948           char *zModeType;
  1942   1949           int mask;
................................................................................
  2032   2039     const char *zVfs       /* Name of the VFS to use */
  2033   2040   ){
  2034   2041     sqlite3 *db;                    /* Store allocated handle here */
  2035   2042     int rc;                         /* Return code */
  2036   2043     int isThreadsafe;               /* True for threadsafe connections */
  2037   2044     char *zOpen = 0;                /* Filename argument to pass to BtreeOpen() */
  2038   2045     char *zErrMsg = 0;              /* Error message from sqlite3ParseUri() */
         2046  +  int btflags = 0;                /* Mask of BTREE_XXX flags */
  2039   2047   
  2040   2048     *ppDb = 0;
  2041   2049   #ifndef SQLITE_OMIT_AUTOINIT
  2042   2050     rc = sqlite3_initialize();
  2043   2051     if( rc ) return rc;
  2044   2052   #endif
  2045   2053   
................................................................................
  2159   2167     assert( db->pDfltColl!=0 );
  2160   2168   
  2161   2169     /* Also add a UTF-8 case-insensitive collation sequence. */
  2162   2170     createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0,
  2163   2171                     nocaseCollatingFunc, 0);
  2164   2172   
  2165   2173     /* Parse the filename/URI argument. */
  2166         -  rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
         2174  +  rc = sqlite3ParseUri(
         2175  +      zVfs, zFilename, &flags, &btflags, &db->pVfs, &zOpen, &zErrMsg);
  2167   2176     if( rc!=SQLITE_OK ){
  2168   2177       if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
  2169   2178       sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
  2170   2179       sqlite3_free(zErrMsg);
  2171   2180       goto opendb_out;
  2172   2181     }
  2173   2182   
  2174   2183     /* Open the backend database driver */
  2175   2184     db->openFlags = flags;
  2176         -  rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0,
         2185  +  rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, btflags,
  2177   2186                           flags | SQLITE_OPEN_MAIN_DB);
  2178   2187     if( rc!=SQLITE_OK ){
  2179   2188       if( rc==SQLITE_IOERR_NOMEM ){
  2180   2189         rc = SQLITE_NOMEM;
  2181   2190       }
  2182   2191       sqlite3Error(db, rc, 0);
  2183   2192       goto opendb_out;

Changes to src/pager.c.

   616    616     u8 noSync;                  /* Do not sync the journal if true */
   617    617     u8 fullSync;                /* Do extra syncs of the journal for robustness */
   618    618     u8 ckptSyncFlags;           /* SYNC_NORMAL or SYNC_FULL for checkpoint */
   619    619     u8 syncFlags;               /* SYNC_NORMAL or SYNC_FULL otherwise */
   620    620     u8 tempFile;                /* zFilename is a temporary file */
   621    621     u8 readOnly;                /* True for a read-only database */
   622    622     u8 memDb;                   /* True to inhibit all file I/O */
          623  +  u8 readOnlyShm;             /* True if read-only shm access is Ok */
   623    624   
   624    625     /**************************************************************************
   625    626     ** The following block contains those class members that change during
   626    627     ** routine opertion.  Class members not in this block are either fixed
   627    628     ** when the pager is first created or else only change when there is a
   628    629     ** significant mode change (such as changing the page_size, locking_mode,
   629    630     ** or the journal_mode).  From another view, these class members describe
................................................................................
  3013   3014   ** makes a snapshot of the database at the current point in time and preserves
  3014   3015   ** that snapshot for use by the reader in spite of concurrently changes by
  3015   3016   ** other writers or checkpointers.
  3016   3017   */
  3017   3018   static int pagerBeginReadTransaction(Pager *pPager){
  3018   3019     int rc;                         /* Return code */
  3019   3020     int changed = 0;                /* True if cache must be reset */
         3021  +  Wal *pWal = pPager->pWal;
  3020   3022   
  3021   3023     assert( pagerUseWal(pPager) );
  3022   3024     assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
  3023   3025   
  3024   3026     /* sqlite3WalEndReadTransaction() was not called for the previous
  3025   3027     ** transaction in locking_mode=EXCLUSIVE.  So call it now.  If we
  3026   3028     ** are in locking_mode=NORMAL and EndRead() was previously called,
  3027   3029     ** the duplicate call is harmless.
  3028   3030     */
  3029         -  sqlite3WalEndReadTransaction(pPager->pWal);
         3031  +  sqlite3WalEndReadTransaction(pWal);
  3030   3032   
  3031         -  rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
         3033  +  rc = sqlite3WalBeginReadTransaction(pWal, pPager->readOnlyShm, &changed);
  3032   3034     if( rc!=SQLITE_OK || changed ){
  3033   3035       pager_reset(pPager);
  3034   3036     }
  3035   3037   
  3036   3038     return rc;
  3037   3039   }
  3038   3040   #endif
................................................................................
  4410   4412       memcpy(pPager->zWal, zPathname, nPathname);
  4411   4413       memcpy(&pPager->zWal[nPathname], "-wal", 4);
  4412   4414   #endif
  4413   4415       sqlite3_free(zPathname);
  4414   4416     }
  4415   4417     pPager->pVfs = pVfs;
  4416   4418     pPager->vfsFlags = vfsFlags;
         4419  +  pPager->readOnlyShm = (flags & PAGER_READONLYSHM)!=0;
  4417   4420   
  4418   4421     /* Open the pager file.
  4419   4422     */
  4420   4423     if( zFilename && zFilename[0] ){
  4421   4424       int fout = 0;                    /* VFS flags returned by xOpen() */
  4422   4425       rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout);
  4423   4426       assert( !memDb );

Changes to src/pager.h.

    56     56   ** Allowed values for the flags parameter to sqlite3PagerOpen().
    57     57   **
    58     58   ** NOTE: These values must match the corresponding BTREE_ values in btree.h.
    59     59   */
    60     60   #define PAGER_OMIT_JOURNAL  0x0001    /* Do not use a rollback journal */
    61     61   #define PAGER_NO_READLOCK   0x0002    /* Omit readlocks on readonly files */
    62     62   #define PAGER_MEMORY        0x0004    /* In-memory database */
           63  +#define PAGER_READONLYSHM   0x0020    /* Read-only SHM access is acceptable */
    63     64   
    64     65   /*
    65     66   ** Valid values for the second argument to sqlite3PagerLockingMode().
    66     67   */
    67     68   #define PAGER_LOCKINGMODE_QUERY      -1
    68     69   #define PAGER_LOCKINGMODE_NORMAL      0
    69     70   #define PAGER_LOCKINGMODE_EXCLUSIVE   1

Changes to src/sqliteInt.h.

  2669   2669   void sqlite3AddNotNull(Parse*, int);
  2670   2670   void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
  2671   2671   void sqlite3AddCheckConstraint(Parse*, Expr*);
  2672   2672   void sqlite3AddColumnType(Parse*,Token*);
  2673   2673   void sqlite3AddDefaultValue(Parse*,ExprSpan*);
  2674   2674   void sqlite3AddCollateType(Parse*, Token*);
  2675   2675   void sqlite3EndTable(Parse*,Token*,Token*,Select*);
  2676         -int sqlite3ParseUri(const char*,const char*,unsigned int*,
         2676  +int sqlite3ParseUri(const char*,const char*,unsigned int*,int*,
  2677   2677                       sqlite3_vfs**,char**,char **);
  2678   2678   
  2679   2679   Bitvec *sqlite3BitvecCreate(u32);
  2680   2680   int sqlite3BitvecTest(Bitvec*, u32);
  2681   2681   int sqlite3BitvecSet(Bitvec*, u32);
  2682   2682   void sqlite3BitvecClear(Bitvec*, u32, void*);
  2683   2683   void sqlite3BitvecDestroy(Bitvec*);

Changes to src/wal.c.

   525    525       if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
   526    526         pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
   527    527         if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
   528    528       }else{
   529    529         rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, 
   530    530             pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
   531    531         );
   532         -      if( rc==SQLITE_CANTOPEN && iPage==0 ){
          532  +      if( rc==SQLITE_CANTOPEN && pWal->readOnlyShm>1 ){
          533  +        assert( iPage==0 );
   533    534           sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_READONLY_SHM, (void*)1);
   534    535           rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, 
   535    536               pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
   536    537           );
   537    538           if( rc==SQLITE_OK ){
   538    539             pWal->readOnly = pWal->readOnlyShm = 1;
   539    540           }
   540    541           sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_READONLY_SHM, (void*)0);
   541    542         }
   542    543       }
   543    544     }
   544         -
   545    545     *ppPage = pWal->apWiData[iPage];
          546  +
   546    547     assert( iPage==0 || *ppPage || rc!=SQLITE_OK );
          548  +  if( pWal->readOnlyShm>1 ) pWal->readOnlyShm = 0;
   547    549     return rc;
   548    550   }
   549    551   
   550    552   /*
   551    553   ** Return a pointer to the WalCkptInfo structure in the wal-index.
   552    554   */
   553    555   static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
................................................................................
  1905   1907     */
  1906   1908     assert( pChanged );
  1907   1909     rc = walIndexPage(pWal, 0, &page0);
  1908   1910     if( rc!=SQLITE_OK ){
  1909   1911       return rc;
  1910   1912     };
  1911   1913     assert( page0 || pWal->writeLock==0 );
         1914  +  assert( pWal->readOnlyShm==0 || pWal->readOnlyShm==1 );
  1912   1915   
  1913   1916     /* If the first page of the wal-index has been mapped, try to read the
  1914   1917     ** wal-index header immediately, without holding any lock. This usually
  1915   1918     ** works, but may fail if the wal-index header is corrupt or currently 
  1916   1919     ** being modified by another thread or process.
  1917   1920     */
  1918   1921     badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
................................................................................
  2196   2199   ** that extra content is ignored by the current thread.
  2197   2200   **
  2198   2201   ** If the database contents have changes since the previous read
  2199   2202   ** transaction, then *pChanged is set to 1 before returning.  The
  2200   2203   ** Pager layer will use this to know that is cache is stale and
  2201   2204   ** needs to be flushed.
  2202   2205   */
  2203         -int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
         2206  +int sqlite3WalBeginReadTransaction(Wal *pWal, int readOnlyShm, int *pChanged){
  2204   2207     int rc;                         /* Return code */
  2205   2208     int cnt = 0;                    /* Number of TryBeginRead attempts */
  2206   2209   
         2210  +  if( pWal->nWiData==0 || pWal->apWiData[0]==0 ){
         2211  +    assert( readOnlyShm==0 || readOnlyShm==1 );
         2212  +    pWal->readOnlyShm = readOnlyShm*2;
         2213  +  }
  2207   2214     do{
  2208   2215       rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
  2209   2216     }while( rc==WAL_RETRY );
         2217  +  assert( rc || pWal->readOnlyShm==0 || (readOnlyShm && pWal->readOnlyShm==1) );
  2210   2218     testcase( (rc&0xff)==SQLITE_BUSY );
  2211   2219     testcase( (rc&0xff)==SQLITE_IOERR );
  2212   2220     testcase( rc==SQLITE_PROTOCOL );
  2213   2221     testcase( rc==SQLITE_OK );
  2214   2222     return rc;
  2215   2223   }
  2216   2224   

Changes to src/wal.h.

    18     18   #define _WAL_H_
    19     19   
    20     20   #include "sqliteInt.h"
    21     21   
    22     22   #ifdef SQLITE_OMIT_WAL
    23     23   # define sqlite3WalOpen(x,y,z)                   0
    24     24   # define sqlite3WalClose(w,x,y,z)                0
    25         -# define sqlite3WalBeginReadTransaction(y,z)     0
           25  +# define sqlite3WalBeginReadTransaction(x,y,z)   0
    26     26   # define sqlite3WalEndReadTransaction(z)
    27     27   # define sqlite3WalRead(v,w,x,y,z)               0
    28     28   # define sqlite3WalDbsize(y)                     0
    29     29   # define sqlite3WalBeginWriteTransaction(y)      0
    30     30   # define sqlite3WalEndWriteTransaction(x)        0
    31     31   # define sqlite3WalUndo(x,y,z)                   0
    32     32   # define sqlite3WalSavepoint(y,z)
................................................................................
    52     52   /* Used by readers to open (lock) and close (unlock) a snapshot.  A 
    53     53   ** snapshot is like a read-transaction.  It is the state of the database
    54     54   ** at an instant in time.  sqlite3WalOpenSnapshot gets a read lock and
    55     55   ** preserves the current state even if the other threads or processes
    56     56   ** write to or checkpoint the WAL.  sqlite3WalCloseSnapshot() closes the
    57     57   ** transaction and releases the lock.
    58     58   */
    59         -int sqlite3WalBeginReadTransaction(Wal *pWal, int *);
           59  +int sqlite3WalBeginReadTransaction(Wal *pWal, int, int *);
    60     60   void sqlite3WalEndReadTransaction(Wal *pWal);
    61     61   
    62     62   /* Read a page from the write-ahead log, if it is present. */
    63     63   int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);
    64     64   
    65     65   /* If the WAL is not empty, return the size of the database. */
    66     66   Pgno sqlite3WalDbsize(Wal *pWal);

Changes to test/walro.test.

    30     30     # Close all connections and delete the database.
    31     31     #
    32     32     code1 { db close  }
    33     33     code2 { db2 close }
    34     34     code3 { db3 close }
    35     35     forcedelete test.db
    36     36     forcedelete walro
           37  +
           38  +  foreach c {code1 code2 code3} {
           39  +    $c {
           40  +      sqlite3_shutdown
           41  +      sqlite3_config_uri 1
           42  +    }
           43  +  }
    37     44   
    38     45     file mkdir walro
    39     46   
    40     47     do_test 1.1.1 {
    41     48       code2 { sqlite3 db2 test.db }
    42     49       sql2 { 
    43     50         PRAGMA journal_mode = WAL;
................................................................................
    45     52         INSERT INTO t1 VALUES('a', 'b');
    46     53       }
    47     54       file exists test.db-shm
    48     55     } {1}
    49     56   
    50     57     do_test 1.1.2 {
    51     58       file attributes test.db-shm -permissions r--r--r--
    52         -    code1 { sqlite3 db test.db }
           59  +    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    53     60     } {}
    54     61   
    55     62     do_test 1.1.3 { sql1 "SELECT * FROM t1" }                {a b}
    56     63     do_test 1.1.4 { sql2 "INSERT INTO t1 VALUES('c', 'd')" } {}
    57     64     do_test 1.1.5 { sql1 "SELECT * FROM t1" }                {a b c d}
    58     65   
    59     66     # Check that the read-only connection cannot write or checkpoint the db.
................................................................................
    80     87   
    81     88     do_test 1.2.1 {
    82     89       code2 { db2 close }
    83     90       code1 { db close }
    84     91       list [file exists test.db-wal] [file exists test.db-shm]
    85     92     } {1 1}
    86     93     do_test 1.2.2 {
    87         -    code1 { sqlite3 db test.db }
           94  +    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    88     95       sql1 { SELECT * FROM t1 }
    89     96     } {a b c d e f g h i j}
    90     97   
    91     98     do_test 1.2.3 {
    92     99       code1 { db close }
    93    100       file attributes test.db-shm -permissions rw-r--r--
    94    101       hexio_write test.db-shm 0 01020304 
    95    102       file attributes test.db-shm -permissions r--r--r--
    96         -    code1 { sqlite3 db test.db }
          103  +    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    97    104       csql1 { SELECT * FROM t1 }
    98    105     } {1 {attempt to write a readonly database}}
    99    106     do_test 1.2.4 {
   100    107       code1 { sqlite3_extended_errcode db } 
   101    108     } {SQLITE_READONLY_RECOVERY}
   102    109   
   103    110     do_test 1.2.5 {