/ Check-in [80794216]
Login

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

Overview
Comment:Add an experimental "BEGIN UNLOCKED" command.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | begin-concurrent
Files: files | file ages | folders
SHA1: 8079421604dbd40d03471dad6d12115119b554c2
User & Date: dan 2015-07-27 19:31:45
Wiki:begin-concurrent
Context
2015-07-28
16:46
Add some test cases and fix some small problems with BEGIN UNLOCKED transactions. check-in: 6da0e962 user: dan tags: begin-concurrent
2015-07-27
19:31
Add an experimental "BEGIN UNLOCKED" command. check-in: 80794216 user: dan tags: begin-concurrent
13:49
Version 3.8.11 check-in: b8e92227 user: drh tags: trunk, release, version-3.8.11
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

  3138   3138       */
  3139   3139       while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) );
  3140   3140   
  3141   3141       if( rc==SQLITE_OK && wrflag ){
  3142   3142         if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
  3143   3143           rc = SQLITE_READONLY;
  3144   3144         }else{
  3145         -        rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
         3145  +        int bSubjInMem = sqlite3TempInMemory(p->db);
         3146  +        int exFlag = p->db->bUnlocked ? -1 : (wrflag>1);
         3147  +        assert( p->db->bUnlocked==0 || wrflag==1 );
         3148  +        rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem);
  3146   3149           if( rc==SQLITE_OK ){
  3147   3150             rc = newDatabase(pBt);
  3148   3151           }
  3149   3152         }
  3150   3153       }
  3151   3154     
  3152   3155       if( rc!=SQLITE_OK ){
................................................................................
  3666   3669   ** the write-transaction for this database file is to delete the journal.
  3667   3670   */
  3668   3671   int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
  3669   3672     int rc = SQLITE_OK;
  3670   3673     if( p->inTrans==TRANS_WRITE ){
  3671   3674       BtShared *pBt = p->pBt;
  3672   3675       sqlite3BtreeEnter(p);
         3676  +
  3673   3677   #ifndef SQLITE_OMIT_AUTOVACUUM
  3674         -    if( pBt->autoVacuum ){
         3678  +    /* Figure out if this is a commit of an UNLOCKED transaction that 
         3679  +    ** requires a snapshot upgrade. If so, skip any auto-vacuum 
         3680  +    ** processing.  */
         3681  +    if( pBt->autoVacuum && (
         3682  +        0==pBt->db->bUnlocked
         3683  +     || 0==sqlite3PagerCommitRequiresUpgrade(pBt->pPager) 
         3684  +    )){
  3675   3685         rc = autoVacuumCommit(pBt);
  3676   3686         if( rc!=SQLITE_OK ){
  3677   3687           sqlite3BtreeLeave(p);
  3678   3688           return rc;
  3679   3689         }
  3680   3690       }
  3681   3691       if( pBt->bDoTruncate ){
  3682   3692         sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
  3683   3693       }
  3684   3694   #endif
  3685         -    rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
         3695  +    if( rc==SQLITE_OK ){
         3696  +      rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
         3697  +    }
  3686   3698       sqlite3BtreeLeave(p);
  3687   3699     }
  3688   3700     return rc;
  3689   3701   }
  3690   3702   
  3691   3703   /*
  3692   3704   ** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback()
................................................................................
  9574   9586     return (p->pBt->btsFlags & BTS_READ_ONLY)!=0;
  9575   9587   }
  9576   9588   
  9577   9589   /*
  9578   9590   ** Return the size of the header added to each page by this module.
  9579   9591   */
  9580   9592   int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
         9593  +
         9594  +int sqlite3BtreeExclusiveLock(Btree *p){
         9595  +  int rc;
         9596  +  BtShared *pBt = p->pBt;
         9597  +  sqlite3BtreeEnter(p);
         9598  +  rc = sqlite3PagerExclusiveLock(pBt->pPager, pBt->pPage1->pDbPage);
         9599  +  sqlite3BtreeLeave(p);
         9600  +  return rc;
         9601  +}
         9602  +
         9603  +
         9604  +

Changes to src/btree.h.

   266    266   # define sqlite3BtreeLeaveAll(X)
   267    267   
   268    268   # define sqlite3BtreeHoldsMutex(X) 1
   269    269   # define sqlite3BtreeHoldsAllMutexes(X) 1
   270    270   # define sqlite3SchemaMutexHeld(X,Y,Z) 1
   271    271   #endif
   272    272   
          273  +int sqlite3BtreeExclusiveLock(Btree*);
   273    274   
   274    275   #endif /* _BTREE_H_ */

Changes to src/build.c.

  3824   3824     assert( db!=0 );
  3825   3825   /*  if( db->aDb[0].pBt==0 ) return; */
  3826   3826     if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){
  3827   3827       return;
  3828   3828     }
  3829   3829     v = sqlite3GetVdbe(pParse);
  3830   3830     if( !v ) return;
  3831         -  if( type!=TK_DEFERRED ){
         3831  +  if( type==TK_IMMEDIATE || type==TK_EXCLUSIVE ){
  3832   3832       for(i=0; i<db->nDb; i++){
  3833   3833         sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
  3834   3834         sqlite3VdbeUsesBtree(v, i);
  3835   3835       }
  3836   3836     }
  3837         -  sqlite3VdbeAddOp2(v, OP_AutoCommit, 0, 0);
         3837  +  sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, (type==TK_UNLOCKED));
  3838   3838   }
  3839   3839   
  3840   3840   /*
  3841   3841   ** Commit a transaction
  3842   3842   */
  3843   3843   void sqlite3CommitTransaction(Parse *pParse){
  3844   3844     Vdbe *v;

Changes to src/main.c.

  2733   2733     db->magic = SQLITE_MAGIC_BUSY;
  2734   2734     db->aDb = db->aDbStatic;
  2735   2735   
  2736   2736     assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
  2737   2737     memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
  2738   2738     db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS;
  2739   2739     db->autoCommit = 1;
         2740  +  db->bUnlocked = 0;
  2740   2741     db->nextAutovac = -1;
  2741   2742     db->szMmap = sqlite3GlobalConfig.szMmap;
  2742   2743     db->nextPagesize = 0;
  2743   2744     db->nMaxSorterMmap = 0x7FFFFFFF;
  2744   2745     db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill
  2745   2746   #if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
  2746   2747                    | SQLITE_AutoIndex

Changes to src/pager.c.

  3062   3062     if( isCommit ){
  3063   3063       /* If a WAL transaction is being committed, there is no point in writing
  3064   3064       ** any pages with page numbers greater than nTruncate into the WAL file.
  3065   3065       ** They will never be read by any client. So remove them from the pDirty
  3066   3066       ** list here. */
  3067   3067       PgHdr **ppNext = &pList;
  3068   3068       nList = 0;
         3069  +
  3069   3070       for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
  3070   3071         if( p->pgno<=nTruncate ){
  3071   3072           ppNext = &p->pDirty;
  3072   3073           nList++;
  3073   3074         }
  3074   3075       }
  3075   3076       assert( pList );
................................................................................
  4077   4078   
  4078   4079     assert( pPager->eState==PAGER_WRITER_CACHEMOD
  4079   4080          || pPager->eState==PAGER_WRITER_DBMOD
  4080   4081     );
  4081   4082     assert( assert_pager_state(pPager) );
  4082   4083     assert( !pagerUseWal(pPager) );
  4083   4084   
  4084         -  rc = sqlite3PagerExclusiveLock(pPager);
         4085  +  rc = sqlite3PagerExclusiveLock(pPager, 0);
  4085   4086     if( rc!=SQLITE_OK ) return rc;
  4086   4087   
  4087   4088     if( !pPager->noSync ){
  4088   4089       assert( !pPager->tempFile );
  4089   4090       if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
  4090   4091         const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
  4091   4092         assert( isOpen(pPager->jfd) );
................................................................................
  4425   4426         || (pPg->flags & PGHDR_NEED_SYNC)!=0)
  4426   4427     ){
  4427   4428       return SQLITE_OK;
  4428   4429     }
  4429   4430   
  4430   4431     pPg->pDirty = 0;
  4431   4432     if( pagerUseWal(pPager) ){
         4433  +    /* If the transaction is a "BEGIN UNLOCKED" transaction, the page 
         4434  +    ** cannot be flushed to disk. Return early in this case. */
         4435  +    if( sqlite3WalIsInTrans(pPager->pWal)==0 ) return SQLITE_OK;
         4436  +
  4432   4437       /* Write a single frame for this page to the log. */
  4433   4438       rc = subjournalPageIfRequired(pPg); 
  4434   4439       if( rc==SQLITE_OK ){
  4435   4440         rc = pagerWalFrames(pPager, pPg, 0, 0);
  4436   4441       }
  4437   4442     }else{
  4438   4443     
................................................................................
  5544   5549     return rc;
  5545   5550   }
  5546   5551   
  5547   5552   /*
  5548   5553   ** Begin a write-transaction on the specified pager object. If a 
  5549   5554   ** write-transaction has already been opened, this function is a no-op.
  5550   5555   **
  5551         -** If the exFlag argument is false, then acquire at least a RESERVED
  5552         -** lock on the database file. If exFlag is true, then acquire at least
         5556  +** If the exFlag argument is 0, then acquire at least a RESERVED
         5557  +** lock on the database file. If exFlag is >0, then acquire at least
  5553   5558   ** an EXCLUSIVE lock. If such a lock is already held, no locking 
  5554   5559   ** functions need be called.
         5560  +**
         5561  +** If (exFlag<0) and the database is in WAL mode, do not take any locks.
         5562  +** The transaction will run in UNLOCKED mode instead.
  5555   5563   **
  5556   5564   ** If the subjInMemory argument is non-zero, then any sub-journal opened
  5557   5565   ** within this transaction will be opened as an in-memory file. This
  5558   5566   ** has no effect if the sub-journal is already opened (as it may be when
  5559   5567   ** running in exclusive mode) or if the transaction does not require a
  5560   5568   ** sub-journal. If the subjInMemory argument is zero, then any required
  5561   5569   ** sub-journal is implemented in-memory if pPager is an in-memory database, 
................................................................................
  5584   5592         }
  5585   5593   
  5586   5594         /* Grab the write lock on the log file. If successful, upgrade to
  5587   5595         ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
  5588   5596         ** The busy-handler is not invoked if another connection already
  5589   5597         ** holds the write-lock. If possible, the upper layer will call it.
  5590   5598         */
  5591         -      rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
         5599  +      if( exFlag>=0 ){
         5600  +        rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
         5601  +      }
  5592   5602       }else{
  5593   5603         /* Obtain a RESERVED lock on the database file. If the exFlag parameter
  5594   5604         ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
  5595   5605         ** busy-handler callback can be used when upgrading to the EXCLUSIVE
  5596   5606         ** lock, but not when obtaining the RESERVED lock.
  5597   5607         */
  5598   5608         rc = pagerLockDb(pPager, RESERVED_LOCK);
  5599         -      if( rc==SQLITE_OK && exFlag ){
         5609  +      if( rc==SQLITE_OK && exFlag>0 ){
  5600   5610           rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  5601   5611         }
  5602   5612       }
  5603   5613   
  5604   5614       if( rc==SQLITE_OK ){
  5605   5615         /* Change to WRITER_LOCKED state.
  5606   5616         **
................................................................................
  6052   6062   ** the database file, an attempt is made to obtain one.
  6053   6063   **
  6054   6064   ** If the EXCLUSIVE lock is already held or the attempt to obtain it is
  6055   6065   ** successful, or the connection is in WAL mode, SQLITE_OK is returned.
  6056   6066   ** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is 
  6057   6067   ** returned.
  6058   6068   */
  6059         -int sqlite3PagerExclusiveLock(Pager *pPager){
         6069  +int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1){
  6060   6070     int rc = SQLITE_OK;
  6061   6071     assert( pPager->eState==PAGER_WRITER_CACHEMOD 
  6062   6072          || pPager->eState==PAGER_WRITER_DBMOD 
  6063   6073          || pPager->eState==PAGER_WRITER_LOCKED 
  6064   6074     );
  6065   6075     assert( assert_pager_state(pPager) );
  6066   6076     if( 0==pagerUseWal(pPager) ){
  6067   6077       rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
         6078  +  }else{
         6079  +    Wal *pWal = pPager->pWal;
         6080  +    if( 0==sqlite3WalIsInTrans(pWal) ){
         6081  +      /* TODO: There must be an optimization opportunity here, as this call 
         6082  +      ** to PcacheDirtyList() sorts the list of dirty pages, even though it 
         6083  +      ** is not really required - and will be sorted again in CommitPhaseOne()
         6084  +      ** in any case.  */
         6085  +      PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
         6086  +
         6087  +      /* This is an UNLOCKED transaction. Attempt to lock the wal database
         6088  +      ** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
         6089  +      ** invoke the busy-handler and try again for as long as it returns
         6090  +      ** non-zero.  */
         6091  +      do {
         6092  +        /* rc = sqlite3WalBeginWriteTransaction(pWal); */
         6093  +        rc = sqlite3WalLockForCommit(pWal, pList, pPage1); 
         6094  +      }while( rc==SQLITE_BUSY 
         6095  +           && pPager->xBusyHandler(pPager->pBusyHandlerArg) 
         6096  +      );
         6097  +    }
  6068   6098     }
  6069   6099     return rc;
  6070   6100   }
         6101  +
         6102  +/*
         6103  +** If this is a WAL mode connection and the WRITER lock is currently held,
         6104  +** relinquish it.
         6105  +*/
         6106  +void sqlite3PagerDropExclusiveLock(Pager *pPager){
         6107  +  if( pagerUseWal(pPager) ){
         6108  +    sqlite3WalEndWriteTransaction(pPager->pWal);
         6109  +  }
         6110  +}
         6111  +
         6112  +/*
         6113  +** Return true if this is a WAL database and snapshot upgrade is required
         6114  +** before the current transaction can be committed.
         6115  +*/
         6116  +int sqlite3PagerCommitRequiresUpgrade(Pager *pPager){
         6117  +  int res = 0;
         6118  +  if( pagerUseWal(pPager) ){
         6119  +    res = sqlite3WalCommitRequiresUpgrade(pPager->pWal);
         6120  +  }
         6121  +  return res;
         6122  +}
  6071   6123   
  6072   6124   /*
  6073   6125   ** Sync the database file for the pager pPager. zMaster points to the name
  6074   6126   ** of a master journal file that should be written into the individual
  6075   6127   ** journal file. zMaster may be NULL, which is interpreted as no master
  6076   6128   ** journal (a single database transaction).
  6077   6129   **

Changes to src/pager.h.

   146    146   void *sqlite3PagerGetData(DbPage *); 
   147    147   void *sqlite3PagerGetExtra(DbPage *); 
   148    148   
   149    149   /* Functions used to manage pager transactions and savepoints. */
   150    150   void sqlite3PagerPagecount(Pager*, int*);
   151    151   int sqlite3PagerBegin(Pager*, int exFlag, int);
   152    152   int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
   153         -int sqlite3PagerExclusiveLock(Pager*);
          153  +int sqlite3PagerExclusiveLock(Pager*, DbPage *pPage1);
   154    154   int sqlite3PagerSync(Pager *pPager, const char *zMaster);
   155    155   int sqlite3PagerCommitPhaseTwo(Pager*);
   156    156   int sqlite3PagerRollback(Pager*);
   157    157   int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
   158    158   int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
   159    159   int sqlite3PagerSharedLock(Pager *pPager);
   160    160   
          161  +void sqlite3PagerDropExclusiveLock(Pager*);
          162  +int sqlite3PagerCommitRequiresUpgrade(Pager*);
          163  +
   161    164   #ifndef SQLITE_OMIT_WAL
   162    165     int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*);
   163    166     int sqlite3PagerWalSupported(Pager *pPager);
   164    167     int sqlite3PagerWalCallback(Pager *pPager);
   165    168     int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
   166    169     int sqlite3PagerCloseWal(Pager *pPager);
   167    170   #endif

Changes to src/parse.y.

   117    117   trans_opt ::= TRANSACTION.
   118    118   trans_opt ::= TRANSACTION nm.
   119    119   %type transtype {int}
   120    120   transtype(A) ::= .             {A = TK_DEFERRED;}
   121    121   transtype(A) ::= DEFERRED(X).  {A = @X;}
   122    122   transtype(A) ::= IMMEDIATE(X). {A = @X;}
   123    123   transtype(A) ::= EXCLUSIVE(X). {A = @X;}
          124  +transtype(A) ::= UNLOCKED(X).  {A = @X;}
   124    125   cmd ::= COMMIT trans_opt.      {sqlite3CommitTransaction(pParse);}
   125    126   cmd ::= END trans_opt.         {sqlite3CommitTransaction(pParse);}
   126    127   cmd ::= ROLLBACK trans_opt.    {sqlite3RollbackTransaction(pParse);}
   127    128   
   128    129   savepoint_opt ::= SAVEPOINT.
   129    130   savepoint_opt ::= .
   130    131   cmd ::= SAVEPOINT nm(X). {

Changes to src/sqliteInt.h.

  1143   1143     i64 szMmap;                   /* Default mmap_size setting */
  1144   1144     unsigned int openFlags;       /* Flags passed to sqlite3_vfs.xOpen() */
  1145   1145     int errCode;                  /* Most recent error code (SQLITE_*) */
  1146   1146     int errMask;                  /* & result codes with this before returning */
  1147   1147     u16 dbOptFlags;               /* Flags to enable/disable optimizations */
  1148   1148     u8 enc;                       /* Text encoding */
  1149   1149     u8 autoCommit;                /* The auto-commit flag. */
         1150  +  u8 bUnlocked;                 /* Current transaction is "UNLOCKED" */
  1150   1151     u8 temp_store;                /* 1: file 2: memory 0: default */
  1151   1152     u8 mallocFailed;              /* True if we have seen a malloc failure */
  1152   1153     u8 dfltLockMode;              /* Default locking-mode for attached dbs */
  1153   1154     signed char nextAutovac;      /* Autovac setting after VACUUM if >=0 */
  1154   1155     u8 suppressErr;               /* Do not issue error messages if true */
  1155   1156     u8 vtabOnConflict;            /* Value to return for s3_vtab_on_conflict() */
  1156   1157     u8 isTransactionSavepoint;    /* True if the outermost savepoint is a TS */

Changes to src/vacuum.c.

   352    352     ** database. No locks are held on any other files (since the main file
   353    353     ** was committed at the btree level). So it safe to end the transaction
   354    354     ** by manually setting the autoCommit flag to true and detaching the
   355    355     ** vacuum database. The vacuum_db journal file is deleted when the pager
   356    356     ** is closed by the DETACH.
   357    357     */
   358    358     db->autoCommit = 1;
          359  +  db->bUnlocked = 0;
   359    360   
   360    361     if( pDb ){
   361    362       sqlite3BtreeClose(pDb->pBt);
   362    363       pDb->pBt = 0;
   363    364       pDb->pSchema = 0;
   364    365     }
   365    366   

Changes to src/vdbe.c.

  2895   2895         ** is committed. 
  2896   2896         */
  2897   2897         int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
  2898   2898         if( isTransaction && p1==SAVEPOINT_RELEASE ){
  2899   2899           if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
  2900   2900             goto vdbe_return;
  2901   2901           }
         2902  +        assert( db->bUnlocked==0 );
  2902   2903           db->autoCommit = 1;
  2903   2904           if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
  2904   2905             p->pc = (int)(pOp - aOp);
  2905   2906             db->autoCommit = 0;
  2906   2907             p->rc = rc = SQLITE_BUSY;
  2907   2908             goto vdbe_return;
  2908   2909           }
................................................................................
  2966   2967         }
  2967   2968       }
  2968   2969     }
  2969   2970   
  2970   2971     break;
  2971   2972   }
  2972   2973   
  2973         -/* Opcode: AutoCommit P1 P2 * * *
         2974  +/* Opcode: AutoCommit P1 P2 P3 * *
  2974   2975   **
  2975   2976   ** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
  2976   2977   ** back any currently active btree transactions. If there are any active
  2977   2978   ** VMs (apart from this one), then a ROLLBACK fails.  A COMMIT fails if
  2978   2979   ** there are active writing VMs or active VMs that use shared cache.
         2980  +**
         2981  +** If P3 is non-zero, then this instruction is being executed as part of
         2982  +** a "BEGIN UNLOCKED" command.
  2979   2983   **
  2980   2984   ** This instruction causes the VM to halt.
  2981   2985   */
  2982   2986   case OP_AutoCommit: {
  2983   2987     int desiredAutoCommit;
  2984   2988     int iRollback;
  2985   2989     int turnOnAC;
         2990  +  int bUnlocked;
         2991  +  int hrc;
  2986   2992   
  2987   2993     desiredAutoCommit = pOp->p1;
  2988   2994     iRollback = pOp->p2;
         2995  +  bUnlocked = pOp->p3;
  2989   2996     turnOnAC = desiredAutoCommit && !db->autoCommit;
  2990   2997     assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
  2991   2998     assert( desiredAutoCommit==1 || iRollback==0 );
         2999  +  assert( desiredAutoCommit==0 || bUnlocked==0 );
         3000  +  assert( db->autoCommit==0 || db->bUnlocked==0 );
  2992   3001     assert( db->nVdbeActive>0 );  /* At least this one VM is active */
  2993   3002     assert( p->bIsReader );
  2994   3003   
  2995         -  if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){
         3004  +  if( turnOnAC && !iRollback && 
         3005  +      (db->nVdbeWrite>0 || (db->bUnlocked && db->nVdbeActive>1))
         3006  +  ){
  2996   3007       /* If this instruction implements a COMMIT and other VMs are writing
  2997   3008       ** return an error indicating that the other VMs must complete first. 
  2998   3009       */
  2999   3010       sqlite3VdbeError(p, "cannot commit transaction - "
  3000   3011                           "SQL statements in progress");
  3001   3012       rc = SQLITE_BUSY;
  3002   3013     }else if( desiredAutoCommit!=db->autoCommit ){
  3003   3014       if( iRollback ){
  3004   3015         assert( desiredAutoCommit==1 );
  3005   3016         sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
  3006   3017         db->autoCommit = 1;
         3018  +      db->bUnlocked = 0;
  3007   3019       }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
  3008   3020         goto vdbe_return;
  3009   3021       }else{
  3010   3022         db->autoCommit = (u8)desiredAutoCommit;
  3011         -      if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
         3023  +      hrc = sqlite3VdbeHalt(p);
         3024  +      if( (hrc & 0xFF)==SQLITE_BUSY ){
  3012   3025           p->pc = (int)(pOp - aOp);
  3013   3026           db->autoCommit = (u8)(1-desiredAutoCommit);
  3014         -        p->rc = rc = SQLITE_BUSY;
         3027  +        p->rc = hrc;
         3028  +        rc = SQLITE_BUSY;
  3015   3029           goto vdbe_return;
  3016   3030         }
         3031  +      db->bUnlocked = (u8)bUnlocked;
  3017   3032       }
  3018   3033       assert( db->nStatement==0 );
  3019   3034       sqlite3CloseSavepoints(db);
  3020   3035       if( p->rc==SQLITE_OK ){
  3021   3036         rc = SQLITE_DONE;
  3022   3037       }else{
  3023   3038         rc = SQLITE_ERROR;
................................................................................
  3204   3219     assert( pDb->pBt!=0 );
  3205   3220     assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
  3206   3221     pIn3 = &aMem[pOp->p3];
  3207   3222     sqlite3VdbeMemIntegerify(pIn3);
  3208   3223     /* See note about index shifting on OP_ReadCookie */
  3209   3224     rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i);
  3210   3225     if( pOp->p2==BTREE_SCHEMA_VERSION ){
  3211         -    /* When the schema cookie changes, record the new cookie internally */
  3212         -    pDb->pSchema->schema_cookie = (int)pIn3->u.i;
  3213         -    db->flags |= SQLITE_InternChanges;
         3226  +    if( db->bUnlocked ){
         3227  +      sqlite3VdbeError(p, "cannot modify database schema - "
         3228  +          "UNLOCKED transaction"
         3229  +      );
         3230  +      rc = SQLITE_ERROR;
         3231  +    }else{
         3232  +      /* When the schema cookie changes, record the new cookie internally */
         3233  +      pDb->pSchema->schema_cookie = (int)pIn3->u.i;
         3234  +      db->flags |= SQLITE_InternChanges;
         3235  +    }
  3214   3236     }else if( pOp->p2==BTREE_FILE_FORMAT ){
  3215   3237       /* Record changes in the file format */
  3216   3238       pDb->pSchema->file_format = (u8)pIn3->u.i;
  3217   3239     }
  3218   3240     if( pOp->p1==1 ){
  3219   3241       /* Invalidate all prepared statements whenever the TEMP database
  3220   3242       ** schema is changed.  Ticket #1644 */

Changes to src/vdbeaux.c.

  2016   2016     ** file is required for an atomic commit.
  2017   2017     */ 
  2018   2018     for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ 
  2019   2019       Btree *pBt = db->aDb[i].pBt;
  2020   2020       if( sqlite3BtreeIsInTrans(pBt) ){
  2021   2021         needXcommit = 1;
  2022   2022         if( i!=1 ) nTrans++;
  2023         -      sqlite3BtreeEnter(pBt);
  2024         -      rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt));
  2025         -      sqlite3BtreeLeave(pBt);
         2023  +      rc = sqlite3BtreeExclusiveLock(pBt);
  2026   2024       }
  2027   2025     }
         2026  +
         2027  +  if( db->bUnlocked && (rc & 0xFF)==SQLITE_BUSY ){
         2028  +    /* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while 
         2029  +    ** attempting to take the WRITER lock on a wal file. Release the
         2030  +    ** WRITER locks on all wal files and return early.  */
         2031  +    for(i=0; i<db->nDb; i++){
         2032  +      Btree *pBt = db->aDb[i].pBt;
         2033  +      if( sqlite3BtreeIsInTrans(pBt) ){
         2034  +        sqlite3BtreeEnter(pBt);
         2035  +        sqlite3PagerDropExclusiveLock(sqlite3BtreePager(pBt));
         2036  +        sqlite3BtreeLeave(pBt);
         2037  +      }
         2038  +    }
         2039  +  }
         2040  +
  2028   2041     if( rc!=SQLITE_OK ){
  2029   2042       return rc;
  2030   2043     }
  2031   2044   
  2032   2045     /* If there are any write-transactions at all, invoke the commit hook */
  2033   2046     if( needXcommit && db->xCommitCallback ){
  2034   2047       rc = db->xCommitCallback(db->pCommitArg);
................................................................................
  2423   2436           }else{
  2424   2437             /* We are forced to roll back the active transaction. Before doing
  2425   2438             ** so, abort any other statements this handle currently has active.
  2426   2439             */
  2427   2440             sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
  2428   2441             sqlite3CloseSavepoints(db);
  2429   2442             db->autoCommit = 1;
         2443  +          db->bUnlocked = 0;
  2430   2444             p->nChange = 0;
  2431   2445           }
  2432   2446         }
  2433   2447       }
  2434   2448   
  2435   2449       /* Check for immediate foreign key violations. */
  2436   2450       if( p->rc==SQLITE_OK ){
................................................................................
  2458   2472           }else{ 
  2459   2473             /* The auto-commit flag is true, the vdbe program was successful 
  2460   2474             ** or hit an 'OR FAIL' constraint and there are no deferred foreign
  2461   2475             ** key constraints to hold up the transaction. This means a commit 
  2462   2476             ** is required. */
  2463   2477             rc = vdbeCommit(db, p);
  2464   2478           }
  2465         -        if( rc==SQLITE_BUSY && p->readOnly ){
         2479  +        if( (rc & 0xFF)==SQLITE_BUSY && p->readOnly ){
  2466   2480             sqlite3VdbeLeave(p);
  2467         -          return SQLITE_BUSY;
         2481  +          return rc;
  2468   2482           }else if( rc!=SQLITE_OK ){
  2469   2483             p->rc = rc;
  2470   2484             sqlite3RollbackAll(db, SQLITE_OK);
  2471   2485             p->nChange = 0;
  2472   2486           }else{
  2473   2487             db->nDeferredCons = 0;
  2474   2488             db->nDeferredImmCons = 0;
................................................................................
  2485   2499           eStatementOp = SAVEPOINT_RELEASE;
  2486   2500         }else if( p->errorAction==OE_Abort ){
  2487   2501           eStatementOp = SAVEPOINT_ROLLBACK;
  2488   2502         }else{
  2489   2503           sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
  2490   2504           sqlite3CloseSavepoints(db);
  2491   2505           db->autoCommit = 1;
         2506  +        db->bUnlocked = 0;
  2492   2507           p->nChange = 0;
  2493   2508         }
  2494   2509       }
  2495   2510     
  2496   2511       /* If eStatementOp is non-zero, then a statement transaction needs to
  2497   2512       ** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
  2498   2513       ** do so. If this operation returns an error, and the current statement
................................................................................
  2506   2521             p->rc = rc;
  2507   2522             sqlite3DbFree(db, p->zErrMsg);
  2508   2523             p->zErrMsg = 0;
  2509   2524           }
  2510   2525           sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
  2511   2526           sqlite3CloseSavepoints(db);
  2512   2527           db->autoCommit = 1;
         2528  +        db->bUnlocked = 0;
  2513   2529           p->nChange = 0;
  2514   2530         }
  2515   2531       }
  2516   2532     
  2517   2533       /* If this was an INSERT, UPDATE or DELETE and no statement transaction
  2518   2534       ** has been rolled back, update the database connection change-counter. 
  2519   2535       */
