/ Check-in [8dd5c691]
Login

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

Overview
Comment:If a database file with the WAL flag set is opened in exclusive-locking mode, use heap memory to store the wal-index instead of shared-memory.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8dd5c69198619866923c6053b71899c1fb8c4c67
User & Date: dan 2010-11-01 17:38:25
Context
2010-11-01
18:45
Add test cases to restore coverage of pager.c and wal.c. check-in: 6cae5529 user: dan tags: trunk
17:38
If a database file with the WAL flag set is opened in exclusive-locking mode, use heap memory to store the wal-index instead of shared-memory. check-in: 8dd5c691 user: dan tags: trunk
14:34
Change the version number to 3.7.4. check-in: db64843b user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/pager.c.

  1046   1046   ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
  1047   1047   ** called, do not modify it. See the comment above the #define of 
  1048   1048   ** UNKNOWN_LOCK for an explanation of this.
  1049   1049   */
  1050   1050   static int pagerUnlockDb(Pager *pPager, int eLock){
  1051   1051     int rc = SQLITE_OK;
  1052   1052   
  1053         -  assert( !pPager->exclusiveMode );
         1053  +  assert( !pPager->exclusiveMode || pPager->eLock==eLock );
  1054   1054     assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
  1055   1055     assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
  1056   1056     if( isOpen(pPager->fd) ){
  1057   1057       assert( pPager->eLock>=eLock );
  1058   1058       rc = sqlite3OsUnlock(pPager->fd, eLock);
  1059   1059       if( pPager->eLock!=UNKNOWN_LOCK ){
  1060   1060         pPager->eLock = (u8)eLock;
................................................................................
  6346   6346   */
  6347   6347   int sqlite3PagerLockingMode(Pager *pPager, int eMode){
  6348   6348     assert( eMode==PAGER_LOCKINGMODE_QUERY
  6349   6349               || eMode==PAGER_LOCKINGMODE_NORMAL
  6350   6350               || eMode==PAGER_LOCKINGMODE_EXCLUSIVE );
  6351   6351     assert( PAGER_LOCKINGMODE_QUERY<0 );
  6352   6352     assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 );
  6353         -  if( eMode>=0 && !pPager->tempFile ){
         6353  +  assert( pPager->exclusiveMode || 0==sqlite3WalHeapMemory(pPager->pWal) );
         6354  +  if( eMode>=0 && !pPager->tempFile && !sqlite3WalHeapMemory(pPager->pWal) ){
  6354   6355       pPager->exclusiveMode = (u8)eMode;
  6355   6356     }
  6356   6357     return (int)pPager->exclusiveMode;
  6357   6358   }
  6358   6359   
  6359   6360   /*
  6360   6361   ** Set the journal-mode for this pager. Parameter eMode must be one of:
................................................................................
  6533   6534   
  6534   6535   /*
  6535   6536   ** Return true if the underlying VFS for the given pager supports the
  6536   6537   ** primitives necessary for write-ahead logging.
  6537   6538   */
  6538   6539   int sqlite3PagerWalSupported(Pager *pPager){
  6539   6540     const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
  6540         -  return pMethods->iVersion>=2 && pMethods->xShmMap!=0;
         6541  +  return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
         6542  +}
         6543  +
         6544  +/*
         6545  +** Attempt to take an exclusive lock on the database file. If a PENDING lock
         6546  +** is obtained instead, immediately release it.
         6547  +*/
         6548  +static int pagerExclusiveLock(Pager *pPager){
         6549  +  int rc;                         /* Return code */
         6550  +
         6551  +  assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
         6552  +  rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
         6553  +  if( rc!=SQLITE_OK ){
         6554  +    /* If the attempt to grab the pending lock failed, release the 
         6555  +    ** exclusive lock that may have been obtained instead.  */
         6556  +    pagerUnlockDb(pPager, SHARED_LOCK);
         6557  +  }
         6558  +
         6559  +  return rc;
         6560  +}
         6561  +
         6562  +/*
         6563  +** Call sqlite3WalOpen() to open the WAL handle. If the pager is in 
         6564  +** exclusive-locking mode when this function is called, take an EXCLUSIVE
         6565  +** lock on the database file and use heap-memory to store the wal-index
         6566  +** in. Otherwise, use the normal shared-memory.
         6567  +*/
         6568  +static int pagerOpenWal(Pager *pPager){
         6569  +  int rc = SQLITE_OK;
         6570  +
         6571  +  assert( pPager->pWal==0 && pPager->tempFile==0 );
         6572  +  assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
         6573  +
         6574  +  /* If the pager is already in exclusive-mode, the WAL module will use 
         6575  +  ** heap-memory for the wal-index instead of the VFS shared-memory 
         6576  +  ** implementation. Take the exclusive lock now, before opening the WAL
         6577  +  ** file, to make sure this is safe.
         6578  +  */
         6579  +  if( pPager->exclusiveMode ){
         6580  +    rc = pagerExclusiveLock(pPager);
         6581  +  }
         6582  +
         6583  +  /* Open the connection to the log file. If this operation fails, 
         6584  +  ** (e.g. due to malloc() failure), return an error code.
         6585  +  */
         6586  +  if( rc==SQLITE_OK ){
         6587  +    rc = sqlite3WalOpen(pPager->pVfs, 
         6588  +        pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal
         6589  +    );
         6590  +  }
         6591  +
         6592  +  return rc;
  6541   6593   }
         6594  +
  6542   6595   
  6543   6596   /*
  6544   6597   ** The caller must be holding a SHARED lock on the database file to call
  6545   6598   ** this function.
  6546   6599   **
  6547   6600   ** If the pager passed as the first argument is open on a real database
  6548   6601   ** file (not a temp file or an in-memory database), and the WAL file
................................................................................
  6569   6622   
  6570   6623     if( !pPager->tempFile && !pPager->pWal ){
  6571   6624       if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN;
  6572   6625   
  6573   6626       /* Close any rollback journal previously open */
  6574   6627       sqlite3OsClose(pPager->jfd);
  6575   6628   
  6576         -    /* Open the connection to the log file. If this operation fails, 
  6577         -    ** (e.g. due to malloc() failure), unlock the database file and 
  6578         -    ** return an error code.
  6579         -    */
  6580         -    rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
         6629  +    rc = pagerOpenWal(pPager);
  6581   6630       if( rc==SQLITE_OK ){
  6582   6631         pPager->journalMode = PAGER_JOURNALMODE_WAL;
  6583   6632         pPager->eState = PAGER_OPEN;
  6584   6633       }
  6585   6634     }else{
  6586   6635       *pbOpen = 1;
  6587   6636     }
................................................................................
  6612   6661       rc = pagerLockDb(pPager, SHARED_LOCK);
  6613   6662       if( rc==SQLITE_OK ){
  6614   6663         rc = sqlite3OsAccess(
  6615   6664             pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
  6616   6665         );
  6617   6666       }
  6618   6667       if( rc==SQLITE_OK && logexists ){
  6619         -      rc = sqlite3WalOpen(pPager->pVfs, pPager->fd,
  6620         -                          pPager->zWal, &pPager->pWal);
         6668  +      rc = pagerOpenWal(pPager);
  6621   6669       }
  6622   6670     }
  6623   6671       
  6624   6672     /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on
  6625   6673     ** the database file, the log and log-summary files will be deleted.
  6626   6674     */
  6627   6675     if( rc==SQLITE_OK && pPager->pWal ){
  6628         -    rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
         6676  +    rc = pagerExclusiveLock(pPager);
  6629   6677       if( rc==SQLITE_OK ){
  6630   6678         rc = sqlite3WalClose(pPager->pWal,
  6631         -                           (pPager->noSync ? 0 : pPager->sync_flags), 
  6632         -        pPager->pageSize, (u8*)pPager->pTmpSpace
         6679  +          (pPager->noSync ? 0 : pPager->sync_flags), 
         6680  +          pPager->pageSize, (u8*)pPager->pTmpSpace
  6633   6681         );
  6634   6682         pPager->pWal = 0;
  6635         -    }else{
  6636         -      /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
  6637         -      ** that we did get back to SHARED. */
  6638         -      pagerUnlockDb(pPager, SQLITE_LOCK_SHARED);
  6639   6683       }
  6640   6684     }
  6641   6685     return rc;
  6642   6686   }
  6643   6687   
  6644   6688   #ifdef SQLITE_HAS_CODEC
  6645   6689   /*

Changes to src/test_vfs.c.

    22     22   **
    23     23   ** Available options are:
    24     24   **
    25     25   **   -noshm      BOOLEAN        (True to omit shm methods. Default false)
    26     26   **   -default    BOOLEAN        (True to make the vfs default. Default false)
    27     27   **   -szosfile   INTEGER        (Value for sqlite3_vfs.szOsFile)
    28     28   **   -mxpathname INTEGER        (Value for sqlite3_vfs.mxPathname)
           29  +**   -iversion   INTEGER        (Value for sqlite3_vfs.iVersion)
    29     30   */
    30     31   
    31     32   #include "sqlite3.h"
    32     33   #include "sqliteInt.h"
    33     34   
    34     35   typedef struct Testvfs Testvfs;
    35     36   typedef struct TestvfsShm TestvfsShm;

