/ Check-in [04113557]
Login

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 Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

3342
3343
3344
3345
3346
3347
3348

3349
3350
3351

3352
3353
3354
3355
3356
3357
3358
        int exFlag = (p->db->bUnlocked && !ISAUTOVACUUM) ? -1 : (wrflag>1);
        int bSubjInMem = sqlite3TempInMemory(p->db);
        assert( p->db->bUnlocked==0 || wrflag==1 );
        rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem);
        if( rc==SQLITE_OK ){
          rc = newDatabase(pBt);
        }

        if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){
          rc = btreePtrmapAllocate(pBt);
        }

      }
    }
  
    if( rc!=SQLITE_OK ){
      unlockBtreeIfUnused(pBt);
    }
  }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&







>



>







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

Changes to src/pager.c.

1742
1743
1744
1745
1746
1747
1748
1749
1750


1751

1752
1753
1754
1755
1756
1757
1758
....
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035










3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
....
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
....
4448
4449
4450
4451
4452
4453
4454

4455

4456
4457
4458
4459
4460
4461
4462
....
5293
5294
5295
5296
5297
5298
5299

5300
5301
5302
5303

5304
5305
5306
5307
5308
5309
5310
....
5602
5603
5604
5605
5606
5607
5608

5609

5610
5611
5612
5613
5614
5615
5616
....
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634




5635
5636
5637
5638
5639
5640
5641
....
5934
5935
5936
5937
5938
5939
5940

5941
5942
5943

5944
5945
5946
5947
5948
5949
5950
....
6084
6085
6086
6087
6088
6089
6090
6091
6092
6093
6094
6095

6096
6097
6098
6099









6100
6101
6102
6103
6104
6105
6106
6107
6108
6109


6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123

6124
6125
6126













6127
6128
6129
6130
6131
6132
6133
....
6136
6137
6138
6139
6140
6141
6142



6143
6144







6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162

6163
6164
6165
6166
6167
6168
6169
}

/*
** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
*/
static void pagerFreeBitvecs(Pager *pPager){
  sqlite3BitvecDestroy(pPager->pInJournal);
  sqlite3BitvecDestroy(pPager->pAllRead);
  pPager->pInJournal = 0;


  pPager->pAllRead = 0;

}

/*
** This function is a no-op if the pager is in exclusive mode and not
** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
** state.
**
................................................................................

/*
** This function is called to rollback a transaction on a WAL database.
*/
static int pagerRollbackWal(Pager *pPager){
  int rc;                         /* Return Code */
  PgHdr *pList;                   /* List of dirty pages to revert */
  int bPage1 = 0;                 /* True if page 1 has been undone */

  /* For all pages in the cache that are currently dirty or have already
  ** been written (but not committed) to the log file, do one of the 
  ** following:
  **
  **   + Discard the cached page (if refcount==0), or
  **   + Reload page content from the database (if refcount>0).
  */
  pPager->dbSize = pPager->dbOrigSize;
  rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
  pList = sqlite3PcacheDirtyList(pPager->pPCache);










  while( pList && rc==SQLITE_OK ){
    PgHdr *pNext = pList->pDirty;
    if( pList->pgno==1 ) bPage1 = 1;
    rc = pagerUndoCallback((void *)pPager, pList->pgno);
    pList = pNext;
  }

  /* If this is an UNLOCKED transaction, then page 1 must be reread from the
  ** db file, even if it is not dirty. This is because the b-tree layer may
  ** have already zeroed the nFree and iTrunk header fields.  */
  if( rc==SQLITE_OK && bPage1==0 && pPager->pAllRead ){
    rc = pagerUndoCallback((void*)pPager, 1);
  }

  return rc;
}

/*
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),
** this function notifies any active backup processes that the pages have
................................................................................
  if( isCommit ){
    /* If a WAL transaction is being committed, there is no point in writing
    ** any pages with page numbers greater than nTruncate into the WAL file.
    ** They will never be read by any client. So remove them from the pDirty
    ** list here. */
    PgHdr **ppNext = &pList;
    nList = 0;

    for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
      if( p->pgno<=nTruncate ){
        ppNext = &p->pDirty;
        nList++;
      }
    }
    assert( pList );