................................................................................
  2550   2566     ** to invoke any required unlock-notify callbacks.
  2551   2567     */
  2552   2568     if( db->autoCommit ){
  2553   2569       sqlite3ConnectionUnlocked(db);
  2554   2570     }
  2555   2571   
  2556   2572     assert( db->nVdbeActive>0 || db->autoCommit==0 || db->nStatement==0 );
  2557         -  return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
         2573  +  return ((p->rc & 0xFF)==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
  2558   2574   }
  2559   2575   
  2560   2576   
  2561   2577   /*
  2562   2578   ** Each VDBE holds the result of the most recent sqlite3_step() call
  2563   2579   ** in p->rc.  This routine sets that result back to SQLITE_OK.
  2564   2580   */

Changes to src/wal.c.

  2353   2353     if( pWal->readLock>=0 ){
  2354   2354       walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  2355   2355       pWal->readLock = -1;
  2356   2356     }
  2357   2357   }
  2358   2358   
  2359   2359   /*
  2360         -** Search the wal file for page pgno. If found, set *piRead to the frame that
  2361         -** contains the page. Otherwise, if pgno is not in the wal file, set *piRead
  2362         -** to zero.
  2363         -**
  2364         -** Return SQLITE_OK if successful, or an error code if an error occurs. If an
  2365         -** error does occur, the final value of *piRead is undefined.
         2360  +** Search the hash tables for an entry matching page number pgno. Ignore
         2361  +** any entries that lie after frame iLast within the wal file.
  2366   2362   */
  2367         -int sqlite3WalFindFrame(
  2368         -  Wal *pWal,                      /* WAL handle */
  2369         -  Pgno pgno,                      /* Database page number to read data for */
  2370         -  u32 *piRead                     /* OUT: Frame number (or zero) */
         2363  +static int walFindFrame(
         2364  +  Wal *pWal, 
         2365  +  Pgno pgno, 
         2366  +  u32 iLast, 
         2367  +  u32 *piRead
  2371   2368   ){
  2372         -  u32 iRead = 0;                  /* If !=0, WAL frame to return data from */
  2373         -  u32 iLast = pWal->hdr.mxFrame;  /* Last page in WAL for this reader */
  2374   2369     int iHash;                      /* Used to loop through N hash tables */
         2370  +  u32 iRead = 0;
  2375   2371   
  2376         -  /* This routine is only be called from within a read transaction. */
  2377         -  assert( pWal->readLock>=0 || pWal->lockError );
  2378         -
  2379         -  /* If the "last page" field of the wal-index header snapshot is 0, then
  2380         -  ** no data will be read from the wal under any circumstances. Return early
  2381         -  ** in this case as an optimization.  Likewise, if pWal->readLock==0, 
  2382         -  ** then the WAL is ignored by the reader so return early, as if the 
  2383         -  ** WAL were empty.
  2384         -  */
  2385         -  if( iLast==0 || pWal->readLock==0 ){
  2386         -    *piRead = 0;
  2387         -    return SQLITE_OK;
  2388         -  }
  2389         -
  2390         -  /* Search the hash table or tables for an entry matching page number
  2391         -  ** pgno. Each iteration of the following for() loop searches one
         2372  +  /* Each iteration of the following for() loop searches one
  2392   2373     ** hash table (each hash table indexes up to HASHTABLE_NPAGE frames).
  2393   2374     **
  2394   2375     ** This code might run concurrently to the code in walIndexAppend()
  2395   2376     ** that adds entries to the wal-index (and possibly to this hash 
  2396   2377     ** table). This means the value just read from the hash 
  2397   2378     ** slot (aHash[iKey]) may have been added before or after the 
  2398   2379     ** current read transaction was opened. Values added after the
................................................................................
  2433   2414         }
  2434   2415         if( (nCollide--)==0 ){
  2435   2416           return SQLITE_CORRUPT_BKPT;
  2436   2417         }
  2437   2418       }
  2438   2419     }
  2439   2420   
         2421  +  *piRead = iRead;
         2422  +  return SQLITE_OK;
         2423  +}
         2424  +
         2425  +/*
         2426  +** Search the wal file for page pgno. If found, set *piRead to the frame that
         2427  +** contains the page. Otherwise, if pgno is not in the wal file, set *piRead
         2428  +** to zero.
         2429  +**
         2430  +** Return SQLITE_OK if successful, or an error code if an error occurs. If an
         2431  +** error does occur, the final value of *piRead is undefined.
         2432  +*/
         2433  +int sqlite3WalFindFrame(
         2434  +  Wal *pWal,                      /* WAL handle */
         2435  +  Pgno pgno,                      /* Database page number to read data for */
         2436  +  u32 *piRead                     /* OUT: Frame number (or zero) */
         2437  +){
         2438  +  u32 iRead = 0;                  /* If !=0, WAL frame to return data from */
         2439  +  u32 iLast = pWal->hdr.mxFrame;  /* Last page in WAL for this reader */
         2440  +  int rc;
         2441  +
         2442  +  /* This routine is only be called from within a read transaction. */
         2443  +  assert( pWal->readLock>=0 || pWal->lockError );
         2444  +
         2445  +  /* If the "last page" field of the wal-index header snapshot is 0, then
         2446  +  ** no data will be read from the wal under any circumstances. Return early
         2447  +  ** in this case as an optimization.  Likewise, if pWal->readLock==0, 
         2448  +  ** then the WAL is ignored by the reader so return early, as if the 
         2449  +  ** WAL were empty.
         2450  +  */
         2451  +  if( iLast==0 || pWal->readLock==0 ){
         2452  +    *piRead = 0;
         2453  +    return SQLITE_OK;
         2454  +  }
         2455  +
         2456  +  rc = walFindFrame(pWal, pgno, iLast, &iRead);
         2457  +
  2440   2458   #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
  2441   2459     /* If expensive assert() statements are available, do a linear search
  2442   2460     ** of the wal-index file content. Make sure the results agree with the
  2443   2461     ** result obtained using the hash indexes above.  */
  2444         -  {
         2462  +  if( rc==SQLITE_OK ){
  2445   2463       u32 iRead2 = 0;
  2446   2464       u32 iTest;
  2447   2465       for(iTest=iLast; iTest>0; iTest--){
  2448   2466         if( walFramePgno(pWal, iTest)==pgno ){
  2449   2467           iRead2 = iTest;
  2450   2468           break;
  2451   2469         }
................................................................................
  2532   2550       walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
  2533   2551       pWal->writeLock = 0;
  2534   2552       rc = SQLITE_BUSY_SNAPSHOT;
  2535   2553     }
  2536   2554   
  2537   2555     return rc;
  2538   2556   }
         2557  +
         2558  +/* 
         2559  +** TODO: Combine some code with BeginWriteTransaction()
         2560  +**
         2561  +** This function is only ever called when committing a "BEGIN UNLOCKED"
         2562  +** transaction. It may be assumed that no frames have been written to
         2563  +** the wal file.
         2564  +*/
         2565  +int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pList, PgHdr *pPage1){
         2566  +  volatile WalIndexHdr *pHead;    /* Head of the wal file */
         2567  +  int rc;
         2568  +
         2569  +  /* Cannot start a write transaction without first holding a read
         2570  +  ** transaction. */
         2571  +  assert( pWal->readLock>=0 );
         2572  +
         2573  +  if( pWal->readOnly ){
         2574  +    return SQLITE_READONLY;
         2575  +  }
         2576  +
         2577  +  /* Only one writer allowed at a time.  Get the write lock.  Return
         2578  +  ** SQLITE_BUSY if unable.
         2579  +  */
         2580  +  rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 0);
         2581  +  if( rc ){
         2582  +    return rc;
         2583  +  }
         2584  +  pWal->writeLock = 1;
         2585  +
         2586  +  /* If the database has been modified since this transaction was started,
         2587  +  ** check if it is still possible to commit. The transaction can be 
         2588  +  ** committed if:
         2589  +  **
         2590  +  **   a) None of the pages in pList have been modified since the 
         2591  +  **      transaction opened, and
         2592  +  **
         2593  +  **   b) The database schema cookie has not been modified since the
         2594  +  **      transaction was started.
         2595  +  */
         2596  +  pHead = walIndexHdr(pWal);
         2597  +  if( memcmp(&pWal->hdr, (void*)pHead, sizeof(WalIndexHdr))!=0 ){
         2598  +    /* TODO: Is this safe? Because it holds the WRITER lock this thread
         2599  +    ** has exclusive access to the live header, but might it be corrupt? */
         2600  +    PgHdr *pPg;
         2601  +    u32 iLast = pHead->mxFrame;
         2602  +    for(pPg=pList; rc==SQLITE_OK && pPg; pPg=pPg->pDirty){
         2603  +      u32 iSlot = 0;
         2604  +      rc = walFindFrame(pWal, pPg->pgno, iLast, &iSlot);
         2605  +      if( iSlot>pWal->hdr.mxFrame ){
         2606  +        sqlite3_log(SQLITE_OK,
         2607  +            "cannot commit UNLOCKED transaction (conflict at page %d)",
         2608  +            (int)pPg->pgno
         2609  +        );
         2610  +        rc = SQLITE_BUSY_SNAPSHOT;
         2611  +      }
         2612  +    }
         2613  +
         2614  +    if( rc==SQLITE_OK ){
         2615  +      /* Read the newest schema cookie from the wal file. */
         2616  +      u32 iSlot = 0;
         2617  +      rc = walFindFrame(pWal, 1, iLast, &iSlot);
         2618  +      if( rc==SQLITE_OK && iSlot>pWal->hdr.mxFrame ){
         2619  +        u8 aNew[4];
         2620  +        u8 *aOld = &((u8*)pPage1->pData)[40];
         2621  +        int sz;
         2622  +        i64 iOffset;
         2623  +        sz = pWal->hdr.szPage;
         2624  +        sz = (sz&0xfe00) + ((sz&0x0001)<<16);
         2625  +        iOffset = walFrameOffset(iSlot, sz) + WAL_FRAME_HDRSIZE + 40;
         2626  +        rc = sqlite3OsRead(pWal->pWalFd, aNew, sizeof(aNew), iOffset);
         2627  +        if( rc==SQLITE_OK && memcmp(aOld, aNew, sizeof(aNew)) ){
         2628  +          /* TODO: New error code? SQLITE_BUSY_SCHEMA. */
         2629  +          rc = SQLITE_BUSY_SNAPSHOT;
         2630  +        }
         2631  +      }
         2632  +    }
         2633  +  }
         2634  +
         2635  +  return rc;
         2636  +}
         2637  +
         2638  +/*
         2639  +** The caller holds the WRITER lock. This function returns true if a snapshot
         2640  +** upgrade is required before the transaction can be committed, or false
         2641  +** otherwise.
         2642  +*/
         2643  +int sqlite3WalCommitRequiresUpgrade(Wal *pWal){
         2644  +  assert( pWal->writeLock );
         2645  +  return memcmp(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0;
         2646  +}
  2539   2647   
  2540   2648   /*
  2541   2649   ** End a write transaction.  The commit has already been done.  This
  2542   2650   ** routine merely releases the lock.
  2543   2651   */
  2544   2652   int sqlite3WalEndWriteTransaction(Wal *pWal){
  2545   2653     if( pWal->writeLock ){
................................................................................
  2560   2668   ** returned to the caller.
  2561   2669   **
  2562   2670   ** Otherwise, if the callback function does not return an error, this
  2563   2671   ** function returns SQLITE_OK.
  2564   2672   */
  2565   2673   int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
  2566   2674     int rc = SQLITE_OK;
  2567         -  if( ALWAYS(pWal->writeLock) ){
         2675  +  if( pWal->writeLock ){
  2568   2676       Pgno iMax = pWal->hdr.mxFrame;
  2569   2677       Pgno iFrame;
  2570   2678     
  2571   2679       /* Restore the clients cache of the wal-index header to the state it
  2572   2680       ** was in before the client began writing to the database. 
  2573   2681       */
  2574   2682       memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
................................................................................
  2599   2707   /* 
  2600   2708   ** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 
  2601   2709   ** values. This function populates the array with values required to 
  2602   2710   ** "rollback" the write position of the WAL handle back to the current 
  2603   2711   ** point in the event of a savepoint rollback (via WalSavepointUndo()).
  2604   2712   */
  2605   2713   void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
  2606         -  assert( pWal->writeLock );
         2714  +  /* assert( pWal->writeLock ); */
  2607   2715     aWalData[0] = pWal->hdr.mxFrame;
  2608   2716     aWalData[1] = pWal->hdr.aFrameCksum[0];
  2609   2717     aWalData[2] = pWal->hdr.aFrameCksum[1];
  2610   2718     aWalData[3] = pWal->nCkpt;
  2611   2719   }
  2612   2720   
  2613   2721   /* 
................................................................................
  2615   2723   ** the values in the aWalData[] array. aWalData must point to an array
  2616   2724   ** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated
  2617   2725   ** by a call to WalSavepoint().
  2618   2726   */
  2619   2727   int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
  2620   2728     int rc = SQLITE_OK;
  2621   2729   
  2622         -  assert( pWal->writeLock );
         2730  +  assert( pWal->writeLock || aWalData[0]==pWal->hdr.mxFrame );
  2623   2731     assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame );
  2624   2732   
  2625   2733     if( aWalData[3]!=pWal->nCkpt ){
  2626   2734       /* This savepoint was opened immediately after the write-transaction
  2627   2735       ** was started. Right after that, the writer decided to wrap around
  2628   2736       ** to the start of the log. Update the savepoint values to match.
  2629   2737       */
................................................................................
  2779   2887     u32 iFrame;                     /* Next frame address */
  2780   2888     PgHdr *p;                       /* Iterator to run through pList with. */
  2781   2889     PgHdr *pLast = 0;               /* Last frame in list */
  2782   2890     int nExtra = 0;                 /* Number of extra copies of last page */
  2783   2891     int szFrame;                    /* The size of a single frame */
  2784   2892     i64 iOffset;                    /* Next byte to write in WAL file */
  2785   2893     WalWriter w;                    /* The writer */
         2894  +  int bUpgrade = 0;               /* True if commit requires snapshot upgrade */
  2786   2895   
  2787   2896     assert( pList );
  2788   2897     assert( pWal->writeLock );
  2789   2898   
  2790   2899     /* If this frame set completes a transaction, then nTruncate>0.  If
  2791   2900     ** nTruncate==0 then this frame set does not complete the transaction. */
  2792   2901     assert( (isCommit!=0)==(nTruncate!=0) );
................................................................................
  2793   2902   
  2794   2903   #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
  2795   2904     { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){}
  2796   2905       WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n",
  2797   2906                 pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill"));
  2798   2907     }
  2799   2908   #endif
         2909  +
         2910  +  if( isCommit ){
         2911  +    volatile WalIndexHdr *pHead = walIndexHdr(pWal);
         2912  +    if( pHead->mxFrame>pWal->hdr.mxFrame ){
         2913  +      if( memcmp((void*)&pHead[0], (void*)&pHead[1], sizeof(WalIndexHdr))!=0 ){
         2914  +        /* TODO: Deal with this case. It's quite possible, but fiddly. */
         2915  +        return SQLITE_CORRUPT_BKPT;
         2916  +      }
         2917  +      memcpy(&pWal->hdr, (void*)pHead, sizeof(WalIndexHdr));
         2918  +      if( nTruncate<pWal->hdr.nPage ){
         2919  +        /* Do not truncate the database file in this case */
         2920  +        nTruncate = pWal->hdr.nPage;
         2921  +      }
         2922  +      bUpgrade = 1;
         2923  +    }
         2924  +  }
  2800   2925   
  2801   2926     /* See if it is possible to write these frames into the start of the
  2802   2927     ** log file, instead of appending to it at pWal->hdr.mxFrame.
  2803   2928     */
  2804   2929     if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){
  2805   2930       return rc;
  2806   2931     }
................................................................................
  2941   3066       }
  2942   3067       /* If this is a commit, update the wal-index header too. */
  2943   3068       if( isCommit ){
  2944   3069         walIndexWriteHdr(pWal);
  2945   3070         pWal->iCallback = iFrame;
  2946   3071       }
  2947   3072     }
         3073  +
         3074  +  if( rc==SQLITE_OK && bUpgrade ){
         3075  +    /* If this commit required a snapshot upgrade, the pager cache is 
         3076  +    ** not currently consistent with the head of the wal file. Zeroing
         3077  +    ** Wal.hdr here forces the next transaction to reset the cache 
         3078  +    ** before beginning to read the db. */
         3079  +    memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
         3080  +  }
  2948   3081   
  2949   3082     WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
  2950   3083     return rc;
  2951   3084   }
  2952   3085   
  2953   3086   /* 
  2954   3087   ** This routine is called to implement sqlite3_wal_checkpoint() and
................................................................................
  3142   3275   ** Return true if the argument is non-NULL and the WAL module is using
  3143   3276   ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
  3144   3277   ** WAL module is using shared-memory, return false. 
  3145   3278   */
  3146   3279   int sqlite3WalHeapMemory(Wal *pWal){
  3147   3280     return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
  3148   3281   }
         3282  +
         3283  +/*
         3284  +** Return true if in a write transaction, false otherwise.
         3285  +*/
         3286  +int sqlite3WalIsInTrans(Wal *pWal){
         3287  +  return (int)pWal->writeLock;
         3288  +}
  3149   3289   
  3150   3290   #ifdef SQLITE_ENABLE_ZIPVFS
  3151   3291   /*
  3152   3292   ** If the argument is not NULL, it points to a Wal object that holds a
  3153   3293   ** read-lock. This function returns the database page-size if it is known,
  3154   3294   ** or zero if it is not (or if pWal is NULL).
  3155   3295   */

Changes to src/wal.h.

   121    121   int sqlite3WalExclusiveMode(Wal *pWal, int op);
   122    122   
   123    123   /* Return true if the argument is non-NULL and the WAL module is using
   124    124   ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
   125    125   ** WAL module is using shared-memory, return false. 
   126    126   */
   127    127   int sqlite3WalHeapMemory(Wal *pWal);
          128  +
          129  +/* Return true if the WRITER lock is held. False otherwise. */
          130  +int sqlite3WalIsInTrans(Wal *pWal);
          131  +int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pDirtyList, PgHdr *pPage1); 
          132  +int sqlite3WalCommitRequiresUpgrade(Wal *pWal);
   128    133   
   129    134   #ifdef SQLITE_ENABLE_ZIPVFS
   130    135   /* If the WAL file is not empty, return the number of bytes of content
   131    136   ** stored in each frame (i.e. the db page-size when the WAL was created).
   132    137   */
   133    138   int sqlite3WalFramesize(Wal *pWal);
   134    139   #endif
   135    140   
   136    141   #endif /* ifndef SQLITE_OMIT_WAL */
   137    142   #endif /* _WAL_H_ */

