Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix a problem causing sqlite3_snapshot_recover() to return SQLITE_IOERR_SHORT_READ. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | serializable-snapshot |
Files: | files | file ages | folders |
SHA1: |
525f75fa9fd4a95acc3fb3b0a01dabe2 |
User & Date: | dan 2016-11-19 16:35:53 |
Context
2016-11-19
| ||
17:20 | Add another fault-injection test for sqlite3_snapshot_recover(). check-in: 7e040406 user: dan tags: serializable-snapshot | |
16:35 | Fix a problem causing sqlite3_snapshot_recover() to return SQLITE_IOERR_SHORT_READ. check-in: 525f75fa user: dan tags: serializable-snapshot | |
14:53 | Fix a bug in sqlite3_snapshot_recover() that could cause subsequent read transactions to use out-of-data cache entries. check-in: 9abeb798 user: dan tags: serializable-snapshot | |
Changes
Changes to src/wal.c.
2388 2388 2389 2389 rc = sqlite3WalBeginReadTransaction(pWal, &dummy); 2390 2390 if( rc==SQLITE_OK ){ 2391 2391 rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); 2392 2392 if( rc==SQLITE_OK ){ 2393 2393 volatile WalCkptInfo *pInfo = walCkptInfo(pWal); 2394 2394 int szPage = (int)pWal->szPage; 2395 - void *pBuf1 = sqlite3_malloc(szPage); 2396 - void *pBuf2 = sqlite3_malloc(szPage); 2395 + i64 szDb; /* Size of db file in bytes */ 2397 2396 2398 - if( pBuf1==0 || pBuf2==0 ){ 2399 - rc = SQLITE_NOMEM; 2400 - }else{ 2401 - u32 i = pInfo->nBackfillAttempted; 2402 - for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){ 2403 - volatile ht_slot *dummy; 2404 - volatile u32 *aPgno; /* Array of page numbers */ 2405 - u32 iZero; /* Frame corresponding to aPgno[0] */ 2406 - u32 pgno; /* Page number in db file */ 2407 - i64 iDbOff; /* Offset of db file entry */ 2408 - i64 iWalOff; /* Offset of wal file entry */ 2409 - rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero); 2397 + rc = sqlite3OsFileSize(pWal->pDbFd, &szDb); 2398 + if( rc==SQLITE_OK ){ 2399 + void *pBuf1 = sqlite3_malloc(szPage); 2400 + void *pBuf2 = sqlite3_malloc(szPage); 2401 + if( pBuf1==0 || pBuf2==0 ){ 2402 + rc = SQLITE_NOMEM; 2403 + }else{ 2404 + u32 i = pInfo->nBackfillAttempted; 2405 + for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){ 2406 + volatile ht_slot *dummy; 2407 + volatile u32 *aPgno; /* Array of page numbers */ 2408 + u32 iZero; /* Frame corresponding to aPgno[0] */ 2409 + u32 pgno; /* Page number in db file */ 2410 + i64 iDbOff; /* Offset of db file entry */ 2411 + i64 iWalOff; /* Offset of wal file entry */ 2410 2412 2411 - if( rc==SQLITE_OK ){ 2413 + rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero); 2414 + if( rc!=SQLITE_OK ) break; 2412 2415 pgno = aPgno[i-iZero]; 2413 2416 iDbOff = (i64)(pgno-1) * szPage; 2414 - iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; 2415 - rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); 2416 - } 2417 2417 2418 - if( rc==SQLITE_OK ){ 2419 - rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); 2420 - } 2418 + if( iDbOff+szPage<=szDb ){ 2419 + iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; 2420 + rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); 2421 2421 2422 - if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ 2423 - break; 2422 + if( rc==SQLITE_OK ){ 2423 + rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); 2424 + } 2425 + 2426 + if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ 2427 + break; 2428 + } 2429 + } 2430 + 2431 + pInfo->nBackfillAttempted = i-1; 2424 2432 } 2425 - 2426 - pInfo->nBackfillAttempted = i-1; 2427 2433 } 2434 + 2435 + sqlite3_free(pBuf1); 2436 + sqlite3_free(pBuf2); 2428 2437 } 2429 - 2430 - sqlite3_free(pBuf1); 2431 - sqlite3_free(pBuf2); 2432 2438 walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); 2433 2439 } 2434 2440 2435 2441 /* End the read transaction opened above. Also zero the cache of the 2436 2442 ** wal-index header to force the pager-cache to be flushed when the next 2437 2443 ** read transaction is open, as it may not match the current contents of 2438 2444 ** pWal->hdr. */
Changes to test/snapshot_fault.test.
12 12 # of this file is the sqlite3_snapshot_xxx() APIs. 13 13 # 14 14 15 15 set testdir [file dirname $argv0] 16 16 source $testdir/tester.tcl 17 17 ifcapable !snapshot {finish_test; return} 18 18 set testprefix snapshot_fault 19 + 20 +if 1 { 19 21 20 22 #------------------------------------------------------------------------- 21 23 # Check that an sqlite3_snapshot_open() client cannot be tricked into 22 24 # reading a corrupt snapshot even if a second client fails while 23 25 # checkpointing the db. 24 26 # 25 27 do_faultsim_test 1.0 -prep { ................................................................................ 155 157 }] 156 158 if {$res != "1 2 3 ok"} { error "res is $res" } 157 159 } 158 160 159 161 sqlite3_snapshot_free $::snapshot 160 162 } 161 163 164 +} 165 + 166 +#------------------------------------------------------------------------- 167 +# Test the handling of faults that occur within sqlite3_snapshot_recover(). 168 +# 169 +reset_db 170 +do_execsql_test 4.0 { 171 + PRAGMA journal_mode = wal; 172 + CREATE TABLE t1(zzz); 173 + INSERT INTO t1 VALUES('abc'); 174 + INSERT INTO t1 VALUES('def'); 175 +} {wal} 176 +faultsim_save_and_close 177 + 178 +do_test 4.1 { 179 + faultsim_restore_and_reopen 180 + db eval { SELECT * FROM sqlite_master } 181 + sqlite3_snapshot_recover db main 182 +} {} 183 +db close 184 + 185 +do_faultsim_test 4 -faults oom* -prep { 186 + faultsim_restore_and_reopen 187 + db eval { SELECT * FROM sqlite_master } 188 +} -body { 189 + sqlite3_snapshot_recover db main 190 +} -test { 191 + faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM} 192 +} 162 193 163 194 164 195 finish_test