/ Check-in [71af9acb]
Login

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

Overview
Comment:Add further test cases for the new code on this branch. And a couple of fixes.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | readonly-wal-recovery
Files: files | file ages | folders
SHA3-256: 71af9acb227a91d9ad8798c9d0b12d6967e863d050f5cb1fddb45f25ee1f47db
User & Date: dan 2017-11-06 19:49:34
Context
2017-11-07
09:08
Add fault-injection tests for the code on this branch. check-in: a7d949fb user: dan tags: readonly-wal-recovery
2017-11-06
19:49
Add further test cases for the new code on this branch. And a couple of fixes. check-in: 71af9acb user: dan tags: readonly-wal-recovery
2017-11-04
21:06
Add further tests for the code added on this branch. check-in: a6716fcd user: dan tags: readonly-wal-recovery
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/wal.c.

2195
2196
2197
2198
2199
2200
2201
2202

2203
2204
2205
2206
2207
2208
2209
....
2219
2220
2221
2222
2223
2224
2225
2226



2227
2228
2229
2230
2231
2232
2233

2234
2235
2236
2237
2238
2239
2240
....
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
  assert( pWal->nWiData>0 && pWal->apWiData[0] );

  /* Take WAL_READ_LOCK(0). This has the effect of preventing any
  ** live clients from running a checkpoint, but does not stop them
  ** from running recovery.  */
  rc = walLockShared(pWal, WAL_READ_LOCK(0));
  if( rc!=SQLITE_OK ){
    return (rc==SQLITE_BUSY ? WAL_RETRY : rc);

  }
  pWal->readLock = 0;

  /* Try to map the *-shm file again. If it succeeds this time, then 
  ** a non-readonly_shm connection has already connected to the database.
  ** In this case, start over with opening the transaction.
  ** 
................................................................................
    assert( rc!=SQLITE_OK );
    rc = (rc==SQLITE_READONLY ? WAL_RETRY : rc);
    goto begin_unlocked_out;
  }

  memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
  rc = sqlite3OsFileSize(pWal->pWalFd, &szWal);
  if( rc!=SQLITE_OK || (szWal<WAL_HDRSIZE && pWal->hdr.mxFrame==0) ){



    /* If the wal file is too small to contain a wal-header and the
    ** wal-index header has mxFrame==0, then it must be safe to proceed
    ** reading the database file only. However, the page cache cannot
    ** be trusted, as a read/write connection may have connected, written
    ** the db, run a checkpoint, truncated the wal file and disconnected
    ** since this client's last read transaction.  */
    *pChanged = 1;

    goto begin_unlocked_out;
  }

  /* Check the salt keys at the start of the wal file still match. */
  rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
  if( rc!=SQLITE_OK ){
    goto begin_unlocked_out;
................................................................................
      iOffset+=szFrame
  ){
    u32 pgno;                   /* Database page number for frame */
    u32 nTruncate;              /* dbsize field from frame header */

    /* Read and decode the next log frame. */
    rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
    if( rc!=SQLITE_OK ){
      if( rc==SQLITE_IOERR_SHORT_READ ){
        /* If this branch is taken, some other client has truncated the
        ** *-wal file since the call to sqlite3OsFileSize() above. This
        ** indicates that a read-write client has connected to the system.
        ** So retry opening this read transaction.  */
        rc = WAL_RETRY;
      }
      break;
    }
    if( !walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame) ) break;

    /* If nTruncate is non-zero, then a complete transaction has been
    ** appended to this wal file. Set rc to WAL_RETRY and break out of
    ** the loop.  */
    if( nTruncate ){
      rc = WAL_RETRY;







|
>







 







|
>
>
>







>







 







|
<
<
<
<
<
<
<
<
<







2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
....
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
....
2269
2270
2271
2272
2273
2274
2275
2276









2277
2278
2279
2280
2281
2282
2283
  assert( pWal->nWiData>0 && pWal->apWiData[0] );

  /* Take WAL_READ_LOCK(0). This has the effect of preventing any
  ** live clients from running a checkpoint, but does not stop them
  ** from running recovery.  */
  rc = walLockShared(pWal, WAL_READ_LOCK(0));
  if( rc!=SQLITE_OK ){
    if( rc==SQLITE_BUSY ) rc = WAL_RETRY;
    goto begin_unlocked_out;
  }
  pWal->readLock = 0;

  /* Try to map the *-shm file again. If it succeeds this time, then 
  ** a non-readonly_shm connection has already connected to the database.
  ** In this case, start over with opening the transaction.
  ** 
................................................................................
    assert( rc!=SQLITE_OK );
    rc = (rc==SQLITE_READONLY ? WAL_RETRY : rc);
    goto begin_unlocked_out;
  }

  memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
  rc = sqlite3OsFileSize(pWal->pWalFd, &szWal);
  if( rc!=SQLITE_OK ){
    goto begin_unlocked_out;
  }
  if( szWal<WAL_HDRSIZE ){
    /* If the wal file is too small to contain a wal-header and the
    ** wal-index header has mxFrame==0, then it must be safe to proceed
    ** reading the database file only. However, the page cache cannot
    ** be trusted, as a read/write connection may have connected, written
    ** the db, run a checkpoint, truncated the wal file and disconnected
    ** since this client's last read transaction.  */
    *pChanged = 1;
    rc = (pWal->hdr.mxFrame==0 ? SQLITE_OK : WAL_RETRY);
    goto begin_unlocked_out;
  }

  /* Check the salt keys at the start of the wal file still match. */
  rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
  if( rc!=SQLITE_OK ){
    goto begin_unlocked_out;
................................................................................
      iOffset+=szFrame
  ){
    u32 pgno;                   /* Database page number for frame */
    u32 nTruncate;              /* dbsize field from frame header */

    /* Read and decode the next log frame. */
    rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
    if( rc!=SQLITE_OK ) break;









    if( !walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame) ) break;

    /* If nTruncate is non-zero, then a complete transaction has been
    ** appended to this wal file. Set rc to WAL_RETRY and break out of
    ** the loop.  */
    if( nTruncate ){
      rc = WAL_RETRY;

Changes to test/walro2.test.

191
192
193
194
195
196
197
198






199








200






























































































































201
    }
    code2 { db2 close }
    list [file size test.db-wal] [file size test.db-shm]
  } [list [wal_file_size 4 1024] 32768]
  do_test 3.3.3 {
    sql1 { SELECT * FROM t1 }
  } {i ii}







}







































































































