................................................................................
    return SQLITE_OK;
  }

  pPg->pDirty = 0;
  if( pagerUseWal(pPager) ){
    /* If the transaction is a "BEGIN UNLOCKED" transaction, the page 
    ** cannot be flushed to disk. Return early in this case. */

    if( pPager->pAllRead ) return SQLITE_OK;


    /* Write a single frame for this page to the log. */
    rc = subjournalPageIfRequired(pPg); 
    if( rc==SQLITE_OK ){
      rc = pagerWalFrames(pPager, pPg, 0, 0);
    }
  }else{
................................................................................
    return SQLITE_CORRUPT_BKPT;
  }
  pPager->hasBeenUsed = 1;

  /* If this is an UNLOCKED transaction and the page being read was
  ** present in the database file when the transaction was opened,
  ** mark it as read in the pAllRead vector.  */

  if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
    rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;
  }


  /* If the pager is in the error state, return an error immediately. 
  ** Otherwise, request the page from the PCache layer. */
  if( pPager->errCode!=SQLITE_OK ){
    rc = pPager->errCode;
  }else{
    if( bMmapOk && pagerUseWal(pPager) ){
................................................................................

  if( pPager->errCode ) return pPager->errCode;
  assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
  pPager->subjInMemory = (u8)subjInMemory;

  if( ALWAYS(pPager->eState==PAGER_READER) ){
    assert( pPager->pInJournal==0 );

    assert( pPager->pAllRead==0 );


    if( pagerUseWal(pPager) ){
      /* If the pager is configured to use locking_mode=exclusive, and an
      ** exclusive lock on the database is not already held, obtain it now.
      */
      if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
        rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
................................................................................
      }

      /* Grab the write lock on the log file. If successful, upgrade to
      ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
      ** The busy-handler is not invoked if another connection already
      ** holds the write-lock. If possible, the upper layer will call it.
      */
      if( exFlag>=0 ){
        rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
      }else{
        pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize);
        if( pPager->pAllRead==0 ){
          rc = SQLITE_NOMEM;
        }




      }
    }else{
      /* Obtain a RESERVED lock on the database file. If the exFlag parameter
      ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
      ** busy-handler callback can be used when upgrading to the EXCLUSIVE
      ** lock, but not when obtaining the RESERVED lock.
      */
................................................................................
}

/*
** Return TRUE if the page given in the argument was previously passed
** to sqlite3PagerWrite().  In other words, return TRUE if it is ok
** to change the content of the page.
*/

int sqlite3PagerIswriteable(DbPage *pPg){
  return pPg->flags & PGHDR_WRITEABLE;
}


/*
** A call to this routine tells the pager that it is not necessary to
** write the information on page pPg back to the disk, even though
** that page might be marked as dirty.  This happens, for example, when
** the page has been added as a leaf of the freelist and so its
** content no longer matters.
................................................................................
    assert( !MEMDB );
    rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
  }
  return rc;
}

/*
** This function may only be called while a write-transaction is active in
** rollback. If the connection is in WAL mode, this call is a no-op. 
** Otherwise, if the connection does not already have an EXCLUSIVE lock on 
** the database file, an attempt is made to obtain one.
**

** If the EXCLUSIVE lock is already held or the attempt to obtain it is
** successful, or the connection is in WAL mode, SQLITE_OK is returned.
** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is 
** returned.









*/
int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1){
  int rc = SQLITE_OK;
  assert( pPager->eState==PAGER_WRITER_CACHEMOD 
       || pPager->eState==PAGER_WRITER_DBMOD 
       || pPager->eState==PAGER_WRITER_LOCKED 
  );
  assert( assert_pager_state(pPager) );
  if( 0==pagerUseWal(pPager) ){
    rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);


  }else{
    if( pPager->pAllRead ){
      /* This is an UNLOCKED transaction. Attempt to lock the wal database
      ** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
      ** invoke the busy-handler and try again for as long as it returns
      ** non-zero.  */
      do {
        /* rc = sqlite3WalBeginWriteTransaction(pWal); */
        rc = sqlite3WalLockForCommit(pPager->pWal, pPage1, pPager->pAllRead);
      }while( rc==SQLITE_BUSY 
           && pPager->xBusyHandler(pPager->pBusyHandlerArg) 
      );
    }
  }

  return rc;
}














