SQLite

Check-in [525f75fa9f]
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
Timelines: family | ancestors | descendants | both | serializable-snapshot
Files: files | file ages | folders
SHA1: 525f75fa9fd4a95acc3fb3b0a01dabe2be39b383
User & Date: dan 2016-11-19 16:35:53.322
Context
2016-11-19
17:20
Add another fault-injection test for sqlite3_snapshot_recover(). (check-in: 7e04040613 user: dan tags: serializable-snapshot)
16:35
Fix a problem causing sqlite3_snapshot_recover() to return SQLITE_IOERR_SHORT_READ. (check-in: 525f75fa9f 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: 9abeb7980a user: dan tags: serializable-snapshot)
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to src/wal.c.
2388
2389
2390
2391
2392
2393
2394




2395
2396


2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408











2409
2410

2411

2412
2413


2414
2415
2416



2417
2418
2419
2420



2421
2422
2423
2424
2425
2426
2427
2428








2429
2430
2431



2432
2433
2434
2435
2436
2437
2438
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398


2399
2400












2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411

2412
2413

2414
2415
2416
2417
2418



2419
2420
2421




2422
2423
2424
2425







2426
2427
2428
2429
2430
2431
2432
2433
2434


2435
2436
2437
2438
2439
2440
2441
2442
2443
2444







+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-

+
-
+


+
+
-
-
-
+
+
+
-
-
-
-
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
+
+
+








  rc = sqlite3WalBeginReadTransaction(pWal, &dummy);
  if( rc==SQLITE_OK ){
    rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
    if( rc==SQLITE_OK ){
      volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
      int szPage = (int)pWal->szPage;
      i64 szDb;                   /* Size of db file in bytes */

      rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
      if( rc==SQLITE_OK ){
      void *pBuf1 = sqlite3_malloc(szPage);
      void *pBuf2 = sqlite3_malloc(szPage);
        void *pBuf1 = sqlite3_malloc(szPage);
        void *pBuf2 = sqlite3_malloc(szPage);

      if( pBuf1==0 || pBuf2==0 ){
        rc = SQLITE_NOMEM;
      }else{
        u32 i = pInfo->nBackfillAttempted;
        for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
          volatile ht_slot *dummy;
          volatile u32 *aPgno;      /* Array of page numbers */
          u32 iZero;                /* Frame corresponding to aPgno[0] */
          u32 pgno;                 /* Page number in db file */
          i64 iDbOff;               /* Offset of db file entry */
          i64 iWalOff;              /* Offset of wal file entry */
        if( pBuf1==0 || pBuf2==0 ){
          rc = SQLITE_NOMEM;
        }else{
          u32 i = pInfo->nBackfillAttempted;
          for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
            volatile ht_slot *dummy;
            volatile u32 *aPgno;      /* Array of page numbers */
            u32 iZero;                /* Frame corresponding to aPgno[0] */
            u32 pgno;                 /* Page number in db file */
            i64 iDbOff;               /* Offset of db file entry */
            i64 iWalOff;              /* Offset of wal file entry */
          rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero);

            rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero);
          if( rc==SQLITE_OK ){
            if( rc!=SQLITE_OK ) break;
            pgno = aPgno[i-iZero];
            iDbOff = (i64)(pgno-1) * szPage;

            if( iDbOff+szPage<=szDb ){
            iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
            rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
          }
              iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
              rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);


          if( rc==SQLITE_OK ){
            rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
          }
              if( rc==SQLITE_OK ){
                rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
              }

          if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
            break;
          }

          pInfo->nBackfillAttempted = i-1;
        }
      }
              if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
                break;
              }
            }

            pInfo->nBackfillAttempted = i-1;
          }
        }

      sqlite3_free(pBuf1);
      sqlite3_free(pBuf2);
        sqlite3_free(pBuf1);
        sqlite3_free(pBuf2);
      }
      walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
    }

    /* End the read transaction opened above. Also zero the cache of the
    ** wal-index header to force the pager-cache to be flushed when the next
    ** read transaction is open, as it may not match the current contents of
    ** pWal->hdr. */
Changes to test/snapshot_fault.test.
12
13
14
15
16
17
18


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







+
+







# of this file is the sqlite3_snapshot_xxx() APIs.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !snapshot {finish_test; return}
set testprefix snapshot_fault

if 1 {

#-------------------------------------------------------------------------
# Check that an sqlite3_snapshot_open() client cannot be tricked into
# reading a corrupt snapshot even if a second client fails while 
# checkpointing the db.
#
do_faultsim_test 1.0 -prep {
155
156
157
158
159
160
161
162

163





























164
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195







-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

    }]
    if {$res != "1 2 3 ok"} { error "res is $res" }
  }

  sqlite3_snapshot_free $::snapshot
}


}

#-------------------------------------------------------------------------
# Test the handling of faults that occur within sqlite3_snapshot_recover().
#
reset_db
do_execsql_test 4.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(zzz);
  INSERT INTO t1 VALUES('abc');
  INSERT INTO t1 VALUES('def');
} {wal}
faultsim_save_and_close

do_test 4.1 {
  faultsim_restore_and_reopen
  db eval { SELECT * FROM sqlite_master } 
  sqlite3_snapshot_recover db main
} {}
db close

do_faultsim_test 4 -faults oom* -prep {
  faultsim_restore_and_reopen
  db eval { SELECT * FROM sqlite_master } 
} -body {
  sqlite3_snapshot_recover db main
} -test {
  faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
}


finish_test