finish_test








>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

191
192
193
194
195
196
197
198
199
200
201
202
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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
    }
    code2 { db2 close }
    list [file size test.db-wal] [file size test.db-shm]
  } [list [wal_file_size 4 1024] 32768]
  do_test 3.3.3 {
    sql1 { SELECT * FROM t1 }
  } {i ii}

  #-----------------------------------------------------------------------
  #
  #
  catch { code1 { db close } }
  catch { code2 { db2 close } }
  catch { code3 { db3 close } }

  do_test 4.0 {
    code1 { forcedelete test.db }
    code1 { sqlite3 db test.db }
    sql1 {
      PRAGMA journal_mode = wal;
      CREATE TABLE t1(x);
      INSERT INTO t1 VALUES('hello');
      INSERT INTO t1 VALUES('world');
    }

    forcecopy test.db test.db2
    forcecopy test.db-wal test.db2-wal
    forcecopy test.db-shm test.db2-shm
    
    code1 { db close }
  } {}

  do_test 4.1.1 {
    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
    sql2 { SELECT * FROM t1 }
  } {hello world}

  do_test 4.1.2 {
    code3 { sqlite3 db3 test.db2 }
    sql3 {
      INSERT INTO t1 VALUES('!');
      PRAGMA wal_checkpoint = truncate;
    }
    code3 { db3 close }
  } {}
  do_test 4.1.3 {
    sql2 { SELECT * FROM t1 }
  } {hello world !}

  catch { code1 { db close } }
  catch { code2 { db2 close } }
  catch { code3 { db3 close } }

  do_test 4.2.1 {
    code1 { sqlite3 db test.db }
    sql1 {
      INSERT INTO t1 VALUES('!');
      INSERT INTO t1 VALUES('!');

      PRAGMA cache_size = 10;
      CREATE TABLE t2(x);

      BEGIN;
        WITH s(i) AS (
          SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500
          )
        INSERT INTO t2 SELECT randomblob(500) FROM s;
        SELECT count(*) FROM t2;
    } 
  } {500}
  do_test 4.2.2 {
    file size test.db-wal
  } {461152}
  do_test 4.2.4 {
    forcecopy test.db test.db2
    forcecopy test.db-wal test.db2-wal
    forcecopy test.db-shm test.db2-shm

    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
    sql2 {
      SELECT * FROM t1;
      SELECT count(*) FROM t2;
    }
  } {hello world ! ! 0}

  #-----------------------------------------------------------------------
  #
  #
  catch { code1 { db close } }
  catch { code2 { db2 close } }
  catch { code3 { db3 close } }

  do_test 5.0 {
    code1 { forcedelete test.db }
    code1 { sqlite3 db test.db }
    sql1 {
      PRAGMA journal_mode = wal;
      CREATE TABLE t1(x);
      INSERT INTO t1 VALUES('hello');
      INSERT INTO t1 VALUES('world');
      INSERT INTO t1 VALUES('!');
      INSERT INTO t1 VALUES('world');
      INSERT INTO t1 VALUES('hello');
    }

    forcecopy test.db test.db2
    forcecopy test.db-wal test.db2-wal
    forcecopy test.db-shm test.db2-shm
    
    code1 { db close }
  } {}

  do_test 5.1 {
    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
    sql2 {
      SELECT * FROM t1;
    }
  } {hello world ! world hello}

  do_test 5.2 {
    code1 {
      proc handle_read {op args} {
        if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
          set ::res2 [sql2 { SELECT * FROM t1 }]
        }
        puts "$msg xRead $args"
        return "SQLITE_OK"
      }
      testvfs tvfs -fullshm 1

      sqlite3 db file:test.db2?vfs=tvfs
      db eval { SELECT * FROM sqlite_master }

      tvfs filter xRead
      tvfs script handle_read
    }
    sql1 {
      PRAGMA wal_checkpoint = truncate;
    }
    code1 { set ::res2 }
  } {hello world ! world hello}

  do_test 5.3 {
    code1 { db close }
    code1 { tvfs delete }
  } {}


}

finish_test