int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1){
  int rc;
  u32 iFrame = 0;

  assert( pPager->pWal && pPager->pAllRead );
  rc = sqlite3WalUpgradeSnapshot(pPager->pWal);
  if( rc==SQLITE_OK ){
................................................................................
  if( rc==SQLITE_OK ){
    rc = readDbPage(pPage1, iFrame);
  }

  return rc;
}




void sqlite3PagerSetDbsize(Pager *pPager, Pgno nFinal){
  pPager->dbSize = nFinal;







}

/*
** If this is a WAL mode connection and the WRITER lock is currently held,
** relinquish it.
*/
void sqlite3PagerDropExclusiveLock(Pager *pPager){
  if( pagerUseWal(pPager) ){
    sqlite3WalEndWriteTransaction(pPager->pWal);
  }
}

/*
** Return true if this pager is currently within an UNLOCKED transaction.
*/
int sqlite3PagerIsUnlocked(Pager *pPager){
  return pPager->pAllRead!=0;
}



/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
** journal file. zMaster may be NULL, which is interpreted as no master
** journal (a single database transaction).







<

>
>

>







 







<











>
>
>
>
>
>
>
>
>
>


<




<
<
<
<
<
<
<







 







<







 







>

>







 







>




>







 







>

>







 







|
|
<




>
>
>
>







 







>



>







 







|
|
|
<

>
|
|
|
|
>
>
>
>
>
>
>
>
>










>
>
|






<






>



>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
|
|
>
>
>
>
>
>
>











<
<
<
<
<
<
<
>







1742
1743
1744
1745
1746
1747
1748

1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
....
3019
3020
3021
3022
3023
3024
3025

3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048

3049
3050
3051
3052







3053
3054
3055
3056
3057
3058
3059
....
3085
3086
3087
3088
3089
3090
3091

3092
3093
3094
3095
3096
3097
3098
....
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
....
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
....
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
....
5629
5630
5631
5632
5633
5634
5635
5636
5637

5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
....
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
....
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106

6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140

6141
6142
6143
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
....
6173
6174
6175
6176
6177
6178
6179
6180
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202







6203
6204
6205
6206
6207
6208
6209
6210
}

/*
** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
*/
static void pagerFreeBitvecs(Pager *pPager){
  sqlite3BitvecDestroy(pPager->pInJournal);

  pPager->pInJournal = 0;
#ifdef SQLITE_ENABLE_UNLOCKED
  sqlite3BitvecDestroy(pPager->pAllRead);
  pPager->pAllRead = 0;
#endif
}

/*
** This function is a no-op if the pager is in exclusive mode and not
** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
** state.
**
................................................................................

/*
** This function is called to rollback a transaction on a WAL database.
*/
static int pagerRollbackWal(Pager *pPager){
  int rc;                         /* Return Code */
  PgHdr *pList;                   /* List of dirty pages to revert */


  /* For all pages in the cache that are currently dirty or have already
  ** been written (but not committed) to the log file, do one of the 
  ** following:
  **
  **   + Discard the cached page (if refcount==0), or
  **   + Reload page content from the database (if refcount>0).
  */
  pPager->dbSize = pPager->dbOrigSize;
  rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
  pList = sqlite3PcacheDirtyList(pPager->pPCache);

  /* If this is an UNLOCKED transaction, then page 1 must be reread from 
  ** the db file, even if it is not dirty. This is because the b-tree layer 
  ** may have already zeroed the nFree and iTrunk header fields.  */
#ifdef SQLITE_ENABLE_UNLOCKED
  if( rc==SQLITE_OK && (pList==0 || pList->pgno!=1) && pPager->pAllRead ){
    rc = pagerUndoCallback((void*)pPager, 1);
  }
#endif

  while( pList && rc==SQLITE_OK ){
    PgHdr *pNext = pList->pDirty;

    rc = pagerUndoCallback((void *)pPager, pList->pgno);
    pList = pNext;
  }








  return rc;
}

/*
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),
** this function notifies any active backup processes that the pages have
................................................................................
  if( isCommit ){
    /* If a WAL transaction is being committed, there is no point in writing
    ** any pages with page numbers greater than nTruncate into the WAL file.
    ** They will never be read by any client. So remove them from the pDirty
    ** list here. */
    PgHdr **ppNext = &pList;
    nList = 0;

    for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
      if( p->pgno<=nTruncate ){
        ppNext = &p->pDirty;
        nList++;
      }
    }
    assert( pList );