Added test/unlocked.test.

            1  +# 2015 July 26
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +
           13  +set testdir [file dirname $argv0]
           14  +source $testdir/tester.tcl
           15  +source $testdir/lock_common.tcl
           16  +set ::testprefix unlocked
           17  +
           18  +
           19  +do_execsql_test 1.0 {
           20  +  PRAGMA journal_mode = wal;
           21  +} {wal}
           22  +
           23  +do_execsql_test 1.1 {
           24  +  CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
           25  +  BEGIN UNLOCKED;
           26  +    INSERT INTO t1 VALUES(1, 'abcd');
           27  +  COMMIT;
           28  +}
           29  +
           30  +do_execsql_test 1.2 {
           31  +  SELECT * FROM t1;
           32  +} {1 abcd}
           33  +
           34  +do_execsql_test 1.3 {
           35  +  BEGIN UNLOCKED;
           36  +    INSERT INTO t1 VALUES(2, 'efgh');
           37  +  ROLLBACK;
           38  +}
           39  +
           40  +do_execsql_test 1.4 {
           41  +  SELECT * FROM t1;
           42  +} {1 abcd}
           43  +
           44  +
           45  +#-------------------------------------------------------------------------
           46  +# UNLOCKED transactions cannot do cache spills.
           47  +#
           48  +foreach {tn trans spill} {
           49  +  1 {BEGIN UNLOCKED}  0
           50  +  2 {BEGIN}           1
           51  +} {
           52  +  do_test 1.5.$tn {
           53  +    sqlite3 db2 test.db
           54  +    set walsz [file size test.db-wal]
           55  +
           56  +    execsql { PRAGMA cache_size = 10 } db2
           57  +    execsql $trans db2
           58  +    execsql {
           59  +      WITH cnt(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<50)
           60  +        INSERT INTO t1(v) SELECT randomblob(900) FROM cnt;
           61  +    } db2
           62  +
           63  +    expr {[file size test.db-wal]==$walsz}
           64  +  } [expr !$spill]
           65  +
           66  +  execsql ROLLBACK db2
           67  +  db2 close
           68  +}
           69  +
           70  +#-------------------------------------------------------------------------
           71  +# UNLOCKED transactions man not be committed while there are active
           72  +# readers.
           73  +do_execsql_test 1.6.setup {
           74  +  DROP TABLE t1;
           75  +  CREATE TABLE t1(a, b);
           76  +  INSERT INTO t1 VALUES(1, 2);
           77  +  INSERT INTO t1 VALUES(3, 4);
           78  +  INSERT INTO t1 VALUES(5, 6);
           79  +}
           80  +foreach {tn trans commit_ok} {
           81  +  1 {BEGIN UNLOCKED}  0
           82  +  2 {BEGIN}           1
           83  +} {
           84  +  do_test 1.6.$tn.1 {
           85  +    set stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
           86  +    sqlite3_step $stmt
           87  +  } SQLITE_ROW
           88  +  do_test 1.6.$tn.2 {
           89  +    execsql $trans
           90  +    execsql { INSERT INTO t1 VALUES(7, 8) }
           91  +  } {}
           92  +
           93  +  if { $commit_ok } {
           94  +    do_test 1.6.$tn.3 { catchsql COMMIT } {0 {}}
           95  +  } else {
           96  +    do_test 1.6.$tn.4 { catchsql COMMIT } {/1 {cannot commit transaction .*}/}
           97  +  }
           98  +
           99  +  sqlite3_finalize $stmt
          100  +  catchsql ROLLBACK
          101  +}
          102  +
          103  +#-------------------------------------------------------------------------
          104  +# UNLOCKED transactions may not modify the db schema.
          105  +#
          106  +foreach {tn sql} {
          107  +  1 { CREATE TABLE xx(a, b) }
          108  +  2 { DROP TABLE t1 }
          109  +} {
          110  +  do_catchsql_test 1.7.$tn.1 "
          111  +    BEGIN UNLOCKED;
          112  +    $sql
          113  +  " {1 {cannot modify database schema - UNLOCKED transaction}}
          114  +
          115  +  do_execsql_test 1.7.$tn.2 ROLLBACK
          116  +}
          117  +
          118  +
          119  +do_multiclient_test tn {
          120  +
          121  +  #-----------------------------------------------------------------------
          122  +  # 1. Start an UNLOCKED transaction using [db1].
          123  +  #
          124  +  # 2. Start and then rollback a regular transaction using [db2]. This 
          125  +  #    can be done as the ongoing [db1] transaction is UNLOCKED.
          126  +  #
          127  +  # 3. The [db1] transaction can now be committed, as [db2] has relinquished
          128  +  #    the write lock.
          129  +  #
          130  +  do_test 2.$tn.1.1 {
          131  +    sql1 { 
          132  +      PRAGMA journal_mode = wal;
          133  +      CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
          134  +      INSERT INTO t1 VALUES(1, 'one');
          135  +    }
          136  +    sql1 { 
          137  +      BEGIN UNLOCKED;
          138  +        INSERT INTO t1 VALUES(2, 'two');
          139  +    }
          140  +    code1 { sqlite3_get_autocommit db }
          141  +  } 0
          142  +
          143  +  do_test 2.$tn.1.2 {
          144  +    sql2 {
          145  +      BEGIN;
          146  +        INSERT INTO t1 VALUES(3, 'three');
          147  +      ROLLBACK;
          148  +    }
          149  +  } {}
          150  +
          151  +  do_test 2.$tn.1.3 {
          152  +    sql1 COMMIT
          153  +    sql2 { SELECT * FROM t1 }
          154  +  } {1 one 2 two}
          155  +  
          156  +  #-----------------------------------------------------------------------
          157  +  # 1. Start an UNLOCKED transaction using [db1].
          158  +  #
          159  +  # 2. Commit a transaction using [db2].
          160  +  #
          161  +  # 3. Try to commit with [db1]. Check that SQLITE_BUSY_SNAPSHOT is returned,
          162  +  #    and the transaction is not rolled back.
          163  +  #
          164  +  do_test 2.$tn.2.1 {
          165  +    sql1 {
          166  +      BEGIN UNLOCKED;
          167  +        INSERT INTO t1 VALUES(-1, 'hello world');
          168  +    }
          169  +  } {}
          170  +
          171  +  do_test 2.$tn.2.2 {
          172  +    sql2 {
          173  +      INSERT INTO t1 VALUES(3, 'three');
          174  +    }
          175  +  } {}
          176  +
          177  +  do_test 2.$tn.2.3.1 {
          178  +    set rc [catch { sql1 COMMIT } msg]
          179  +    list $rc $msg
          180  +  } {1 {database is locked}}
          181  +
          182  +  do_test 2.$tn.2.3.2 {
          183  +    code1 { list [sqlite3_extended_errcode db] [sqlite3_get_autocommit db] }
          184  +  } {SQLITE_BUSY_SNAPSHOT 0}
          185  +
          186  +  do_test 2.$tn.2.3.3 {
          187  +    sql1 {
          188  +      SELECT * FROM t1;
          189  +      ROLLBACK;
          190  +    }
          191  +  } {-1 {hello world} 1 one 2 two}
          192  +  
          193  +  #-----------------------------------------------------------------------
          194  +  # 1. Start an UNLOCKED transaction using [db1].
          195  +  #
          196  +  # 2. Open a transaction using [db2].
          197  +  #
          198  +  # 3. Try to commit with [db1]. Check that SQLITE_BUSY is returned,
          199  +  #    and the transaction is not rolled back.
          200  +  #
          201  +  # 4. Have [db2] roll its transaction back. Then check that [db1] can
          202  +  #    commit.
          203  +  #
          204  +  do_test 2.$tn.3.1 {
          205  +    sql1 {
          206  +      BEGIN UNLOCKED;
          207  +        INSERT INTO t1 VALUES(4, 'four');
          208  +    }
          209  +  } {}
          210  +
          211  +  do_test 2.$tn.3.2 {
          212  +    sql2 {
          213  +      BEGIN;
          214  +        INSERT INTO t1 VALUES(-1, 'xyz');
          215  +    }
          216  +  } {}
          217  +
          218  +  do_test 2.$tn.3.3.1 {
          219  +    set rc [catch { sql1 COMMIT } msg]
          220  +    list $rc $msg
          221  +  } {1 {database is locked}}
          222  +
          223  +  do_test 2.$tn.3.3.2 {
          224  +    code1 { list [sqlite3_extended_errcode db] [sqlite3_get_autocommit db] }
          225  +  } {SQLITE_BUSY 0}
          226  +
          227  +  do_test 2.$tn.3.3.3 {
          228  +    sql1 { SELECT * FROM t1; }
          229  +  } {1 one 2 two 3 three 4 four}
          230  +
          231  +  do_test 2.$tn.3.4 {
          232  +    sql2 ROLLBACK
          233  +    sql1 COMMIT
          234  +    sql1 { SELECT * FROM t1; }
          235  +  } {1 one 2 two 3 three 4 four}
          236  +
          237  +  #-----------------------------------------------------------------------
          238  +  # 1. Create a second table - t2.
          239  +  #
          240  +  # 2. Write to t1 with [db] and t2 with [db2].
          241  +  #
          242  +  # 3. See if it worked.
          243  +  #
          244  +  do_test 2.$tn.4.1 {
          245  +    sql1 { CREATE TABLE t2(a, b) }
          246  +  } {}
          247  +  do_test 2.$tn.4.2 {
          248  +    sql2 {
          249  +      BEGIN UNLOCKED;
          250  +        INSERT INTO t2 VALUES('i', 'n');
          251  +    }
          252  +
          253  +    sql1 {
          254  +      BEGIN UNLOCKED;
          255  +        INSERT INTO t1 VALUES(5, 'five');
          256  +      COMMIT;
          257  +    }
          258  +
          259  +    sql2 COMMIT
          260  +  } {}
          261  +
          262  +  do_test 2.$tn.4.3.1 {
          263  +    sql2 {SELECT * FROM t1}
          264  +  } {1 one 2 two 3 three 4 four 5 five}
          265  +  do_test 2.$tn.4.3.2 {
          266  +    sql1 {SELECT * FROM t1}
          267  +  } {1 one 2 two 3 three 4 four 5 five}
          268  +
          269  +  do_test 2.$tn.4.3.3 { sql2 {SELECT * FROM t2} } {i n}
          270  +  do_test 2.$tn.4.3.4 { sql1 {SELECT * FROM t2} } {i n}
          271  +
          272  +  #-----------------------------------------------------------------------
          273  +  # The "schema cookie" issue.
          274  +  #
          275  +  # 1. Begin and UNLOCKED write to "t1" using [db]
          276  +  #
          277  +  # 2. Create an index on t1 using [db2].
          278  +  #
          279  +  # 3. Attempt to commit the UNLOCKED write. This is an SQLITE_BUSY_SNAPSHOT,
          280  +  #    even though there is no page collision.
          281  +  #
          282  +  
          283  +  do_test 2.$tn.5.1 {
          284  +    sql1 {
          285  +      BEGIN UNLOCKED;
          286  +        INSERT INTO t1 VALUES(6, 'six');
          287  +    }
          288  +  } {}
          289  +
          290  +  do_test 2.$tn.5.2 {
          291  +    sql2 { CREATE INDEX i1 ON t1(v); }
          292  +  } {}
          293  +
          294  +  do_test 2.$tn.5.3 {
          295  +    list [catch { sql1 { COMMIT } } msg] $msg [sqlite3_errcode db]
          296  +  } {1 {database is locked} SQLITE_BUSY_SNAPSHOT}
          297  +
          298  +  do_test 2.$tn.5.4 {
          299  +    sql2 { PRAGMA integrity_check }
          300  +  } {ok}
          301  +  catch { sql1 ROLLBACK }
          302  +
          303  +}
          304  +
          305  +
          306  +
          307  +finish_test