Changes to src/wal.c.

   424    424     const char *zWalName;      /* Name of WAL file */
   425    425     u32 nCkpt;                 /* Checkpoint sequence counter in the wal-header */
   426    426   #ifdef SQLITE_DEBUG
   427    427     u8 lockError;              /* True if a locking error has occurred */
   428    428   #endif
   429    429   };
   430    430   
          431  +/*
          432  +** Candidate values for Wal.exclusiveMode.
          433  +*/
          434  +#define WAL_NORMAL_MODE     0
          435  +#define WAL_EXCLUSIVE_MODE  1     
          436  +#define WAL_HEAPMEMORY_MODE 2
          437  +
   431    438   /*
   432    439   ** Each page of the wal-index mapping contains a hash-table made up of
   433    440   ** an array of HASHTABLE_NSLOT elements of the following type.
   434    441   */
   435    442   typedef u16 ht_slot;
   436    443   
   437    444   /*
................................................................................
   510    517              sizeof(u32*)*(iPage+1-pWal->nWiData));
   511    518       pWal->apWiData = apNew;
   512    519       pWal->nWiData = iPage+1;
   513    520     }
   514    521   
   515    522     /* Request a pointer to the required page from the VFS */
   516    523     if( pWal->apWiData[iPage]==0 ){
   517         -    rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, 
   518         -        pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
   519         -    );
          524  +    if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
          525  +      pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
          526  +      if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
          527  +    }else{
          528  +      rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, 
          529  +          pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
          530  +      );
          531  +    }
   520    532     }
   521    533   
   522    534     *ppPage = pWal->apWiData[iPage];
   523    535     assert( iPage==0 || *ppPage || rc!=SQLITE_OK );
   524    536     return rc;
   525    537   }
   526    538   