................................................................................
    return SQLITE_OK;
  }

  pPg->pDirty = 0;
  if( pagerUseWal(pPager) ){
    /* If the transaction is a "BEGIN UNLOCKED" transaction, the page 
    ** cannot be flushed to disk. Return early in this case. */
#ifdef SQLITE_ENABLE_UNLOCKED
    if( pPager->pAllRead ) return SQLITE_OK;
#endif

    /* Write a single frame for this page to the log. */
    rc = subjournalPageIfRequired(pPg); 
    if( rc==SQLITE_OK ){
      rc = pagerWalFrames(pPager, pPg, 0, 0);
    }
  }else{
................................................................................
    return SQLITE_CORRUPT_BKPT;
  }
  pPager->hasBeenUsed = 1;

  /* If this is an UNLOCKED transaction and the page being read was
  ** present in the database file when the transaction was opened,
  ** mark it as read in the pAllRead vector.  */
#ifdef SQLITE_ENABLE_UNLOCKED
  if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
    rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;
  }
#endif

  /* If the pager is in the error state, return an error immediately. 
  ** Otherwise, request the page from the PCache layer. */
  if( pPager->errCode!=SQLITE_OK ){
    rc = pPager->errCode;
  }else{
    if( bMmapOk && pagerUseWal(pPager) ){
................................................................................

  if( pPager->errCode ) return pPager->errCode;
  assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
  pPager->subjInMemory = (u8)subjInMemory;

  if( ALWAYS(pPager->eState==PAGER_READER) ){
    assert( pPager->pInJournal==0 );
#ifdef SQLITE_ENABLE_UNLOCKED
    assert( pPager->pAllRead==0 );
#endif

    if( pagerUseWal(pPager) ){
      /* If the pager is configured to use locking_mode=exclusive, and an
      ** exclusive lock on the database is not already held, obtain it now.
      */
      if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
        rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
................................................................................
      }

      /* Grab the write lock on the log file. If successful, upgrade to
      ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
      ** The busy-handler is not invoked if another connection already
      ** holds the write-lock. If possible, the upper layer will call it.
      */
#ifdef SQLITE_ENABLE_UNLOCKED
      if( exFlag<0 ){

        pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize);
        if( pPager->pAllRead==0 ){
          rc = SQLITE_NOMEM;
        }
      }else
#endif
      {
        rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
      }
    }else{
      /* Obtain a RESERVED lock on the database file. If the exFlag parameter
      ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
      ** busy-handler callback can be used when upgrading to the EXCLUSIVE
      ** lock, but not when obtaining the RESERVED lock.
      */
................................................................................
}

/*
** Return TRUE if the page given in the argument was previously passed
** to sqlite3PagerWrite().  In other words, return TRUE if it is ok
** to change the content of the page.
*/
#if defined(SQLITE_ENABLE_UNLOCKED) || !defined(NDEBUG)
int sqlite3PagerIswriteable(DbPage *pPg){
  return pPg->flags & PGHDR_WRITEABLE;
}
#endif

/*
** A call to this routine tells the pager that it is not necessary to
** write the information on page pPg back to the disk, even though
** that page might be marked as dirty.  This happens, for example, when
** the page has been added as a leaf of the freelist and so its
** content no longer matters.
................................................................................
    assert( !MEMDB );
    rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
  }
  return rc;
}

/*
** This function is called to ensure that all locks required to commit the
** current write-transaction to the database file are held. If the db is
** in rollback mode, this means the EXCLUSIVE lock on the database file.

**
** Or, if this is a non-UNLOCKED transaction on a wal-mode database, this
** function is a no-op.
**
** If this is an UNLOCKED transaction on a wal-mode database, this function
** attempts to obtain the WRITER lock on the wal file and also checks to
** see that the transaction can be safely committed (does not commit with 
** any other transaction committed since it was opened).
**
** If the required locks are already held or successfully obtained and
** the transaction can be committed, SQLITE_OK is returned. If a required lock
** cannot be obtained, SQLITE_BUSY is returned. Or, if the current transaction
** is UNLOCKED and cannot be committed due to a conflict, SQLITE_BUSY_SNAPSHOT
** is returned. Otherwise, if some other error occurs (IO error, OOM etc.),
** and SQLite error code is returned.
*/
int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1){
  int rc = SQLITE_OK;
  assert( pPager->eState==PAGER_WRITER_CACHEMOD 
       || pPager->eState==PAGER_WRITER_DBMOD 
       || pPager->eState==PAGER_WRITER_LOCKED 
  );
  assert( assert_pager_state(pPager) );
  if( 0==pagerUseWal(pPager) ){
    rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  }
#ifdef SQLITE_ENABLE_UNLOCKED
  else{
    if( pPager->pAllRead ){
      /* This is an UNLOCKED transaction. Attempt to lock the wal database
      ** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
      ** invoke the busy-handler and try again for as long as it returns
      ** non-zero.  */
      do {

        rc = sqlite3WalLockForCommit(pPager->pWal, pPage1, pPager->pAllRead);
      }while( rc==SQLITE_BUSY 
           && pPager->xBusyHandler(pPager->pBusyHandlerArg) 
      );
    }
  }
#endif
  return rc;
}

