/ Check-in [04113557]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Fix compilation without SQLITE_ENABLE_UNLOCKED. Also other code organization issues.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | begin-concurrent
Files: files | file ages | folders
SHA1: 041135575417201bbcf0544cc69dcb7369c7fb34
User & Date: dan 2015-08-24 16:00:08
Wiki:begin-concurrent
Context
2015-08-24
19:08
Fix handling of attempts to modify the database schema, application_id or user_version within an UNLOCKED transaction. check-in: 5b9f2721 user: dan tags: begin-concurrent
16:00
Fix compilation without SQLITE_ENABLE_UNLOCKED. Also other code organization issues. check-in: 04113557 user: dan tags: begin-concurrent
10:05
Consolidate two blocks of similar code in btreeFixUnlocked(). check-in: 701302b4 user: dan tags: begin-concurrent
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

  3342   3342           int exFlag = (p->db->bUnlocked && !ISAUTOVACUUM) ? -1 : (wrflag>1);
  3343   3343           int bSubjInMem = sqlite3TempInMemory(p->db);
  3344   3344           assert( p->db->bUnlocked==0 || wrflag==1 );
  3345   3345           rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem);
  3346   3346           if( rc==SQLITE_OK ){
  3347   3347             rc = newDatabase(pBt);
  3348   3348           }
         3349  +#ifdef SQLITE_ENABLE_UNLOCKED
  3349   3350           if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){
  3350   3351             rc = btreePtrmapAllocate(pBt);
  3351   3352           }
         3353  +#endif
  3352   3354         }
  3353   3355       }
  3354   3356     
  3355   3357       if( rc!=SQLITE_OK ){
  3356   3358         unlockBtreeIfUnused(pBt);
  3357   3359       }
  3358   3360     }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&

Changes to src/pager.c.

  1742   1742   }
  1743   1743   
  1744   1744   /*
  1745   1745   ** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
  1746   1746   */
  1747   1747   static void pagerFreeBitvecs(Pager *pPager){
  1748   1748     sqlite3BitvecDestroy(pPager->pInJournal);
  1749         -  sqlite3BitvecDestroy(pPager->pAllRead);
  1750   1749     pPager->pInJournal = 0;
         1750  +#ifdef SQLITE_ENABLE_UNLOCKED
         1751  +  sqlite3BitvecDestroy(pPager->pAllRead);
  1751   1752     pPager->pAllRead = 0;
         1753  +#endif
  1752   1754   }
  1753   1755   
  1754   1756   /*
  1755   1757   ** This function is a no-op if the pager is in exclusive mode and not
  1756   1758   ** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
  1757   1759   ** state.
  1758   1760   **
................................................................................
  3017   3019   
  3018   3020   /*
  3019   3021   ** This function is called to rollback a transaction on a WAL database.
  3020   3022   */
  3021   3023   static int pagerRollbackWal(Pager *pPager){
  3022   3024     int rc;                         /* Return Code */
  3023   3025     PgHdr *pList;                   /* List of dirty pages to revert */
  3024         -  int bPage1 = 0;                 /* True if page 1 has been undone */
  3025   3026   
  3026   3027     /* For all pages in the cache that are currently dirty or have already
  3027   3028     ** been written (but not committed) to the log file, do one of the 
  3028   3029     ** following:
  3029   3030     **
  3030   3031     **   + Discard the cached page (if refcount==0), or
  3031   3032     **   + Reload page content from the database (if refcount>0).
  3032   3033     */
  3033   3034     pPager->dbSize = pPager->dbOrigSize;
  3034   3035     rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
  3035   3036     pList = sqlite3PcacheDirtyList(pPager->pPCache);
         3037  +
         3038  +  /* If this is an UNLOCKED transaction, then page 1 must be reread from 
         3039  +  ** the db file, even if it is not dirty. This is because the b-tree layer 
         3040  +  ** may have already zeroed the nFree and iTrunk header fields.  */
         3041  +#ifdef SQLITE_ENABLE_UNLOCKED
         3042  +  if( rc==SQLITE_OK && (pList==0 || pList->pgno!=1) && pPager->pAllRead ){
         3043  +    rc = pagerUndoCallback((void*)pPager, 1);
         3044  +  }
         3045  +#endif
         3046  +
  3036   3047     while( pList && rc==SQLITE_OK ){
  3037   3048       PgHdr *pNext = pList->pDirty;
  3038         -    if( pList->pgno==1 ) bPage1 = 1;
  3039   3049       rc = pagerUndoCallback((void *)pPager, pList->pgno);
  3040   3050       pList = pNext;
  3041   3051     }
  3042   3052   
  3043         -  /* If this is an UNLOCKED transaction, then page 1 must be reread from the
  3044         -  ** db file, even if it is not dirty. This is because the b-tree layer may
  3045         -  ** have already zeroed the nFree and iTrunk header fields.  */
  3046         -  if( rc==SQLITE_OK && bPage1==0 && pPager->pAllRead ){
  3047         -    rc = pagerUndoCallback((void*)pPager, 1);
  3048         -  }
  3049         -
  3050   3053     return rc;
  3051   3054   }
  3052   3055   
  3053   3056   /*
  3054   3057   ** This function is a wrapper around sqlite3WalFrames(). As well as logging
  3055   3058   ** the contents of the list of pages headed by pList (connected by pDirty),
  3056   3059   ** this function notifies any active backup processes that the pages have
................................................................................
  3082   3085     if( isCommit ){
  3083   3086       /* If a WAL transaction is being committed, there is no point in writing
  3084   3087       ** any pages with page numbers greater than nTruncate into the WAL file.
  3085   3088       ** They will never be read by any client. So remove them from the pDirty
  3086   3089       ** list here. */
  3087   3090       PgHdr **ppNext = &pList;
  3088   3091       nList = 0;
  3089         -
  3090   3092       for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
  3091   3093         if( p->pgno<=nTruncate ){
  3092   3094           ppNext = &p->pDirty;
  3093   3095           nList++;
  3094   3096         }
  3095   3097       }
  3096   3098       assert( pList );
