SQLite

Check-in [49263c9136]
Login

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

Overview
Comment:Fix a problem in SQLITE_DIRECT_OVERFLOW_READ builds that could allow a concurrent transaction to be committed even if it read from an overflow page that was modified concurrently, in cases where the overflow page was written without also writing the b-tree page to which it is linked.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | begin-concurrent
Files: files | file ages | folders
SHA3-256: 49263c9136c81638833aa71c9d590e318ead2ca60c4d7207ebf8884174df9c8f
User & Date: dan 2024-03-29 17:58:51.554
References
2024-03-29
18:16
Fix handling of an OOM case broken by [49263c91]. (check-in: 9e6b64decb user: dan tags: begin-concurrent)
Context
2024-06-04
15:33
Fix a problem in SQLITE_DIRECT_OVERFLOW_READ builds that could allow a concurrent transaction to be committed even if it read from an overflow page that was modified concurrently, in cases where the overflow page was written without also writing the b-tree page to which it is linked. (check-in: f1d17258f4 user: dan tags: bedrock-3.45)
2024-03-29
18:16
Fix handling of an OOM case broken by [49263c91]. (check-in: 9e6b64decb user: dan tags: begin-concurrent)
18:07
Fix a problem in SQLITE_DIRECT_OVERFLOW_READ builds that could allow a concurrent transaction to be committed even if it read from an overflow page that was modified concurrently, in cases where the overflow page was written without also writing the b-tree page to which it is linked. (check-in: 11015b4ed1 user: dan tags: bedrock)
17:58
Fix a problem in SQLITE_DIRECT_OVERFLOW_READ builds that could allow a concurrent transaction to be committed even if it read from an overflow page that was modified concurrently, in cases where the overflow page was written without also writing the b-tree page to which it is linked. (check-in: 49263c9136 user: dan tags: begin-concurrent)
17:32
Improve the log message emitted when a BEGIN CONCURRENT transaction cannot be committed due to conflicts so that it identifies the conflicting table in a few more cases. (check-in: 5d30e362cf user: dan tags: begin-concurrent)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
5667
5668
5669
5670
5671
5672
5673


5674
5675
5676
5677
5678
5679
5680
         && &pBuf[-4]>=pBufStart                               /* (6) */
        ){
          sqlite3_file *fd = sqlite3PagerFile(pBt->pPager);
          u8 aSave[4];
          u8 *aWrite = &pBuf[-4];
          assert( aWrite>=pBufStart );                         /* due to (6) */
          memcpy(aSave, aWrite, 4);


          rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
          nextPage = get4byte(aWrite);
          memcpy(aWrite, aSave, 4);
        }else
#endif

        {







>
>







5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
         && &pBuf[-4]>=pBufStart                               /* (6) */
        ){
          sqlite3_file *fd = sqlite3PagerFile(pBt->pPager);
          u8 aSave[4];
          u8 *aWrite = &pBuf[-4];
          assert( aWrite>=pBufStart );                         /* due to (6) */
          memcpy(aSave, aWrite, 4);
          rc = sqlite3PagerUsePage(pBt->pPager, nextPage);
          if( rc!=SQLITE_OK ) break;
          rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
          nextPage = get4byte(aWrite);
          memcpy(aWrite, aSave, 4);
        }else
#endif

        {
Changes to src/pager.c.
5517
5518
5519
5520
5521
5522
5523

















5524
5525
5526
5527
5528
5529
5530
static void pagerUnlockIfUnused(Pager *pPager){
  if( sqlite3PcacheRefCount(pPager->pPCache)==0 ){
    assert( pPager->nMmapOut==0 ); /* because page1 is never memory mapped */
    pagerUnlockAndRollback(pPager);
  }
}


















/*
** The page getter methods each try to acquire a reference to a
** page with page number pgno. If the requested reference is
** successfully obtained, it is copied to *ppPage and SQLITE_OK returned.
**
** There are different implementations of the getter method depending
** on the current state of the pager.







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







5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
static void pagerUnlockIfUnused(Pager *pPager){
  if( sqlite3PcacheRefCount(pPager->pPCache)==0 ){
    assert( pPager->nMmapOut==0 ); /* because page1 is never memory mapped */
    pagerUnlockAndRollback(pPager);
  }
}

#ifndef SQLITE_OMIT_CONCURRENT
/*
** If this pager is currently in a concurrent transaction (pAllRead!=0),
** then set the bit in the pAllRead vector to indicate that the transaction
** read from page pgno. Return SQLITE_OK if successful, or an SQLite error
** code (i.e. SQLITE_NOMEM) if an error occurs.
*/
int sqlite3PagerUsePage(Pager *pPager, Pgno pgno){
  int rc = SQLITE_OK;
  if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
    PAGERTRACE(("USING page %d\n", pgno));
    rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
  }
  return rc;
}
#endif

/*
** The page getter methods each try to acquire a reference to a
** page with page number pgno. If the requested reference is
** successfully obtained, it is copied to *ppPage and SQLITE_OK returned.
**
** There are different implementations of the getter method depending
** on the current state of the pager.
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600

5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
  sqlite3_pcache_page *pBase;

  assert( pPager->errCode==SQLITE_OK );
  assert( pPager->eState>=PAGER_READER );
  assert( assert_pager_state(pPager) );
  assert( pPager->hasHeldSharedLock==1 );

#ifndef SQLITE_OMIT_CONCURRENT
  /* If this is an CONCURRENT 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.  */

  pPg = 0;
  if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
    PAGERTRACE(("USING page %d\n", pgno));
    rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;
  }