#ifdef SQLITE_ENABLE_UNLOCKED
/*
** This function is called as part of committing an UNLOCKED transaction.
** At this point the wal WRITER lock is held, and all pages in the cache 
** except for page 1 are compatible with the snapshot at the head of the
** wal file. 
**
** This function updates the in-memory data structures and reloads the
** contents of page 1 so that the client is operating on the snapshot 
** at the head of the wal file.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1){
  int rc;
  u32 iFrame = 0;

  assert( pPager->pWal && pPager->pAllRead );
  rc = sqlite3WalUpgradeSnapshot(pPager->pWal);
  if( rc==SQLITE_OK ){
................................................................................
  if( rc==SQLITE_OK ){
    rc = readDbPage(pPage1, iFrame);
  }

  return rc;
}

/*
** Set the in-memory cache of the database file size to nSz pages.
*/
void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
  pPager->dbSize = nSz;
}

/*
** Return true if this pager is currently within an UNLOCKED transaction.
*/
int sqlite3PagerIsUnlocked(Pager *pPager){
  return pPager->pAllRead!=0;
}

/*
** If this is a WAL mode connection and the WRITER lock is currently held,
** relinquish it.
*/
void sqlite3PagerDropExclusiveLock(Pager *pPager){
  if( pagerUseWal(pPager) ){
    sqlite3WalEndWriteTransaction(pPager->pWal);
  }
}







#endif   /* ifdef SQLITE_ENABLE_UNLOCKED */


/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
** journal file. zMaster may be NULL, which is interpreted as no master
** journal (a single database transaction).

Changes to src/test_config.c.

568
569
570
571
572
573
574






575
576
577
578
579
580
581
#endif

#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
  Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY);
#endif







#ifdef SQLITE_OMIT_UTF16
  Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY);
#endif








>
>
>
>
>
>







568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
#endif

#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
  Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_ENABLE_UNLOCKED
  Tcl_SetVar2(interp, "sqlite_options", "unlocked", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "unlocked", "0", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_OMIT_UTF16
  Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY);
#endif

Changes to src/vdbeaux.c.

2021
2022
2023
2024
2025
2026
2027

2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040

2041
2042
2043
2044
2045
2046
2047
    if( sqlite3BtreeIsInTrans(pBt) ){
      needXcommit = 1;
      if( i!=1 ) nTrans++;
      rc = sqlite3BtreeExclusiveLock(pBt);
    }
  }


  if( db->bUnlocked && (rc & 0xFF)==SQLITE_BUSY ){
    /* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while 
    ** attempting to take the WRITER lock on a wal file. Release the
    ** WRITER locks on all wal files and return early.  */
    for(i=0; i<db->nDb; i++){
      Btree *pBt = db->aDb[i].pBt;
      if( sqlite3BtreeIsInTrans(pBt) ){
        sqlite3BtreeEnter(pBt);
        sqlite3PagerDropExclusiveLock(sqlite3BtreePager(pBt));
        sqlite3BtreeLeave(pBt);
      }
    }
  }


  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* If there are any write-transactions at all, invoke the commit hook */
  if( needXcommit && db->xCommitCallback ){







>













>







2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
    if( sqlite3BtreeIsInTrans(pBt) ){
      needXcommit = 1;
      if( i!=1 ) nTrans++;
      rc = sqlite3BtreeExclusiveLock(pBt);
    }
  }

#ifdef SQLITE_ENABLE_UNLOCKED
  if( db->bUnlocked && (rc & 0xFF)==SQLITE_BUSY ){
    /* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while 
    ** attempting to take the WRITER lock on a wal file. Release the
    ** WRITER locks on all wal files and return early.  */
    for(i=0; i<db->nDb; i++){
      Btree *pBt = db->aDb[i].pBt;
      if( sqlite3BtreeIsInTrans(pBt) ){
        sqlite3BtreeEnter(pBt);
        sqlite3PagerDropExclusiveLock(sqlite3BtreePager(pBt));
        sqlite3BtreeLeave(pBt);
      }
    }
  }