................................................................................
   594    606         aData += 2;
   595    607       }while( aData<aEnd );
   596    608     }
   597    609   
   598    610     aOut[0] = s1;
   599    611     aOut[1] = s2;
   600    612   }
          613  +
          614  +static void walShmBarrier(Wal *pWal){
          615  +  if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){
          616  +    sqlite3OsShmBarrier(pWal->pDbFd);
          617  +  }
          618  +}
   601    619   
   602    620   /*
   603    621   ** Write the header information in pWal->hdr into the wal-index.
   604    622   **
   605    623   ** The checksum on pWal->hdr is updated before it is written.
   606    624   */
   607    625   static void walIndexWriteHdr(Wal *pWal){
................................................................................
   609    627     const int nCksum = offsetof(WalIndexHdr, aCksum);
   610    628   
   611    629     assert( pWal->writeLock );
   612    630     pWal->hdr.isInit = 1;
   613    631     pWal->hdr.iVersion = WALINDEX_MAX_VERSION;
   614    632     walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum);
   615    633     memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr));
   616         -  sqlite3OsShmBarrier(pWal->pDbFd);
          634  +  walShmBarrier(pWal);
   617    635     memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr));
   618    636   }
   619    637   
   620    638   /*
   621    639   ** This function encodes a single frame header and writes it to a buffer
   622    640   ** supplied by the caller. A frame-header is made up of a series of 
   623    641   ** 4-byte big-endian integers, as follows:
................................................................................
  1181   1199     return rc;
  1182   1200   }
  1183   1201   
  1184   1202   /*
  1185   1203   ** Close an open wal-index.
  1186   1204   */
  1187   1205   static void walIndexClose(Wal *pWal, int isDelete){
  1188         -  sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
         1206  +  if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
         1207  +    int i;
         1208  +    for(i=0; i<pWal->nWiData; i++){
         1209  +      sqlite3_free((void *)pWal->apWiData[i]);
         1210  +      pWal->apWiData[i] = 0;
         1211  +    }
         1212  +  }else{
         1213  +    sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
         1214  +  }
  1189   1215   }
  1190   1216   
  1191   1217   /* 
  1192   1218   ** Open a connection to the WAL file zWalName. The database file must 
  1193   1219   ** already be opened on connection pDbFd. The buffer that zWalName points
  1194   1220   ** to must remain valid for the lifetime of the returned Wal* handle.
  1195   1221   **
................................................................................
  1203   1229   ** *ppWal is set to point to a new WAL handle. If an error occurs,
  1204   1230   ** an SQLite error code is returned and *ppWal is left unmodified.
  1205   1231   */
  1206   1232   int sqlite3WalOpen(
  1207   1233     sqlite3_vfs *pVfs,              /* vfs module to open wal and wal-index */
  1208   1234     sqlite3_file *pDbFd,            /* The open database file */
  1209   1235     const char *zWalName,           /* Name of the WAL file */
         1236  +  int bNoShm,                     /* True to run in heap-memory mode */
  1210   1237     Wal **ppWal                     /* OUT: Allocated Wal handle */
  1211   1238   ){
  1212   1239     int rc;                         /* Return Code */
  1213   1240     Wal *pRet;                      /* Object to allocate and return */
  1214   1241     int flags;                      /* Flags passed to OsOpen() */
  1215   1242   
  1216   1243     assert( zWalName && zWalName[0] );
................................................................................
  1236   1263     }
  1237   1264   
  1238   1265     pRet->pVfs = pVfs;
  1239   1266     pRet->pWalFd = (sqlite3_file *)&pRet[1];
  1240   1267     pRet->pDbFd = pDbFd;
  1241   1268     pRet->readLock = -1;
  1242   1269     pRet->zWalName = zWalName;
         1270  +  pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
  1243   1271   
  1244   1272     /* Open file handle on the write-ahead log file. */
  1245   1273     flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL);
  1246   1274     rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags);
  1247   1275     if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){
  1248   1276       pRet->readOnly = 1;
  1249   1277     }