#endif

  if( pgno==0 ) return SQLITE_CORRUPT_BKPT;
  pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
  if( pBase==0 ){
    pPg = 0;
    rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;







<



>
|
<
<
<
|

<







5607
5608
5609
5610
5611
5612
5613

5614
5615
5616
5617
5618



5619
5620

5621
5622
5623
5624
5625
5626
5627
  sqlite3_pcache_page *pBase;

  assert( pPager->errCode==SQLITE_OK );
  assert( pPager->eState>=PAGER_READER );
  assert( assert_pager_state(pPager) );
  assert( pPager->hasHeldSharedLock==1 );


  /* If this is an CONCURRENT 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( sqlite3PagerUsePage(pPager, pgno)!=SQLITE_OK ){
    pPg = 0;



    goto pager_acquire_err;
  }


  if( pgno==0 ) return SQLITE_CORRUPT_BKPT;
  pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
  if( pBase==0 ){
    pPg = 0;
    rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;
Changes to src/pager.h.
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237

238
239
240
241
242
243
244

/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);

void sqlite3PagerRekey(DbPage*, Pgno, u16);

#ifndef SQLITE_OMIT_CONCURRENT

void sqlite3PagerEndConcurrent(Pager*);
int sqlite3PagerBeginConcurrent(Pager*);
void sqlite3PagerDropExclusiveLock(Pager*);
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*);
void sqlite3PagerSetDbsize(Pager *pPager, Pgno);
int sqlite3PagerIsWal(Pager*);
#else
# define sqlite3PagerEndConcurrent(x)

#endif

#if defined(SQLITE_DEBUG) || !defined(SQLITE_OMIT_CONCURRENT)
int sqlite3PagerIswriteable(DbPage*);
#endif

int sqlite3PagerWalInfo(Pager*, u32 *pnPrior, u32 *pnFrame);







>








>







223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);

void sqlite3PagerRekey(DbPage*, Pgno, u16);

#ifndef SQLITE_OMIT_CONCURRENT
int sqlite3PagerUsePage(Pager*, Pgno);
void sqlite3PagerEndConcurrent(Pager*);
int sqlite3PagerBeginConcurrent(Pager*);
void sqlite3PagerDropExclusiveLock(Pager*);
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*);
void sqlite3PagerSetDbsize(Pager *pPager, Pgno);
int sqlite3PagerIsWal(Pager*);
#else
# define sqlite3PagerEndConcurrent(x)
# define sqlite3PagerUsePage(x, y) SQLITE_OK
#endif

#if defined(SQLITE_DEBUG) || !defined(SQLITE_OMIT_CONCURRENT)
int sqlite3PagerIswriteable(DbPage*);
#endif

int sqlite3PagerWalInfo(Pager*, u32 *pnPrior, u32 *pnFrame);
Changes to test/concurrent5.test.
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#-------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db

set big1 [string repeat ab 10000]
set big2 "[string repeat ab  9999]xy"

catchsql { ROLLBACK }

do_execsql_test 1.6.0 {
  CREATE TABLE x1(x, y);
  INSERT INTO x1 VALUES(1, $big1);
  PRAGMA journal_mode = wal;
} {wal}

do_execsql_test -db db2 1.6.1.1 {







<
<







157
158
159
160
161
162
163


164
165
166
167
168
169
170
#-------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db

set big1 [string repeat ab 10000]
set big2 "[string repeat ab  9999]xy"



do_execsql_test 1.6.0 {
  CREATE TABLE x1(x, y);
  INSERT INTO x1 VALUES(1, $big1);
  PRAGMA journal_mode = wal;
} {wal}

do_execsql_test -db db2 1.6.1.1 {
205
206
207
208
209
210
211



212


























213
214
215
216
217
218
219
220
  COMMIT;
} {1 {database is locked}}
do_test_conflict_msg 1.6.1.5 {
  conflict at page 21 (read/write page; part of db table x1; content=0000000061626162...)
}
catchsql ROLLBACK
































db close
db2 close
sqlite3_shutdown
test_sqlite3_log 
sqlite3_initialize
finish_test








>
>
>

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








203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  COMMIT;
} {1 {database is locked}}
do_test_conflict_msg 1.6.1.5 {
  conflict at page 21 (read/write page; part of db table x1; content=0000000061626162...)
}
catchsql ROLLBACK

#--------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db

set big1 [string repeat ab 10000]
set big2 "[string repeat ab  9999]xy"

do_execsql_test 1.7.0 {
  CREATE TABLE ww(a);
  CREATE TABLE y1(x, y);
  INSERT INTO y1 VALUES(1, $big1);
  PRAGMA journal_mode = wal;
} {wal}

do_execsql_test -db db2 1.7.1 {
  BEGIN;
    UPDATE y1 SET y=$big2;
    SELECT * FROM ww;
}

do_execsql_test 1.7.2 {
  BEGIN CONCURRENT;
    INSERT INTO ww SELECT y FROM y1;
}

do_execsql_test -db db2 1.7.3 COMMIT

do_catchsql_test 1.7.4 {
  COMMIT;
} {1 {database is locked}}

db close
db2 close
sqlite3_shutdown
test_sqlite3_log 
sqlite3_initialize
finish_test