#endif

  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* If there are any write-transactions at all, invoke the commit hook */
  if( needXcommit && db->xCommitCallback ){

Changes to src/wal.c.

2562
2563
2564
2565
2566
2567
2568
2569
2570


2571























2572
2573
2574























2575
2576
2577
2578
2579
2580
2581
....
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673

2674




2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
....
2702
2703
2704
2705
2706
2707
2708

2709
2710
2711
2712
2713
2714
2715
      pWal->writeLock = 0;
      rc = SQLITE_BUSY_SNAPSHOT;
    }
  }
  return rc;
}

/* 
** TODO: Combine some code with BeginWriteTransaction()


**























** This function is only ever called when committing a "BEGIN UNLOCKED"
** transaction. It may be assumed that no frames have been written to
** the wal file.























*/
int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPage1, Bitvec *pAllRead){
  Pager *pPager = pPage1->pPager;
  int rc = walWriteLock(pWal);

  /* If the database has been modified since this transaction was started,
  ** check if it is still possible to commit. The transaction can be 
................................................................................
    }
  }

  return rc;
}

/*
** This function is called by a writer that has a read-lock on aReadmark[0]
** (pWal->readLock==0). This function relinquishes that lock and takes a
** lock on a different aReadmark[] slot. 

**




** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
static int walUpgradeReadlock(Wal *pWal){
  int cnt;
  int rc;
  assert( pWal->writeLock && pWal->readLock==0 );
  walUnlockShared(pWal, WAL_READ_LOCK(0));
  pWal->readLock = -1;
  cnt = 0;
  do{
    int notUsed;
    rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
  }while( rc==WAL_RETRY );
  assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
  testcase( (rc&0xff)==SQLITE_IOERR );
  testcase( rc==SQLITE_PROTOCOL );
  testcase( rc==SQLITE_OK );
  return rc;
}

int sqlite3WalUpgradeSnapshot(Wal *pWal){
  int rc = SQLITE_OK;
  assert( pWal->writeLock );
  memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));

  /* If this client has its read-lock on slot aReadmark[0] and the entire
  ** wal has not been checkpointed, switch it to a different slot. Otherwise
................................................................................
  ** any reads performed between now and committing the transaction will
  ** read from the old snapshot - not the one just upgraded to.  */
  if( pWal->readLock==0 && pWal->hdr.mxFrame!=walCkptInfo(pWal)->nBackfill ){
    rc = walUpgradeReadlock(pWal);
  }
  return rc;
}


/*
** End a write transaction.  The commit has already been done.  This
** routine merely releases the lock.
*/
int sqlite3WalEndWriteTransaction(Wal *pWal){
  if( pWal->writeLock ){







|
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
|
>

>
>
>
>


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>







2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
....
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729


















2730
2731
2732
2733
2734
2735
2736
....
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
      pWal->writeLock = 0;
      rc = SQLITE_BUSY_SNAPSHOT;
    }
  }
  return rc;
}