................................................................................
  1669   1697       ** the database. In this case checkpoint the database and unlink both
  1670   1698       ** the wal and wal-index files.
  1671   1699       **
  1672   1700       ** The EXCLUSIVE lock is not released before returning.
  1673   1701       */
  1674   1702       rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
  1675   1703       if( rc==SQLITE_OK ){
  1676         -      pWal->exclusiveMode = 1;
         1704  +      if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
         1705  +        pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
         1706  +      }
  1677   1707         rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf);
  1678   1708         if( rc==SQLITE_OK ){
  1679   1709           isDelete = 1;
  1680   1710         }
  1681   1711       }
  1682   1712   
  1683   1713       walIndexClose(pWal, isDelete);
................................................................................
  1725   1755     ** There are two copies of the header at the beginning of the wal-index.
  1726   1756     ** When reading, read [0] first then [1].  Writes are in the reverse order.
  1727   1757     ** Memory barriers are used to prevent the compiler or the hardware from
  1728   1758     ** reordering the reads and writes.
  1729   1759     */
  1730   1760     aHdr = walIndexHdr(pWal);
  1731   1761     memcpy(&h1, (void *)&aHdr[0], sizeof(h1));
  1732         -  sqlite3OsShmBarrier(pWal->pDbFd);
         1762  +  walShmBarrier(pWal);
  1733   1763     memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
  1734   1764   
  1735   1765     if( memcmp(&h1, &h2, sizeof(h1))!=0 ){
  1736   1766       return 1;   /* Dirty read */
  1737   1767     }  
  1738   1768     if( h1.isInit==0 ){
  1739   1769       return 1;   /* Malformed header - probably all zeros */
................................................................................
  1926   1956   
  1927   1957     pInfo = walCkptInfo(pWal);
  1928   1958     if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){
  1929   1959       /* The WAL has been completely backfilled (or it is empty).
  1930   1960       ** and can be safely ignored.
  1931   1961       */
  1932   1962       rc = walLockShared(pWal, WAL_READ_LOCK(0));
  1933         -    sqlite3OsShmBarrier(pWal->pDbFd);
         1963  +    walShmBarrier(pWal);
  1934   1964       if( rc==SQLITE_OK ){
  1935   1965         if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
  1936   1966           /* It is not safe to allow the reader to continue here if frames
  1937   1967           ** may have been appended to the log before READ_LOCK(0) was obtained.
  1938   1968           ** When holding READ_LOCK(0), the reader ignores the entire log file,
  1939   1969           ** which implies that the database file contains a trustworthy
  1940   1970           ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from
................................................................................
  2020   2050       **
  2021   2051       ** This does not guarantee that the copy of the wal-index header is up to
  2022   2052       ** date before proceeding. That would not be possible without somehow
  2023   2053       ** blocking writers. It only guarantees that a dangerous checkpoint or 
  2024   2054       ** log-wrap (either of which would require an exclusive lock on
  2025   2055       ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid.
  2026   2056       */
  2027         -    sqlite3OsShmBarrier(pWal->pDbFd);
         2057  +    walShmBarrier(pWal);
  2028   2058       if( pInfo->aReadMark[mxI]!=mxReadMark
  2029   2059        || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
  2030   2060       ){
  2031   2061         walUnlockShared(pWal, WAL_READ_LOCK(mxI));
  2032   2062         return WAL_RETRY;
  2033   2063       }else{
  2034   2064         assert( mxReadMark<=pWal->hdr.mxFrame );
................................................................................
  2663   2693   ** locking_mode=EXCLUSIVE.  This means that the pWal->readLock must
  2664   2694   ** be released.  Return 1 if the transition is made and 0 if the
  2665   2695   ** WAL is already in exclusive-locking mode - meaning that this
  2666   2696   ** routine is a no-op.  The pager must already hold the exclusive lock
  2667   2697   ** on the main database file before invoking this operation.
  2668   2698   **
  2669   2699   ** If op is negative, then do a dry-run of the op==1 case but do
  2670         -** not actually change anything.  The pager uses this to see if it
         2700  +** not actually change anything. The pager uses this to see if it
  2671   2701   ** should acquire the database exclusive lock prior to invoking
  2672   2702   ** the op==1 case.
  2673   2703   */
  2674   2704   int sqlite3WalExclusiveMode(Wal *pWal, int op){
  2675   2705     int rc;
  2676   2706     assert( pWal->writeLock==0 );
         2707  +  assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 );
  2677   2708   
  2678   2709     /* pWal->readLock is usually set, but might be -1 if there was a 
  2679   2710     ** prior error while attempting to acquire are read-lock. This cannot 
  2680   2711     ** happen if the connection is actually in exclusive mode (as no xShmLock
  2681   2712     ** locks are taken in this case). Nor should the pager attempt to
  2682   2713     ** upgrade to exclusive-mode following such an error.
  2683   2714     */
................................................................................
  2702   2733       pWal->exclusiveMode = 1;
  2703   2734       rc = 1;
  2704   2735     }else{
  2705   2736       rc = pWal->exclusiveMode==0;
  2706   2737     }
  2707   2738     return rc;
  2708   2739   }
         2740  +
         2741  +/* 
         2742  +** Return true if the argument is non-NULL and the WAL module is using
         2743  +** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
         2744  +** WAL module is using shared-memory, return false. 
         2745  +*/
         2746  +int sqlite3WalHeapMemory(Wal *pWal){
         2747  +  return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
         2748  +}
  2709   2749   
  2710   2750   #endif /* #ifndef SQLITE_OMIT_WAL */

Changes to src/wal.h.

    31     31   # define sqlite3WalUndo(x,y,z)                 0
    32     32   # define sqlite3WalSavepoint(y,z)
    33     33   # define sqlite3WalSavepointUndo(y,z)          0
    34     34   # define sqlite3WalFrames(u,v,w,x,y,z)         0
    35     35   # define sqlite3WalCheckpoint(u,v,w,x)         0
    36     36   # define sqlite3WalCallback(z)                 0
    37     37   # define sqlite3WalExclusiveMode(y,z)          0
           38  +# define sqlite3WalHeapMemory(z)               0
    38     39   #else
    39     40   
    40     41   #define WAL_SAVEPOINT_NDATA 4
    41     42   
    42     43   /* Connection to a write-ahead log (WAL) file. 
    43     44   ** There is one object of this type for each pager. 
    44     45   */
    45     46   typedef struct Wal Wal;
    46     47   
    47     48   /* Open and close a connection to a write-ahead log. */
    48         -int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, Wal**);
           49  +int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, int, Wal**);
    49     50   int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *);
    50     51   
    51     52   /* Used by readers to open (lock) and close (unlock) a snapshot.  A 
    52     53   ** snapshot is like a read-transaction.  It is the state of the database
    53     54   ** at an instant in time.  sqlite3WalOpenSnapshot gets a read lock and
    54     55   ** preserves the current state even if the other threads or processes
    55     56   ** write to or checkpoint the WAL.  sqlite3WalCloseSnapshot() closes the
................................................................................
    97     98   */
    98     99   int sqlite3WalCallback(Wal *pWal);
    99    100   
   100    101   /* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released)
   101    102   ** by the pager layer on the database file.
   102    103   */
   103    104   int sqlite3WalExclusiveMode(Wal *pWal, int op);
          105  +
          106  +/* Return true if the argument is non-NULL and the WAL module is using
          107  +** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
          108  +** WAL module is using shared-memory, return false. 
          109  +*/
          110  +int sqlite3WalHeapMemory(Wal *pWal);
   104    111   
   105    112   #endif /* ifndef SQLITE_OMIT_WAL */
   106    113   #endif /* _WAL_H_ */

