/ Check-in [525f75fa]
Login

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: 525f75fa9fd4a95acc3fb3b0a01dabe2be39b383
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
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

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