/*
** This function is called by a writer that has a read-lock on aReadmark[0]
** (pWal->readLock==0). This function relinquishes that lock and takes a
** lock on a different aReadmark[] slot. 
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
static int walUpgradeReadlock(Wal *pWal){
  int cnt;
  int rc;
  assert( pWal->writeLock && pWal->readLock==0 );
  walUnlockShared(pWal, WAL_READ_LOCK(0));
  pWal->readLock = -1;
  cnt = 0;
  do{
    int notUsed;
    rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
  }while( rc==WAL_RETRY );
  assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
  testcase( (rc&0xff)==SQLITE_IOERR );
  testcase( rc==SQLITE_PROTOCOL );
  testcase( rc==SQLITE_OK );
  return rc;
}


#ifdef SQLITE_ENABLE_UNLOCKED
/* 
** This function is only ever called when committing a "BEGIN UNLOCKED"
** transaction. It may be assumed that no frames have been written to
** the wal file. The second parameter is a pointer to the in-memory 
** representation of page 1 of the database (which may or may not be
** dirty). The third is a bitvec with a bit set for each page in the
** database file that was read by the current unlocked transaction.
**
** This function performs three tasks:
**
**   1) It obtains the WRITER lock on the wal file,
**
**   2) It checks that there are no conflicts between the current
**      transaction and any transactions committed to the wal file since
**      it was opened, and
**
**   3) It ejects any non-dirty pages from the page-cache that have been
**      written by another client since the UNLOCKED transaction was started
**      (so as to avoid ending up with an inconsistent cache after the
**      current transaction is committed).
**
** If no error occurs and the caller may proceed with committing the 
** transaction, SQLITE_OK is returned. SQLITE_BUSY is returned if the WRITER
** lock cannot be obtained. Or, if the WRITER lock can be obtained but there
** are conflicts with a committed transaction, SQLITE_BUSY_SNAPSHOT. Finally,
** if an error (i.e. an OOM condition or IO error), an SQLite error code
** is returned.
*/
int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPage1, Bitvec *pAllRead){
  Pager *pPager = pPage1->pPager;
  int rc = walWriteLock(pWal);

  /* If the database has been modified since this transaction was started,
  ** check if it is still possible to commit. The transaction can be 
................................................................................
    }
  }

  return rc;
}

/*
** This function is called as part of committing an UNLOCKED transaction.
** It is assumed that sqlite3WalLockForCommit() has already been successfully
** called and so (a) the WRITER lock is held and (b) it is known that the
** wal-index-header stored in shared memory is not corrupt.
**
** Before returning, this function upgrades the client so that it is 
** operating on the database snapshot currently at the head of the wal file
** (even if the UNLOCKED transaction ran against an older snapshot).
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/


















int sqlite3WalUpgradeSnapshot(Wal *pWal){
  int rc = SQLITE_OK;
  assert( pWal->writeLock );
  memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));

  /* If this client has its read-lock on slot aReadmark[0] and the entire
  ** wal has not been checkpointed, switch it to a different slot. Otherwise
................................................................................
  ** any reads performed between now and committing the transaction will
  ** read from the old snapshot - not the one just upgraded to.  */
  if( pWal->readLock==0 && pWal->hdr.mxFrame!=walCkptInfo(pWal)->nBackfill ){
    rc = walUpgradeReadlock(pWal);
  }
  return rc;
}
#endif   /* SQLITE_ENABLE_UNLOCKED */

/*
** End a write transaction.  The commit has already been done.  This
** routine merely releases the lock.
*/
int sqlite3WalEndWriteTransaction(Wal *pWal){
  if( pWal->writeLock ){

Changes to test/unlocked.test.

11
12
13
14
15
16
17




18
19
20
21
22
23
24
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix unlocked






do_execsql_test 1.0 {
  PRAGMA journal_mode = wal;
} {wal}

do_execsql_test 1.1 {
  CREATE TABLE t1(k INTEGER PRIMARY KEY, v);







>
>
>
>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix unlocked

ifcapable !unlocked {
  finish_test
  return
}

do_execsql_test 1.0 {
  PRAGMA journal_mode = wal;
} {wal}

do_execsql_test 1.1 {
  CREATE TABLE t1(k INTEGER PRIMARY KEY, v);

Changes to test/unlocked2.test.

11
12
13
14
15
16
17




18
19
20
21
22
23
24
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix unlocked2






do_multiclient_test tn {

  do_test 1.$tn.1 {
    sql1 {
      PRAGMA journal_mode = wal;
      CREATE TABLE t1(x);







>
>
>
>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix unlocked2

ifcapable !unlocked {
  finish_test
  return
}

do_multiclient_test tn {

  do_test 1.$tn.1 {
    sql1 {
      PRAGMA journal_mode = wal;
      CREATE TABLE t1(x);

Changes to test/unlocked3.test.

15
16
17
18
19
20
21




22
23
24
25
26
27
28

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix unlocked3

if {$AUTOVACUUM} { finish_test ; return }





proc create_schema {} {
  db eval {
    PRAGMA journal_mode = wal;

    CREATE TABLE t1(x, y);
    CREATE TABLE t2(x, y);







>
>
>
>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix unlocked3

if {$AUTOVACUUM} { finish_test ; return }
ifcapable !unlocked {
  finish_test
  return
}

proc create_schema {} {
  db eval {
    PRAGMA journal_mode = wal;

    CREATE TABLE t1(x, y);
    CREATE TABLE t2(x, y);