Changes to test/pager1.test.

  1103   1103     
  1104   1104       3  {
  1105   1105         BEGIN;
  1106   1106           SELECT * FROM t1;
  1107   1107         COMMIT;
  1108   1108       } {1 2} 0 -1
  1109   1109     
  1110         -    4  { PRAGMA journal_mode = WAL }    wal    -1 -1
  1111         -    5  { INSERT INTO t1 VALUES(3, 4) }  {}     -1 [wal_file_size 1 1024]
  1112         -    6  { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024]
  1113         -    7  { INSERT INTO t1 VALUES(5, 6); } {}     -1 [wal_file_size 2 1024]
         1110  +    4  { PRAGMA journal_mode = WAL }    wal       -1 -1
         1111  +    5  { INSERT INTO t1 VALUES(3, 4) }  {}        -1 [wal_file_size 1 1024]
         1112  +    6  { PRAGMA locking_mode = NORMAL } exclusive -1 [wal_file_size 1 1024]
         1113  +    7  { INSERT INTO t1 VALUES(5, 6); } {}        -1 [wal_file_size 2 1024]
  1114   1114     
  1115   1115       8  { PRAGMA journal_mode = TRUNCATE } truncate          0 -1
  1116   1116       9  { INSERT INTO t1 VALUES(7, 8) }    {}                0 -1
  1117   1117       10 { SELECT * FROM t1 }               {1 2 3 4 5 6 7 8} 0 -1
  1118   1118     
  1119   1119     }] {
  1120   1120       do_execsql_test pager1-7.1.$tn.1 $sql $res

Changes to test/permutations.test.

   179    179   #   coverage-wal
   180    180   #
   181    181   test_suite "coverage-wal" -description {
   182    182     Coverage tests for file wal.c.
   183    183   } -files {
   184    184     wal.test       wal2.test     wal3.test       walmode.test    
   185    185     walbak.test    walhook.test  walcrash2.test  walcksum.test
   186         -  walfault.test  walbig.test
          186  +  walfault.test  walbig.test   walnoshm.test
   187    187   } 
   188    188   
   189    189   test_suite "coverage-pager" -description {
   190    190     Coverage tests for file pager.c.
   191    191   } -files {
   192    192     pager1.test    pager2.test  pagerfault.test  pagerfault2.test
   193    193     walfault.test  walbak.test  journal2.test    tkt-9d68c883.test

Changes to test/tester.tcl.

   298    298   
   299    299   
   300    300   # Invoke the do_test procedure to run a single test 
   301    301   #
   302    302   proc do_test {name cmd expected} {
   303    303   
   304    304     global argv cmdlinearg
          305  +
          306  +  fix_testname name
   305    307   
   306    308     sqlite3_memdebug_settitle $name
   307    309   
   308    310   #  if {[llength $argv]==0} { 
   309    311   #    set go 1
   310    312   #  } else {
   311    313   #    set go 0

Changes to test/wal2.test.

   426    426   #               connection silently remains in exclusive mode.
   427    427   #
   428    428   do_test wal2-6.1.1 {
   429    429     file delete -force test.db test.db-wal test.db-journal
   430    430     sqlite3 db test.db
   431    431     execsql {
   432    432       Pragma Journal_Mode = Wal;
   433         -    Pragma Locking_Mode = Exclusive;
   434    433     }
   435         -} {wal exclusive}
          434  +} {wal}
   436    435   do_test wal2-6.1.2 {
   437    436     execsql { PRAGMA lock_status }
   438    437   } {main unlocked temp closed}
   439    438   do_test wal2-6.1.3 {
          439  +  execsql {
          440  +    SELECT * FROM sqlite_master;
          441  +    Pragma Locking_Mode = Exclusive;
          442  +  }
   440    443     execsql {
   441    444       BEGIN;
   442    445         CREATE TABLE t1(a, b);
   443    446         INSERT INTO t1 VALUES(1, 2);
   444    447       COMMIT;
   445    448       PRAGMA lock_status;
   446    449     }
................................................................................
   482    485       COMMIT;
   483    486       Pragma loCK_STATus;
   484    487     }
   485    488   } {main exclusive temp closed}
   486    489   do_test wal2-6.2.3 {
   487    490     db close
   488    491     sqlite3 db test.db
          492  +  execsql { SELECT * FROM sqlite_master }
   489    493     execsql { PRAGMA LOCKING_MODE = EXCLUSIVE }
   490    494   } {exclusive}
   491    495   do_test wal2-6.2.4 {
   492    496     execsql {
   493    497       SELECT * FROM t1;
   494    498       pragma lock_status;
   495    499     }
................................................................................
   728    732     return SQLITE_OK
   729    733   }
   730    734   do_test wal2-6.6.1 {
   731    735     testvfs T
   732    736     T script lock_control
   733    737     T filter {}
   734    738     sqlite3 db test.db -vfs T
          739  +  execsql { SELECT * FROM sqlite_master }
   735    740     execsql { PRAGMA locking_mode = exclusive }
   736    741     execsql { INSERT INTO t2 VALUES('V', 'VI') }
   737    742   } {}
   738    743   do_test wal2-6.6.2 {
   739    744     execsql { PRAGMA locking_mode = normal }
   740    745     T filter xShmLock
   741    746     execsql { INSERT INTO t2 VALUES('VII', 'VIII') }
................................................................................
   751    756     catchsql { SELECT * FROM t2 } db2
   752    757   } {1 {database is locked}}
   753    758   do_test wal2-6.6.2 {
   754    759     db2 close
   755    760     T filter {}
   756    761     execsql { INSERT INTO t2 VALUES('IX', 'X') }
   757    762   } {}
   758         -do_test wal2-6.6.3 {
          763  +do_test wal2-6.6.4 {
   759    764     # This time, we have successfully exited exclusive mode. So the second
   760    765     # connection can read the database.
   761    766     sqlite3 db2 test.db -vfs T
   762    767     catchsql { SELECT * FROM t2 } db2
   763    768   } {0 {I II III IV V VI VII VIII IX X}}
   764    769   
   765    770   db close

Changes to test/walfault.test.

   442    442     set rc [sqlite3_wal_checkpoint db]
   443    443     if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] }
   444    444   } -test {
   445    445     db close
   446    446     faultsim_test_result {0 {}}
   447    447   }
   448    448   
          449  +#-------------------------------------------------------------------------
          450  +# Test simple recovery, reading and writing a database file using a 
          451  +# heap-memory wal-index.
          452  +# 
          453  +do_test walfault-13-pre-1 {
          454  +  faultsim_delete_and_reopen
          455  +  execsql {
          456  +    PRAGMA journal_mode = WAL;
          457  +    PRAGMA wal_autocheckpoint = 0;
          458  +    BEGIN;
          459  +      CREATE TABLE abc(a PRIMARY KEY);
          460  +      INSERT INTO abc VALUES(randomblob(1500));
          461  +      INSERT INTO abc VALUES(randomblob(1500));
          462  +    COMMIT;
          463  +  }
          464  +  faultsim_save_and_close
          465  +  file delete sv_test.db-shm
          466  +} {}
          467  +
          468  +do_faultsim_test walfault-13.1 -prep {
          469  +  faultsim_restore_and_reopen
          470  +} -body {
          471  +  db eval { PRAGMA locking_mode = exclusive }
          472  +  db eval { SELECT count(*) FROM abc }
          473  +} -test {
          474  +  faultsim_test_result {0 2}
          475  +  if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
          476  +  faultsim_integrity_check
          477  +}
          478  +
          479  +do_faultsim_test walfault-13.2 -prep {
          480  +  faultsim_restore_and_reopen
          481  +  db eval { PRAGMA locking_mode = exclusive }
          482  +} -body {
          483  +  db eval { PRAGMA journal_mode = delete }
          484  +} -test {
          485  +  faultsim_test_result {0 delete}
          486  +  if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
          487  +  faultsim_integrity_check
          488  +}
          489  +
          490  +do_test walfault-13-pre-2 {
          491  +  faultsim_delete_and_reopen
          492  +  execsql {
          493  +    BEGIN;
          494  +      CREATE TABLE abc(a PRIMARY KEY);
          495  +      INSERT INTO abc VALUES(randomblob(1500));
          496  +      INSERT INTO abc VALUES(randomblob(1500));
          497  +    COMMIT;
          498  +  }
          499  +  faultsim_save_and_close
          500  +} {}
          501  +
          502  +do_faultsim_test walfault-13.3 -prep {
          503  +  faultsim_restore_and_reopen
          504  +} -body {
          505  +  db eval { 
          506  +    PRAGMA locking_mode = exclusive;
          507  +    PRAGMA journal_mode = WAL;
          508  +    INSERT INTO abc VALUES(randomblob(1500));
          509  +  }
          510  +} -test {
          511  +  faultsim_test_result {0 {exclusive wal}}
          512  +  if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
          513  +  faultsim_integrity_check
          514  +  set nRow [db eval {SELECT count(*) FROM abc}]
          515  +  if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" }
          516  +}
   449    517   
   450    518   finish_test

