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: |
49263c9136c81638833aa71c9d590e31 |
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
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 | sqlite3_pcache_page *pBase; assert( pPager->errCode==SQLITE_OK ); assert( pPager->eState>=PAGER_READER ); assert( assert_pager_state(pPager) ); assert( pPager->hasHeldSharedLock==1 ); | < > | < < < | < | 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 | #------------------------------------------------------------------------- reset_db sqlite3 db2 test.db set big1 [string repeat ab 10000] set big2 "[string repeat ab 9999]xy" | < < | 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 |