................................................................................
  4448   4450       return SQLITE_OK;
  4449   4451     }
  4450   4452   
  4451   4453     pPg->pDirty = 0;
  4452   4454     if( pagerUseWal(pPager) ){
  4453   4455       /* If the transaction is a "BEGIN UNLOCKED" transaction, the page 
  4454   4456       ** cannot be flushed to disk. Return early in this case. */
         4457  +#ifdef SQLITE_ENABLE_UNLOCKED
  4455   4458       if( pPager->pAllRead ) return SQLITE_OK;
         4459  +#endif
  4456   4460   
  4457   4461       /* Write a single frame for this page to the log. */
  4458   4462       rc = subjournalPageIfRequired(pPg); 
  4459   4463       if( rc==SQLITE_OK ){
  4460   4464         rc = pagerWalFrames(pPager, pPg, 0, 0);
  4461   4465       }
  4462   4466     }else{
................................................................................
  5293   5297       return SQLITE_CORRUPT_BKPT;
  5294   5298     }
  5295   5299     pPager->hasBeenUsed = 1;
  5296   5300   
  5297   5301     /* If this is an UNLOCKED transaction and the page being read was
  5298   5302     ** present in the database file when the transaction was opened,
  5299   5303     ** mark it as read in the pAllRead vector.  */
         5304  +#ifdef SQLITE_ENABLE_UNLOCKED
  5300   5305     if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
  5301   5306       rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
  5302   5307       if( rc!=SQLITE_OK ) goto pager_acquire_err;
  5303   5308     }
         5309  +#endif
  5304   5310   
  5305   5311     /* If the pager is in the error state, return an error immediately. 
  5306   5312     ** Otherwise, request the page from the PCache layer. */
  5307   5313     if( pPager->errCode!=SQLITE_OK ){
  5308   5314       rc = pPager->errCode;
  5309   5315     }else{
  5310   5316       if( bMmapOk && pagerUseWal(pPager) ){
................................................................................
  5602   5608   
  5603   5609     if( pPager->errCode ) return pPager->errCode;
  5604   5610     assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
  5605   5611     pPager->subjInMemory = (u8)subjInMemory;
  5606   5612   
  5607   5613     if( ALWAYS(pPager->eState==PAGER_READER) ){
  5608   5614       assert( pPager->pInJournal==0 );
         5615  +#ifdef SQLITE_ENABLE_UNLOCKED
  5609   5616       assert( pPager->pAllRead==0 );
         5617  +#endif
  5610   5618   
  5611   5619       if( pagerUseWal(pPager) ){
  5612   5620         /* If the pager is configured to use locking_mode=exclusive, and an
  5613   5621         ** exclusive lock on the database is not already held, obtain it now.
  5614   5622         */
  5615   5623         if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
  5616   5624           rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
................................................................................
  5621   5629         }
  5622   5630   
  5623   5631         /* Grab the write lock on the log file. If successful, upgrade to
  5624   5632         ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
  5625   5633         ** The busy-handler is not invoked if another connection already
  5626   5634         ** holds the write-lock. If possible, the upper layer will call it.
  5627   5635         */
  5628         -      if( exFlag>=0 ){
  5629         -        rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
  5630         -      }else{
         5636  +#ifdef SQLITE_ENABLE_UNLOCKED
         5637  +      if( exFlag<0 ){
  5631   5638           pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize);
  5632   5639           if( pPager->pAllRead==0 ){
  5633   5640             rc = SQLITE_NOMEM;
  5634   5641           }
         5642  +      }else
         5643  +#endif
         5644  +      {
         5645  +        rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
  5635   5646         }
  5636   5647       }else{
  5637   5648         /* Obtain a RESERVED lock on the database file. If the exFlag parameter
  5638   5649         ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
  5639   5650         ** busy-handler callback can be used when upgrading to the EXCLUSIVE
  5640   5651         ** lock, but not when obtaining the RESERVED lock.
  5641   5652         */
................................................................................
  5934   5945   }
  5935   5946   
  5936   5947   /*
  5937   5948   ** Return TRUE if the page given in the argument was previously passed
  5938   5949   ** to sqlite3PagerWrite().  In other words, return TRUE if it is ok
  5939   5950   ** to change the content of the page.
  5940   5951   */
         5952  +#if defined(SQLITE_ENABLE_UNLOCKED) || !defined(NDEBUG)
  5941   5953   int sqlite3PagerIswriteable(DbPage *pPg){
  5942   5954     return pPg->flags & PGHDR_WRITEABLE;
  5943   5955   }
         5956  +#endif
  5944   5957   
  5945   5958   /*
  5946   5959   ** A call to this routine tells the pager that it is not necessary to
  5947   5960   ** write the information on page pPg back to the disk, even though
  5948   5961   ** that page might be marked as dirty.  This happens, for example, when
  5949   5962   ** the page has been added as a leaf of the freelist and so its
  5950   5963   ** content no longer matters.
................................................................................
  6084   6097       assert( !MEMDB );
  6085   6098       rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
  6086   6099     }
  6087   6100     return rc;
  6088   6101   }
  6089   6102   
  6090   6103   /*
  6091         -** This function may only be called while a write-transaction is active in
  6092         -** rollback. If the connection is in WAL mode, this call is a no-op. 
  6093         -** Otherwise, if the connection does not already have an EXCLUSIVE lock on 
  6094         -** the database file, an attempt is made to obtain one.
         6104  +** This function is called to ensure that all locks required to commit the
         6105  +** current write-transaction to the database file are held. If the db is
         6106  +** in rollback mode, this means the EXCLUSIVE lock on the database file.
  6095   6107   **
  6096         -** If the EXCLUSIVE lock is already held or the attempt to obtain it is
  6097         -** successful, or the connection is in WAL mode, SQLITE_OK is returned.
  6098         -** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is 
  6099         -** returned.
         6108  +** Or, if this is a non-UNLOCKED transaction on a wal-mode database, this
         6109  +** function is a no-op.
         6110  +**
         6111  +** If this is an UNLOCKED transaction on a wal-mode database, this function
         6112  +** attempts to obtain the WRITER lock on the wal file and also checks to
         6113  +** see that the transaction can be safely committed (does not commit with 
         6114  +** any other transaction committed since it was opened).
         6115  +**
         6116  +** If the required locks are already held or successfully obtained and
         6117  +** the transaction can be committed, SQLITE_OK is returned. If a required lock
         6118  +** cannot be obtained, SQLITE_BUSY is returned. Or, if the current transaction
         6119  +** is UNLOCKED and cannot be committed due to a conflict, SQLITE_BUSY_SNAPSHOT
         6120  +** is returned. Otherwise, if some other error occurs (IO error, OOM etc.),
         6121  +** and SQLite error code is returned.
  6100   6122   */
  6101   6123   int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1){
  6102   6124     int rc = SQLITE_OK;
  6103   6125     assert( pPager->eState==PAGER_WRITER_CACHEMOD 
  6104   6126          || pPager->eState==PAGER_WRITER_DBMOD 
  6105   6127          || pPager->eState==PAGER_WRITER_LOCKED 
  6106   6128     );
  6107   6129     assert( assert_pager_state(pPager) );
  6108   6130     if( 0==pagerUseWal(pPager) ){
  6109   6131       rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  6110         -  }else{
         6132  +  }
         6133  +#ifdef SQLITE_ENABLE_UNLOCKED
         6134  +  else{
  6111   6135       if( pPager->pAllRead ){
  6112   6136         /* This is an UNLOCKED transaction. Attempt to lock the wal database
  6113   6137         ** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
  6114   6138         ** invoke the busy-handler and try again for as long as it returns
  6115   6139         ** non-zero.  */
  6116   6140         do {
  6117         -        /* rc = sqlite3WalBeginWriteTransaction(pWal); */
  6118   6141           rc = sqlite3WalLockForCommit(pPager->pWal, pPage1, pPager->pAllRead);
  6119   6142         }while( rc==SQLITE_BUSY 
  6120   6143              && pPager->xBusyHandler(pPager->pBusyHandlerArg) 
  6121   6144         );
  6122   6145       }
  6123   6146     }
         6147  +#endif
  6124   6148     return rc;
  6125   6149   }
  6126   6150   
         6151  +#ifdef SQLITE_ENABLE_UNLOCKED
         6152  +/*
         6153  +** This function is called as part of committing an UNLOCKED transaction.
         6154  +** At this point the wal WRITER lock is held, and all pages in the cache 
         6155  +** except for page 1 are compatible with the snapshot at the head of the
         6156  +** wal file. 
         6157  +**
         6158  +** This function updates the in-memory data structures and reloads the
         6159  +** contents of page 1 so that the client is operating on the snapshot 
         6160  +** at the head of the wal file.
         6161  +**
         6162  +** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
         6163  +*/
  6127   6164   int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1){
  6128   6165     int rc;
  6129   6166     u32 iFrame = 0;
  6130   6167   
  6131   6168     assert( pPager->pWal && pPager->pAllRead );
  6132   6169     rc = sqlite3WalUpgradeSnapshot(pPager->pWal);
  6133   6170     if( rc==SQLITE_OK ){
................................................................................
  6136   6173     if( rc==SQLITE_OK ){
  6137   6174       rc = readDbPage(pPage1, iFrame);
  6138   6175     }
  6139   6176   
  6140   6177     return rc;
  6141   6178   }
  6142   6179   
  6143         -void sqlite3PagerSetDbsize(Pager *pPager, Pgno nFinal){
  6144         -  pPager->dbSize = nFinal;
         6180  +/*
         6181  +** Set the in-memory cache of the database file size to nSz pages.
         6182  +*/
         6183  +void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
         6184  +  pPager->dbSize = nSz;
         6185  +}
         6186  +
         6187  +/*
         6188  +** Return true if this pager is currently within an UNLOCKED transaction.
         6189  +*/
         6190  +int sqlite3PagerIsUnlocked(Pager *pPager){
         6191  +  return pPager->pAllRead!=0;
  6145   6192   }
  6146   6193   
  6147   6194   /*
  6148   6195   ** If this is a WAL mode connection and the WRITER lock is currently held,
  6149   6196   ** relinquish it.
  6150   6197   */
  6151   6198   void sqlite3PagerDropExclusiveLock(Pager *pPager){
  6152   6199     if( pagerUseWal(pPager) ){
  6153   6200       sqlite3WalEndWriteTransaction(pPager->pWal);
  6154   6201     }
  6155   6202   }
  6156         -
  6157         -/*
  6158         -** Return true if this pager is currently within an UNLOCKED transaction.
  6159         -*/
  6160         -int sqlite3PagerIsUnlocked(Pager *pPager){
  6161         -  return pPager->pAllRead!=0;
  6162         -}
         6203  +#endif   /* ifdef SQLITE_ENABLE_UNLOCKED */
  6163   6204   
  6164   6205   
  6165   6206   /*
  6166   6207   ** Sync the database file for the pager pPager. zMaster points to the name
  6167   6208   ** of a master journal file that should be written into the individual
  6168   6209   ** journal file. zMaster may be NULL, which is interpreted as no master
  6169   6210   ** journal (a single database transaction).

Changes to src/test_config.c.

   568    568   #endif
   569    569   
   570    570   #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
   571    571     Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY);
   572    572   #else
   573    573     Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY);
   574    574   #endif
          575  +
          576  +#ifdef SQLITE_ENABLE_UNLOCKED
          577  +  Tcl_SetVar2(interp, "sqlite_options", "unlocked", "1", TCL_GLOBAL_ONLY);
          578  +#else
          579  +  Tcl_SetVar2(interp, "sqlite_options", "unlocked", "0", TCL_GLOBAL_ONLY);
          580  +#endif
   575    581   
   576    582   #ifdef SQLITE_OMIT_UTF16
   577    583     Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY);
   578    584   #else
   579    585     Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY);
   580    586   #endif
   581    587   

Changes to src/vdbeaux.c.

  2021   2021       if( sqlite3BtreeIsInTrans(pBt) ){
  2022   2022         needXcommit = 1;
  2023   2023         if( i!=1 ) nTrans++;
  2024   2024         rc = sqlite3BtreeExclusiveLock(pBt);
  2025   2025       }
  2026   2026     }
  2027   2027   
         2028  +#ifdef SQLITE_ENABLE_UNLOCKED
  2028   2029     if( db->bUnlocked && (rc & 0xFF)==SQLITE_BUSY ){
  2029   2030       /* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while 
  2030   2031       ** attempting to take the WRITER lock on a wal file. Release the
  2031   2032       ** WRITER locks on all wal files and return early.  */
  2032   2033       for(i=0; i<db->nDb; i++){
  2033   2034         Btree *pBt = db->aDb[i].pBt;
  2034   2035         if( sqlite3BtreeIsInTrans(pBt) ){
  2035   2036           sqlite3BtreeEnter(pBt);
  2036   2037           sqlite3PagerDropExclusiveLock(sqlite3BtreePager(pBt));
  2037   2038           sqlite3BtreeLeave(pBt);
  2038   2039         }
  2039   2040       }
  2040   2041     }
         2042  +#endif
  2041   2043   
  2042   2044     if( rc!=SQLITE_OK ){
  2043   2045       return rc;
  2044   2046     }
  2045   2047   
  2046   2048     /* If there are any write-transactions at all, invoke the commit hook */
  2047   2049     if( needXcommit && db->xCommitCallback ){

Changes to src/wal.c.

  2562   2562         pWal->writeLock = 0;
  2563   2563         rc = SQLITE_BUSY_SNAPSHOT;
  2564   2564       }
  2565   2565     }
  2566   2566     return rc;
  2567   2567   }
  2568   2568   
         2569  +/*
         2570  +** This function is called by a writer that has a read-lock on aReadmark[0]
         2571  +** (pWal->readLock==0). This function relinquishes that lock and takes a
         2572  +** lock on a different aReadmark[] slot. 
         2573  +**
         2574  +** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
         2575  +*/
         2576  +static int walUpgradeReadlock(Wal *pWal){
         2577  +  int cnt;
         2578  +  int rc;
         2579  +  assert( pWal->writeLock && pWal->readLock==0 );
         2580  +  walUnlockShared(pWal, WAL_READ_LOCK(0));
         2581  +  pWal->readLock = -1;
         2582  +  cnt = 0;
         2583  +  do{
         2584  +    int notUsed;
         2585  +    rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
         2586  +  }while( rc==WAL_RETRY );
         2587  +  assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
         2588  +  testcase( (rc&0xff)==SQLITE_IOERR );
         2589  +  testcase( rc==SQLITE_PROTOCOL );
         2590  +  testcase( rc==SQLITE_OK );
         2591  +  return rc;
         2592  +}
         2593  +
         2594  +
         2595  +#ifdef SQLITE_ENABLE_UNLOCKED
  2569   2596   /* 
  2570         -** TODO: Combine some code with BeginWriteTransaction()
  2571         -**
  2572   2597   ** This function is only ever called when committing a "BEGIN UNLOCKED"
  2573   2598   ** transaction. It may be assumed that no frames have been written to
  2574         -** the wal file.
         2599  +** the wal file. The second parameter is a pointer to the in-memory 
         2600  +** representation of page 1 of the database (which may or may not be
         2601  +** dirty). The third is a bitvec with a bit set for each page in the
         2602  +** database file that was read by the current unlocked transaction.
         2603  +**
         2604  +** This function performs three tasks:
         2605  +**
         2606  +**   1) It obtains the WRITER lock on the wal file,
         2607  +**
         2608  +**   2) It checks that there are no conflicts between the current
         2609  +**      transaction and any transactions committed to the wal file since
         2610  +**      it was opened, and
         2611  +**
         2612  +**   3) It ejects any non-dirty pages from the page-cache that have been
         2613  +**      written by another client since the UNLOCKED transaction was started
         2614  +**      (so as to avoid ending up with an inconsistent cache after the
         2615  +**      current transaction is committed).
         2616  +**
         2617  +** If no error occurs and the caller may proceed with committing the 
         2618  +** transaction, SQLITE_OK is returned. SQLITE_BUSY is returned if the WRITER
         2619  +** lock cannot be obtained. Or, if the WRITER lock can be obtained but there
         2620  +** are conflicts with a committed transaction, SQLITE_BUSY_SNAPSHOT. Finally,
         2621  +** if an error (i.e. an OOM condition or IO error), an SQLite error code
         2622  +** is returned.
  2575   2623   */
  2576   2624   int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPage1, Bitvec *pAllRead){
  2577   2625     Pager *pPager = pPage1->pPager;
  2578   2626     int rc = walWriteLock(pWal);
  2579   2627   
  2580   2628     /* If the database has been modified since this transaction was started,
  2581   2629     ** check if it is still possible to commit. The transaction can be 
................................................................................
  2664   2712       }
  2665   2713     }
  2666   2714   
  2667   2715     return rc;
  2668   2716   }
  2669   2717   
  2670   2718   /*
  2671         -** This function is called by a writer that has a read-lock on aReadmark[0]
  2672         -** (pWal->readLock==0). This function relinquishes that lock and takes a
  2673         -** lock on a different aReadmark[] slot. 
         2719  +** This function is called as part of committing an UNLOCKED transaction.
         2720  +** It is assumed that sqlite3WalLockForCommit() has already been successfully
         2721  +** called and so (a) the WRITER lock is held and (b) it is known that the
         2722  +** wal-index-header stored in shared memory is not corrupt.
         2723  +**
         2724  +** Before returning, this function upgrades the client so that it is 
         2725  +** operating on the database snapshot currently at the head of the wal file
         2726  +** (even if the UNLOCKED transaction ran against an older snapshot).
  2674   2727   **
  2675   2728   ** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
  2676   2729   */
  2677         -static int walUpgradeReadlock(Wal *pWal){
  2678         -  int cnt;
  2679         -  int rc;
  2680         -  assert( pWal->writeLock && pWal->readLock==0 );
  2681         -  walUnlockShared(pWal, WAL_READ_LOCK(0));
  2682         -  pWal->readLock = -1;
  2683         -  cnt = 0;
  2684         -  do{
  2685         -    int notUsed;
  2686         -    rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
  2687         -  }while( rc==WAL_RETRY );
  2688         -  assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
  2689         -  testcase( (rc&0xff)==SQLITE_IOERR );
  2690         -  testcase( rc==SQLITE_PROTOCOL );
  2691         -  testcase( rc==SQLITE_OK );
  2692         -  return rc;
  2693         -}
  2694         -
  2695   2730   int sqlite3WalUpgradeSnapshot(Wal *pWal){
  2696   2731     int rc = SQLITE_OK;
  2697   2732     assert( pWal->writeLock );
  2698   2733     memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
  2699   2734   
  2700   2735     /* If this client has its read-lock on slot aReadmark[0] and the entire
  2701   2736     ** wal has not been checkpointed, switch it to a different slot. Otherwise
................................................................................
  2702   2737     ** any reads performed between now and committing the transaction will
  2703   2738     ** read from the old snapshot - not the one just upgraded to.  */
  2704   2739     if( pWal->readLock==0 && pWal->hdr.mxFrame!=walCkptInfo(pWal)->nBackfill ){
  2705   2740       rc = walUpgradeReadlock(pWal);
  2706   2741     }
  2707   2742     return rc;
  2708   2743   }
         2744  +#endif   /* SQLITE_ENABLE_UNLOCKED */
  2709   2745   
  2710   2746   /*
  2711   2747   ** End a write transaction.  The commit has already been done.  This
  2712   2748   ** routine merely releases the lock.
  2713   2749   */
  2714   2750   int sqlite3WalEndWriteTransaction(Wal *pWal){
  2715   2751     if( pWal->writeLock ){

Changes to test/unlocked.test.

    11     11   #
    12     12   
    13     13   set testdir [file dirname $argv0]
    14     14   source $testdir/tester.tcl
    15     15   source $testdir/lock_common.tcl
    16     16   set ::testprefix unlocked
    17     17   
           18  +ifcapable !unlocked {
           19  +  finish_test
           20  +  return
           21  +}
    18     22   
    19     23   do_execsql_test 1.0 {
    20     24     PRAGMA journal_mode = wal;
    21     25   } {wal}
    22     26   
    23     27   do_execsql_test 1.1 {
    24     28     CREATE TABLE t1(k INTEGER PRIMARY KEY, v);

Changes to test/unlocked2.test.

    11     11   #
    12     12   
    13     13   set testdir [file dirname $argv0]
    14     14   source $testdir/tester.tcl
    15     15   source $testdir/lock_common.tcl
    16     16   set ::testprefix unlocked2
    17     17   
           18  +ifcapable !unlocked {
           19  +  finish_test
           20  +  return
           21  +}
    18     22   
    19     23   do_multiclient_test tn {
    20     24   
    21     25     do_test 1.$tn.1 {
    22     26       sql1 {
    23     27         PRAGMA journal_mode = wal;
    24     28         CREATE TABLE t1(x);

Changes to test/unlocked3.test.

    15     15   
    16     16   set testdir [file dirname $argv0]
    17     17   source $testdir/tester.tcl
    18     18   source $testdir/lock_common.tcl
    19     19   set ::testprefix unlocked3
    20     20   
    21     21   if {$AUTOVACUUM} { finish_test ; return }
           22  +ifcapable !unlocked {
           23  +  finish_test
           24  +  return
           25  +}
    22     26   
    23     27   proc create_schema {} {
    24     28     db eval {
    25     29       PRAGMA journal_mode = wal;
    26     30   
    27     31       CREATE TABLE t1(x, y);
    28     32       CREATE TABLE t2(x, y);