Added test/walnoshm.test.

            1  +# 2010 November 1
            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  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this file is testing that WAL databases may be accessed without
           13  +# using the xShm primitives if the connection is in exclusive-mode.
           14  +#
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +set testprefix walnoshm
           19  +ifcapable !wal {finish_test ; return }
           20  +
           21  +db close
           22  +testvfs tvfsshm
           23  +testvfs tvfs -default 1 -iversion 1 
           24  +sqlite3 db test.db
           25  +
           26  +#--------------------------------------------------------------------------
           27  +# Test that when using a version 1 VFS, a database can only be converted
           28  +# to WAL mode after setting locking_mode=EXCLUSIVE. Also, test that if a
           29  +# WAL database is opened using heap-memory for the WAL index, the connection
           30  +# cannot change back to locking_mode=NORMAL while the database is still in
           31  +# WAL mode.
           32  +#
           33  +do_execsql_test 1.1 {
           34  +  CREATE TABLE t1(x, y);
           35  +  INSERT INTO t1 VALUES(1, 2);
           36  +}
           37  +
           38  +do_execsql_test 1.2 { 
           39  +  PRAGMA journal_mode = WAL;
           40  +  SELECT * FROM t1;
           41  +} {delete 1 2}
           42  +do_test 1.3 { file exists test.db-wal } {0}
           43  +
           44  +do_execsql_test 1.4 { 
           45  +  PRAGMA locking_mode = exclusive;
           46  +  PRAGMA journal_mode = WAL;
           47  +  SELECT * FROM t1;
           48  +} {exclusive wal 1 2}
           49  +do_test 1.5 { file exists test.db-wal } {1}
           50  +
           51  +do_execsql_test 1.6 { INSERT INTO t1 VALUES(3, 4) }
           52  +
           53  +do_execsql_test 1.7 {
           54  +  PRAGMA locking_mode = normal;
           55  +} {exclusive}
           56  +do_execsql_test 1.8 {
           57  +  PRAGMA journal_mode = delete;
           58  +  PRAGMA main.locking_mode;
           59  +} {delete exclusive}
           60  +do_execsql_test 1.9 {
           61  +  PRAGMA locking_mode = normal;
           62  +} {normal}
           63  +do_execsql_test 1.10 {
           64  +  SELECT * FROM t1;
           65  +} {1 2 3 4}
           66  +do_test 1.11 { file exists test.db-wal } {0}
           67  +
           68  +#-------------------------------------------------------------------------
           69  +#
           70  +# 2.1.*: Test that a connection using a version 1 VFS can open a WAL database
           71  +#        and convert it to rollback mode if it is set to use
           72  +#        locking_mode=exclusive.
           73  +#
           74  +# 2.2.*: Test that if the exclusive lock cannot be obtained while attempting
           75  +#        the above, the operation fails and the WAL file is not opened.
           76  +#
           77  +do_execsql_test 2.1.1 {
           78  +  CREATE TABLE t2(x, y);
           79  +  INSERT INTO t2 VALUES('a', 'b');
           80  +  INSERT INTO t2 VALUES('c', 'd');
           81  +}
           82  +do_execsql_test 2.1.2 {
           83  +  PRAGMA locking_mode = exclusive;
           84  +  PRAGMA journal_mode = WAL;
           85  +  INSERT INTO t2 VALUES('e', 'f');
           86  +  INSERT INTO t2 VALUES('g', 'h');
           87  +} {exclusive wal}
           88  +
           89  +do_test 2.1.3 {
           90  +  file copy -force test.db     test2.db
           91  +  file copy -force test.db-wal test2.db-wal
           92  +  sqlite3 db2 test2.db
           93  +  catchsql { SELECT * FROM t2 } db2
           94  +} {1 {unable to open database file}}
           95  +do_test 2.1.4 {
           96  +  catchsql { PRAGMA journal_mode = delete } db2
           97  +} {1 {unable to open database file}}
           98  +do_test 2.1.5 {
           99  +  execsql { 
          100  +    PRAGMA locking_mode = exclusive; 
          101  +    PRAGMA journal_mode = delete;
          102  +    SELECT * FROM t2;
          103  +  } db2
          104  +} {exclusive delete a b c d e f g h}
          105  +
          106  +do_test 2.2.1 {
          107  +  file copy -force test.db     test2.db
          108  +  file copy -force test.db-wal test2.db-wal
          109  +  sqlite3 db3 test2.db -vfs tvfsshm
          110  +  sqlite3 db2 test2.db
          111  +  execsql { SELECT * FROM t2 } db3
          112  +} {a b c d e f g h}
          113  +
          114  +do_test 2.2.2 {
          115  +  execsql  { PRAGMA locking_mode = exclusive }  db2
          116  +  catchsql { PRAGMA journal_mode = delete } db2
          117  +} {1 {database is locked}}
          118  +
          119  +do_test 2.2.3 {
          120  +  # This is to test that [db2] is not holding a PENDING lock (which can 
          121  +  # happen when an attempt to obtain an EXCLUSIVE lock fails).
          122  +  sqlite3 db4 test2.db -vfs tvfsshm
          123  +  execsql { SELECT * FROM t2 } db4
          124  +} {a b c d e f g h}
          125  +
          126  +do_test 2.2.4 {
          127  +  catchsql { SELECT * FROM t2 } db2
          128  +} {1 {database is locked}}
          129  +
          130  +do_test 2.2.5 {
          131  +  db4 close
          132  +  sqlite3 db4 test2.db -vfs tvfsshm
          133  +  execsql { SELECT * FROM t2 } db4
          134  +} {a b c d e f g h}
          135  +
          136  +do_test 2.2.6 {
          137  +  db3 close
          138  +  db4 close
          139  +  execsql { SELECT * FROM t2 } db2
          140  +} {a b c d e f g h}
          141  +
          142  +db2 close
          143  +
          144  +
          145  +finish_test
          146  +