Changes to tool/mkkeywordhash.c.

   258    258     { "TEMPORARY",        "TK_TEMP",         ALWAYS                 },
   259    259     { "THEN",             "TK_THEN",         ALWAYS                 },
   260    260     { "TO",               "TK_TO",           ALWAYS                 },
   261    261     { "TRANSACTION",      "TK_TRANSACTION",  ALWAYS                 },
   262    262     { "TRIGGER",          "TK_TRIGGER",      TRIGGER                },
   263    263     { "UNION",            "TK_UNION",        COMPOUND               },
   264    264     { "UNIQUE",           "TK_UNIQUE",       ALWAYS                 },
          265  +  { "UNLOCKED",         "TK_UNLOCKED",     ALWAYS                 },
   265    266     { "UPDATE",           "TK_UPDATE",       ALWAYS                 },
   266    267     { "USING",            "TK_USING",        ALWAYS                 },
   267    268     { "VACUUM",           "TK_VACUUM",       VACUUM                 },
   268    269     { "VALUES",           "TK_VALUES",       ALWAYS                 },
   269    270     { "VIEW",             "TK_VIEW",         VIEW                   },
   270    271     { "VIRTUAL",          "TK_VIRTUAL",      VTAB                   },
   271    272     { "WITH",             "TK_WITH",         CTE                    },