/ Check-in [8d0f8a7f]
Login

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

Overview
Comment:Add a version number to the wal-index header. If SQLite encounters a version number in either the wal or wal-index files that it does not understand, the operation is abandoned and SQLITE_CANTOPEN returned.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8d0f8a7f70d6fb42369411a934b30f8c8ca8322f
User & Date: dan 2010-06-23 15:55:43
Context
2010-06-23
17:58
Adjust the shared-memory locking range to account for the new version number values in the wal-index header. check-in: a6dc0df3 user: drh tags: trunk
15:55
Add a version number to the wal-index header. If SQLite encounters a version number in either the wal or wal-index files that it does not understand, the operation is abandoned and SQLITE_CANTOPEN returned. check-in: 8d0f8a7f user: dan tags: trunk
15:18
Fix the xCurrentTimeInt64 interface on windows. It was off by a factor of 10. check-in: 51ef43b9 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/wal.c.

36
37
38
39
40
41
42


43
44
45
46
47
48
49
...
242
243
244
245
246
247
248















249
250
251
252
253
254
255
...
268
269
270
271
272
273
274


275
276
277
278
279
280
281
...
347
348
349
350
351
352
353
354

355
356
357
358
359
360
361
362
...
576
577
578
579
580
581
582

583
584
585
586
587
588
589
....
1025
1026
1027
1028
1029
1030
1031

1032
1033
1034
1035
1036
1037
1038
....
1051
1052
1053
1054
1055
1056
1057
1058
1059












1060
1061
1062
1063
1064
1065
1066
....
1654
1655
1656
1657
1658
1659
1660



1661
1662
1663
1664
1665
1666
1667
....
2261
2262
2263
2264
2265
2266
2267
2268


2269
2270
2271
2272
2273
2274
2275









2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
**
**     0: Magic number.  0x377f0682 or 0x377f0683
**     4: File format version.  Currently 3007000
**     8: Database page size.  Example: 1024
**    12: Checkpoint sequence number
**    16: Salt-1, random integer incremented with each checkpoint
**    20: Salt-2, a different random integer changing with each ckpt


**
** Immediately following the wal-header are zero or more frames. Each
** frame consists of a 24-byte frame-header followed by a <page-size> bytes
** of page data. The frame-header is broken into 6 big-endian 32-bit unsigned 
** integer values, as follows:
**
**     0: Page number.
................................................................................
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
int sqlite3WalTrace = 0;
# define WALTRACE(X)  if(sqlite3WalTrace) sqlite3DebugPrintf X
#else
# define WALTRACE(X)
#endif

















/*
** Indices of various locking bytes.   WAL_NREADER is the number
** of available reader locks and should be at least 3.
*/
#define WAL_WRITE_LOCK         0
#define WAL_ALL_BUT_WRITE      1
................................................................................
/*
** The following object holds a copy of the wal-index header content.
**
** The actual header in the wal-index consists of two copies of this
** object.
*/
struct WalIndexHdr {


  u32 iChange;                    /* Counter incremented each transaction */
  u8 isInit;                      /* 1 when initialized */
  u8 bigEndCksum;                 /* True if checksums in WAL are big-endian */
  u16 szPage;                     /* Database page size in bytes */
  u32 mxFrame;                    /* Index of last valid frame in the WAL */
  u32 nPage;                      /* Size of database in pages */
  u32 aFrameCksum[2];             /* Checksum of last frame in log */
................................................................................
#define WALINDEX_LOCK_OFFSET   (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo))
#define WALINDEX_LOCK_RESERVED 16
#define WALINDEX_HDR_SIZE      (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED)

/* Size of header before each frame in wal */
#define WAL_FRAME_HDRSIZE 24

/* Size of write ahead log header */

#define WAL_HDRSIZE 24

/* WAL magic value. Either this value, or the same value with the least
** significant bit also set (WAL_MAGIC | 0x00000001) is stored in 32-bit
** big-endian format in the first 4 bytes of a WAL file.
**
** If the LSB is set, then the checksums for each frame within the WAL
** file are calculated by treating all data as an array of 32-bit 
................................................................................
*/
static void walIndexWriteHdr(Wal *pWal){
  volatile WalIndexHdr *aHdr = walIndexHdr(pWal);
  const int nCksum = offsetof(WalIndexHdr, aCksum);

  assert( pWal->writeLock );
  pWal->hdr.isInit = 1;

  walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum);
  memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr));
  sqlite3OsShmBarrier(pWal->pDbFd);
  memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr));
}

/*
................................................................................
    u8 *aFrame = 0;               /* Malloc'd buffer to load entire frame */
    int szFrame;                  /* Number of bytes in buffer aFrame[] */
    u8 *aData;                    /* Pointer to data part of aFrame buffer */
    int iFrame;                   /* Index of last frame read */
    i64 iOffset;                  /* Next offset to read from log file */
    int szPage;                   /* Page size according to the log */
    u32 magic;                    /* Magic value read from WAL header */


    /* Read in the WAL header. */
    rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
    if( rc!=SQLITE_OK ){
      goto recovery_error;
    }

................................................................................
      goto finished;
    }
    pWal->hdr.bigEndCksum = (magic&0x00000001);
    pWal->szPage = szPage;
    pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
    memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
    walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, 
        aBuf, WAL_HDRSIZE, 0, pWal->hdr.aFrameCksum
    );













    /* Malloc a buffer to read frames into. */
    szFrame = szPage + WAL_FRAME_HDRSIZE;
    aFrame = (u8 *)sqlite3_malloc(szFrame);
    if( !aFrame ){
      rc = SQLITE_NOMEM;
      goto recovery_error;
................................................................................

  /* If the first page of the wal-index has been mapped, try to read the
  ** wal-index header immediately, without holding any lock. This usually
  ** works, but may fail if the wal-index header is corrupt or currently 
  ** being modified by another user.
  */
  badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);




  /* If the first attempt failed, it might have been due to a race
  ** with a writer.  So get a WRITE lock and try again.
  */
  assert( badHdr==0 || pWal->writeLock==0 );
  if( badHdr && SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
    pWal->writeLock = 1;
................................................................................

  /* If this is the first frame written into the log, write the WAL
  ** header to the start of the WAL file. See comments at the top of
  ** this source file for a description of the WAL header format.
  */
  iFrame = pWal->hdr.mxFrame;
  if( iFrame==0 ){
    u8 aWalHdr[WAL_HDRSIZE];        /* Buffer to assembly wal-header in */


    sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN));
    sqlite3Put4byte(&aWalHdr[4], 3007000);
    sqlite3Put4byte(&aWalHdr[8], szPage);
    pWal->szPage = szPage;
    pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
    sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
    memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);









    rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
    WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
    if( rc!=SQLITE_OK ){
      return rc;
    }
    walChecksumBytes(1, aWalHdr, sizeof(aWalHdr), 0, pWal->hdr.aFrameCksum);
  }
  assert( pWal->szPage==szPage );

  /* Write the log file. */
  for(p=pList; p; p=p->pDirty){
    u32 nDbsize;                  /* Db-size field for frame header */
    i64 iOffset;                  /* Write offset in log file */







>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>







 







|
>
|







 







>







 







>







 







|

>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>







 







|
>
>

|

<
<


>
>
>
>
>
>
>
>
>





<







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
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
...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
....
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
....
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
....
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
....
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310


2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326

2327
2328
2329
2330
2331
2332
2333
**
**     0: Magic number.  0x377f0682 or 0x377f0683
**     4: File format version.  Currently 3007000
**     8: Database page size.  Example: 1024
**    12: Checkpoint sequence number
**    16: Salt-1, random integer incremented with each checkpoint
**    20: Salt-2, a different random integer changing with each ckpt
**    24: Checksum-1 (first part of checksum for first 24 bytes of header).
**    28: Checksum-2 (second part of checksum for first 24 bytes of header).
**
** Immediately following the wal-header are zero or more frames. Each
** frame consists of a 24-byte frame-header followed by a <page-size> bytes
** of page data. The frame-header is broken into 6 big-endian 32-bit unsigned 
** integer values, as follows:
**
**     0: Page number.
................................................................................
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
int sqlite3WalTrace = 0;
# define WALTRACE(X)  if(sqlite3WalTrace) sqlite3DebugPrintf X
#else
# define WALTRACE(X)
#endif

/*
** The maximum (and only) versions of the wal and wal-index formats
** that may be interpreted by this version of SQLite.
**
** If a client begins recovering a WAL file and finds that (a) the checksum
** values in the wal-header are correct and (b) the version field is not
** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN.
**
** Similarly, if a client successfully reads a wal-index header (i.e. the 
** checksum test is successful) and finds that the version field is not
** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite
** returns SQLITE_CANTOPEN.
*/
#define WAL_MAX_VERSION      3007000
#define WALINDEX_MAX_VERSION 3007000

/*
** Indices of various locking bytes.   WAL_NREADER is the number
** of available reader locks and should be at least 3.
*/
#define WAL_WRITE_LOCK         0
#define WAL_ALL_BUT_WRITE      1
................................................................................
/*
** The following object holds a copy of the wal-index header content.
**
** The actual header in the wal-index consists of two copies of this
** object.
*/
struct WalIndexHdr {
  u32 iVersion;                   /* Wal-index version */
  u32 unused;                     /* Unused (padding) field */
  u32 iChange;                    /* Counter incremented each transaction */
  u8 isInit;                      /* 1 when initialized */
  u8 bigEndCksum;                 /* True if checksums in WAL are big-endian */
  u16 szPage;                     /* Database page size in bytes */
  u32 mxFrame;                    /* Index of last valid frame in the WAL */
  u32 nPage;                      /* Size of database in pages */
  u32 aFrameCksum[2];             /* Checksum of last frame in log */
................................................................................
#define WALINDEX_LOCK_OFFSET   (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo))
#define WALINDEX_LOCK_RESERVED 16
#define WALINDEX_HDR_SIZE      (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED)

/* Size of header before each frame in wal */
#define WAL_FRAME_HDRSIZE 24

/* Size of write ahead log header, including checksum. */
/* #define WAL_HDRSIZE 24 */
#define WAL_HDRSIZE 32

/* WAL magic value. Either this value, or the same value with the least
** significant bit also set (WAL_MAGIC | 0x00000001) is stored in 32-bit
** big-endian format in the first 4 bytes of a WAL file.
**
** If the LSB is set, then the checksums for each frame within the WAL
** file are calculated by treating all data as an array of 32-bit 
................................................................................
*/
static void walIndexWriteHdr(Wal *pWal){
  volatile WalIndexHdr *aHdr = walIndexHdr(pWal);
  const int nCksum = offsetof(WalIndexHdr, aCksum);

  assert( pWal->writeLock );
  pWal->hdr.isInit = 1;
  pWal->hdr.iVersion = WALINDEX_MAX_VERSION;
  walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum);
  memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr));
  sqlite3OsShmBarrier(pWal->pDbFd);
  memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr));
}

/*
................................................................................
    u8 *aFrame = 0;               /* Malloc'd buffer to load entire frame */
    int szFrame;                  /* Number of bytes in buffer aFrame[] */
    u8 *aData;                    /* Pointer to data part of aFrame buffer */
    int iFrame;                   /* Index of last frame read */
    i64 iOffset;                  /* Next offset to read from log file */
    int szPage;                   /* Page size according to the log */
    u32 magic;                    /* Magic value read from WAL header */
    u32 version;                  /* Magic value read from WAL header */

    /* Read in the WAL header. */
    rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
    if( rc!=SQLITE_OK ){
      goto recovery_error;
    }

................................................................................
      goto finished;
    }
    pWal->hdr.bigEndCksum = (magic&0x00000001);
    pWal->szPage = szPage;
    pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
    memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
    walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, 
        aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
    );

    if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24])
     || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28])
    ){
      goto finished;
    }

    version = sqlite3Get4byte(&aBuf[4]);
    if( version!=WAL_MAX_VERSION ){
      rc = SQLITE_CANTOPEN_BKPT;
      goto finished;
    }

    /* Malloc a buffer to read frames into. */
    szFrame = szPage + WAL_FRAME_HDRSIZE;
    aFrame = (u8 *)sqlite3_malloc(szFrame);
    if( !aFrame ){
      rc = SQLITE_NOMEM;
      goto recovery_error;
................................................................................

  /* If the first page of the wal-index has been mapped, try to read the
  ** wal-index header immediately, without holding any lock. This usually
  ** works, but may fail if the wal-index header is corrupt or currently 
  ** being modified by another user.
  */
  badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
  if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){
    rc = SQLITE_CANTOPEN_BKPT;
  }

  /* If the first attempt failed, it might have been due to a race
  ** with a writer.  So get a WRITE lock and try again.
  */
  assert( badHdr==0 || pWal->writeLock==0 );
  if( badHdr && SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
    pWal->writeLock = 1;
................................................................................

  /* If this is the first frame written into the log, write the WAL
  ** header to the start of the WAL file. See comments at the top of
  ** this source file for a description of the WAL header format.
  */
  iFrame = pWal->hdr.mxFrame;
  if( iFrame==0 ){
    u8 aWalHdr[WAL_HDRSIZE];      /* Buffer to assemble wal-header in */
    u32 aCksum[2];                /* Checksum for wal-header */

    sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN));
    sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
    sqlite3Put4byte(&aWalHdr[8], szPage);


    sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
    memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
    walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
    sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
    sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
    
    pWal->szPage = szPage;
    pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
    pWal->hdr.aFrameCksum[0] = aCksum[0];
    pWal->hdr.aFrameCksum[1] = aCksum[1];

    rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
    WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
    if( rc!=SQLITE_OK ){
      return rc;
    }

  }
  assert( pWal->szPage==szPage );

  /* Write the log file. */
  for(p=pList; p; p=p->pDirty){
    u32 nDbsize;                  /* Db-size field for frame header */
    i64 iOffset;                  /* Write offset in log file */

Changes to test/backup2.test.

13
14
15
16
17
18
19


20
21
22
23
24
25
26
# of the TCL interface - methods which are based on the
# sqlite3_backup_XXX API.
#
# $Id: backup2.test,v 1.4 2009/04/07 14:14:23 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl



ifcapable !trigger||!view { finish_test ; return }

# Fill a database with test data.
#
do_test backup2-1 {
  db eval {







>
>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# of the TCL interface - methods which are based on the
# sqlite3_backup_XXX API.
#
# $Id: backup2.test,v 1.4 2009/04/07 14:14:23 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_not_use_codec

ifcapable !trigger||!view { finish_test ; return }

# Fill a database with test data.
#
do_test backup2-1 {
  db eval {

Changes to test/date.test.

496
497
498
499
500
501
502

503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528

529

# Test for issues reported by BareFeet (list.sql at tandb.com.au)
# on mailing list on 2008-06-12.
#
# Put a floating point number in the database so that we can manipulate
# raw bits using the hexio interface.
#

do_test date-14.1 {
  execsql {
    PRAGMA auto_vacuum=OFF;
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x);
    INSERT INTO t1 VALUES(1.1);
  }
  db close
  hexio_write test.db 2040 4142ba32bffffff9
  sqlite3 db test.db
  db eval {SELECT * FROM t1}
} {2454629.5}

# Changing the least significant byte of the floating point value between
# 00 and FF should always generate a time of either 23:59:59 or 00:00:00,
# never 24:00:00
#
for {set i 0} {$i<=255} {incr i} {
  db close
  hexio_write test.db 2047 [format %02x $i]
  sqlite3 db test.db
  do_test date-14.2.$i {
    set date [db one {SELECT datetime(x) FROM t1}]
    expr {$date eq "2008-06-12 00:00:00" || $date eq "2008-06-11 23:59:59"}
  } {1}
}

finish_test







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

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531

# Test for issues reported by BareFeet (list.sql at tandb.com.au)
# on mailing list on 2008-06-12.
#
# Put a floating point number in the database so that we can manipulate
# raw bits using the hexio interface.
#
if {0==[sqlite3 -has-codec]} {
  do_test date-14.1 {
    execsql {
      PRAGMA auto_vacuum=OFF;
      PRAGMA page_size = 1024;
      CREATE TABLE t1(x);
      INSERT INTO t1 VALUES(1.1);
    }
    db close
    hexio_write test.db 2040 4142ba32bffffff9
    sqlite3 db test.db
    db eval {SELECT * FROM t1}
  } {2454629.5}
  
  # Changing the least significant byte of the floating point value between
  # 00 and FF should always generate a time of either 23:59:59 or 00:00:00,
  # never 24:00:00
  #
  for {set i 0} {$i<=255} {incr i} {
    db close
    hexio_write test.db 2047 [format %02x $i]
    sqlite3 db test.db
    do_test date-14.2.$i {
      set date [db one {SELECT datetime(x) FROM t1}]
      expr {$date eq "2008-06-12 00:00:00" || $date eq "2008-06-11 23:59:59"}
    } {1}
  }
}
finish_test

Changes to test/pager1.test.

552
553
554
555
556
557
558




559
560
561
562
563
564
565
...
569
570
571
572
573
574
575








576
577
578
579
580
581
582
#  pager1-4.5.4: Restore the file-system. Corrupt the first record in the
#                journal. Check the transaction is not rolled back.
#
#  pager1-4.5.5: Restore the file-system. Corrupt the second record in the
#                journal. Check that the first record in the transaction is 
#                played back, but not the second.
#




faultsim_restore_and_reopen
do_execsql_test pager1.4.5.3 {
  SELECT * FROM t1;
  SELECT * FROM t2;
} {I II III IV}
faultsim_restore_and_reopen
hexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF
................................................................................
} {I II 1 2 III IV 3 4}
faultsim_restore_and_reopen
hexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF
do_execsql_test pager1.4.5.5 {
  SELECT * FROM t1;
  SELECT * FROM t2;
} {I II III IV 3 4}









db close
tv delete

#-------------------------------------------------------------------------
# The following tests deal with multi-file commits.
#







>
>
>
>







 







>
>
>
>
>
>
>
>







552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
...
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
#  pager1-4.5.4: Restore the file-system. Corrupt the first record in the
#                journal. Check the transaction is not rolled back.
#
#  pager1-4.5.5: Restore the file-system. Corrupt the second record in the
#                journal. Check that the first record in the transaction is 
#                played back, but not the second.
#
#  pager1-4.5.6: Restore the file-system. Try to open the database with a
#                readonly connection. This should fail, as a read-only
#                connection cannot roll back the database file.
#
faultsim_restore_and_reopen
do_execsql_test pager1.4.5.3 {
  SELECT * FROM t1;
  SELECT * FROM t2;
} {I II III IV}
faultsim_restore_and_reopen
hexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF
................................................................................
} {I II 1 2 III IV 3 4}
faultsim_restore_and_reopen
hexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF
do_execsql_test pager1.4.5.5 {
  SELECT * FROM t1;
  SELECT * FROM t2;
} {I II III IV 3 4}

faultsim_restore_and_reopen
db close
sqlite3 db test.db -readonly 1
do_catchsql_test pager1.4.5.6 {
  SELECT * FROM t1;
  SELECT * FROM t2;
} {1 {disk I/O error}}

db close
tv delete

#-------------------------------------------------------------------------
# The following tests deal with multi-file commits.
#

Changes to test/wal.test.

13
14
15
16
17
18
19

20
21
22
23
24
25
26
..
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
..
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
...
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
...
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
...
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
....
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
....
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
....
1276
1277
1278
1279
1280
1281
1282


1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
....
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420

1421
1422
1423
1424
1425
1426
1427
# "PRAGMA journal_mode=WAL" mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl


ifcapable !wal {finish_test ; return }

proc reopen_db {} {
  catch { db close }
  file delete -force test.db test.db-wal test.db-wal-summary
  sqlite3_wal db test.db
................................................................................
  [lindex $args 0] eval { PRAGMA auto_vacuum = 0 }
  [lindex $args 0] eval { PRAGMA page_size = 1024 }
  [lindex $args 0] eval { PRAGMA journal_mode = wal }
  [lindex $args 0] eval { PRAGMA synchronous = normal }
  [lindex $args 0] function blob blob
}

proc log_file_size {nFrame pgsz} {
  expr {24 + ($pgsz+24)*$nFrame}
}

proc log_deleted {logfile} {
  return [expr [file exists $logfile]==0]
}

#
# These are 'warm-body' tests used while developing the WAL code. They
# serve to prove that a few really simple cases work:
................................................................................
do_test wal-1.1 {
  execsql COMMIT
  list [file exists test.db-journal] [file exists test.db-wal]
} {0 1}
do_test wal-1.2 {
  # There are now two pages in the log.
  file size test.db-wal
} [log_file_size 2 1024]

do_test wal-1.3 {
  execsql { SELECT * FROM sqlite_master }
} {table t1 t1 2 {CREATE TABLE t1(a, b)}}

do_test wal-1.4 {
  execsql { INSERT INTO t1 VALUES(1, 2) }
................................................................................
  sqlite3_wal db test.db
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
  }
  list [file size test.db] [file size test.db-wal]
} [list 1024 [log_file_size 3 1024]]
do_test wal-7.2 {
  execsql { PRAGMA wal_checkpoint }
  list [file size test.db] [file size test.db-wal]
} [list 2048 [log_file_size 3 1024]]

# Execute some transactions in auto-vacuum mode to test database file
# truncation.
#
do_test wal-8.1 {
  reopen_db
  catch { db close }
................................................................................
    CREATE TABLE t1(x PRIMARY KEY);
  }
  list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044]
} {1 3}
do_test wal-11.2 {
  execsql { PRAGMA wal_checkpoint }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [log_file_size 3 1024]]
do_test wal-11.3 {
  execsql { INSERT INTO t1 VALUES( blob(900) ) }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [log_file_size 4 1024]]

do_test wal-11.4 {
  execsql { 
    BEGIN;
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 2
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 4
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 8
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 16
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [log_file_size 32 1024]]
do_test wal-11.5 {
  execsql { 
    SELECT count(*) FROM t1;
    PRAGMA integrity_check;
  }
} {16 ok}
do_test wal-11.6 {
  execsql COMMIT
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [log_file_size 41 1024]]
do_test wal-11.7 {
  execsql { 
    SELECT count(*) FROM t1;
    PRAGMA integrity_check;
  }
} {16 ok}
do_test wal-11.8 {
  execsql { PRAGMA wal_checkpoint }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 41 1024]]
do_test wal-11.9 {
  db close
  list [expr [file size test.db]/1024] [log_deleted test.db-wal]
} {37 1}
sqlite3_wal db test.db
do_test wal-11.10 {
  execsql {
    PRAGMA cache_size = 10;
    BEGIN;
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 32
      SELECT count(*) FROM t1;
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 37 1024]]
do_test wal-11.11 {
  execsql {
      SELECT count(*) FROM t1;
    ROLLBACK;
    SELECT count(*) FROM t1;
  }
} {32 16}
do_test wal-11.12 {
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 37 1024]]
do_test wal-11.13 {
  execsql {
    INSERT INTO t1 VALUES( blob(900) );
    SELECT count(*) FROM t1;
    PRAGMA integrity_check;
  }
} {17 ok}
do_test wal-11.14 {
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 37 1024]]


#-------------------------------------------------------------------------
# This block of tests, wal-12.*, tests the fix for a problem that 
# could occur if a log that is a prefix of an older log is written 
# into a reused log file.
#
................................................................................
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x, y);
    CREATE TABLE t2(x, y);
    INSERT INTO t1 VALUES('A', 1);
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 1 [log_file_size 5 1024]]
do_test wal-12.2 {
  db close
  sqlite3 db test.db
  execsql {
    PRAGMA synchronous = normal;
    UPDATE t1 SET y = 0 WHERE x = 'A';
  }
................................................................................
  db function blob blob
  for {set i 0} {$i < 16} {incr i} {
    execsql { INSERT INTO t2 SELECT blob(400), blob(400) FROM t2 }
  }
  execsql { SELECT count(*) FROM t2 }
} [expr int(pow(2, 16))]
do_test wal-13.2.3 {
  expr [file size test.db-wal] > [log_file_size 33000 1024]
} 1

do_multiclient_test tn {
  incr tn 2

  do_test wal-13.$tn.0 {
    sql1 {
................................................................................

      INSERT INTO t2 VALUES(1, randomblob(1000));
      INSERT INTO t2 VALUES(2, randomblob(1000));
      INSERT INTO t1 SELECT * FROM t2;
    }
  
    list [file size test.db] [file size test.db-wal]
  } [list [expr 1*1024] [log_file_size 10 1024]]
  do_test wal-16.$tn.3 {
    list [file size test2.db] [file size test2.db-wal]
  } [list [expr 1*1024] [log_file_size 16 1024]]
  
  do_test wal-16.$tn.4 [list eval $ckpt_cmd] $ckpt_res
  
  do_test wal-16.$tn.5 {
    list [file size test.db] [file size test.db-wal]
  } [list [expr ($ckpt_main ? 7 : 1)*1024] [log_file_size 10 1024]]

  do_test wal-16.$tn.6 {
    list [file size test2.db] [file size test2.db-wal]
  } [list [expr ($ckpt_aux ? 7 : 1)*1024] [log_file_size 16 1024]]

  catch { db close }
}

#-------------------------------------------------------------------------
# The following tests - wal-17.* - attempt to verify that the correct
# number of "padding" frames are appended to the log file when a transaction
# is committed in synchronous=FULL mode.
# 
# Do this by creating a database that uses 512 byte pages. Then writing
# a transaction that modifies 171 pages. In synchronous=NORMAL mode, this
# produces a log file of:
#
#   24 + (24+512)*171 = 90312 bytes.
#
# Slightly larger than 11*8192 = 90112 bytes.
#
# Run the test using various different sector-sizes. In each case, the
# WAL code should write the 90300 bytes of log file containing the 
# transaction, then append as may frames as are required to extend the
# log file so that no part of the next transaction will be written into
# a disk-sector used by transaction just committed.
#
set old_pending_byte [sqlite3_test_control_pending_byte 0x10000000]
catch { db close }
foreach {tn sectorsize logsize} {
  1   128  92216
  2   256  92216
  3   512  92216 
  4  1024  92216
  5  2048  92216
  6  4096  94360
  7  8192  98648
} {
  file delete -force test.db test.db-wal test.db-journal
  sqlite3_simulate_device -sectorsize $sectorsize
  sqlite3 db test.db -vfs devsym

  do_test wal-17.$tn.1 {
    execsql {
      PRAGMA auto_vacuum = 0;
................................................................................
    INSERT INTO t1 VALUES(5, 6);          -- frames 5 and 6
  }

  file copy -force test.db testX.db
  file copy -force test.db-wal testX.db-wal
  db close
  list [file size testX.db] [file size testX.db-wal]
} [list [expr 3*1024] [log_file_size 6 1024]]

unset -nocomplain nFrame result
foreach {nFrame result} {
         0      {0 0}
         1      {0 0}
         2      {0 0 1 2}
         3      {0 0 1 2}
................................................................................
      #
      set walhdr [binary format IIIIII 931071618 3007000 $pgsz 1234 22 23]
      set framebody [randomblob $pgsz]
      set framehdr  [binary format IIII $pg 5 22 23]
      set c1 0
      set c2 0
      logcksum c1 c2 $walhdr


      logcksum c1 c2 [string range $framehdr 0 7]
      logcksum c1 c2 $framebody
      set framehdr [binary format IIIIII $pg 5 22 23 $c1 $c2]

      set fd [open test.db-wal w]
      fconfigure $fd -encoding binary -translation binary
      puts -nonewline $fd $walhdr
      puts -nonewline $fd $framehdr
      puts -nonewline $fd $framebody
      close $fd
  
      file size test.db-wal
    } [log_file_size 1 $pgsz]
  
    do_test wal-18.2.$tn.$pg.5 {
      sqlite3 db test.db
      set rc [catch { db one {PRAGMA integrity_check} } msg]
      expr { $rc!=0 || $msg!="ok" }
    } $works
  
................................................................................
  sqlite3 db test.db
  execsql { SELECT count(*) FROM t1 }
} {16384}
integrity_check wal-20.5

catch { db2 close }
catch { db close }
file delete -force test.db test.db-wal test.db-journal

do_test wal-21.1 {
  sqlite3 db test.db

  execsql { 
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t1 VALUES(3, 4);
    INSERT INTO t1 VALUES(5, 6);
    INSERT INTO t1 VALUES(7, 8);







>







 







<
<
<
<







 







|







 







|



|







 







|



|










|









|









|













|









|









|







 







|







 







|







 







|


|





|



|













|











|
|
|
|
|
|
|
|
|







 







|







 







>
>












|







 







<


<
>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
..
38
39
40
41
42
43
44




45
46
47
48
49
50
51
..
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
...
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
...
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
...
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
....
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
....
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
....
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
....
1409
1410
1411
1412
1413
1414
1415

1416
1417

1418
1419
1420
1421
1422
1423
1424
1425
# "PRAGMA journal_mode=WAL" mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl

ifcapable !wal {finish_test ; return }

proc reopen_db {} {
  catch { db close }
  file delete -force test.db test.db-wal test.db-wal-summary
  sqlite3_wal db test.db
................................................................................
  [lindex $args 0] eval { PRAGMA auto_vacuum = 0 }
  [lindex $args 0] eval { PRAGMA page_size = 1024 }
  [lindex $args 0] eval { PRAGMA journal_mode = wal }
  [lindex $args 0] eval { PRAGMA synchronous = normal }
  [lindex $args 0] function blob blob
}





proc log_deleted {logfile} {
  return [expr [file exists $logfile]==0]
}

#
# These are 'warm-body' tests used while developing the WAL code. They
# serve to prove that a few really simple cases work:
................................................................................
do_test wal-1.1 {
  execsql COMMIT
  list [file exists test.db-journal] [file exists test.db-wal]
} {0 1}
do_test wal-1.2 {
  # There are now two pages in the log.
  file size test.db-wal
} [wal_file_size 2 1024]

do_test wal-1.3 {
  execsql { SELECT * FROM sqlite_master }
} {table t1 t1 2 {CREATE TABLE t1(a, b)}}

do_test wal-1.4 {
  execsql { INSERT INTO t1 VALUES(1, 2) }
................................................................................
  sqlite3_wal db test.db
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
  }
  list [file size test.db] [file size test.db-wal]
} [list 1024 [wal_file_size 3 1024]]
do_test wal-7.2 {
  execsql { PRAGMA wal_checkpoint }
  list [file size test.db] [file size test.db-wal]
} [list 2048 [wal_file_size 3 1024]]

# Execute some transactions in auto-vacuum mode to test database file
# truncation.
#
do_test wal-8.1 {
  reopen_db
  catch { db close }
................................................................................
    CREATE TABLE t1(x PRIMARY KEY);
  }
  list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044]
} {1 3}
do_test wal-11.2 {
  execsql { PRAGMA wal_checkpoint }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [wal_file_size 3 1024]]
do_test wal-11.3 {
  execsql { INSERT INTO t1 VALUES( blob(900) ) }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [wal_file_size 4 1024]]

do_test wal-11.4 {
  execsql { 
    BEGIN;
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 2
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 4
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 8
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 16
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [wal_file_size 32 1024]]
do_test wal-11.5 {
  execsql { 
    SELECT count(*) FROM t1;
    PRAGMA integrity_check;
  }
} {16 ok}
do_test wal-11.6 {
  execsql COMMIT
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [wal_file_size 41 1024]]
do_test wal-11.7 {
  execsql { 
    SELECT count(*) FROM t1;
    PRAGMA integrity_check;
  }
} {16 ok}
do_test wal-11.8 {
  execsql { PRAGMA wal_checkpoint }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [wal_file_size 41 1024]]
do_test wal-11.9 {
  db close
  list [expr [file size test.db]/1024] [log_deleted test.db-wal]
} {37 1}
sqlite3_wal db test.db
do_test wal-11.10 {
  execsql {
    PRAGMA cache_size = 10;
    BEGIN;
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 32
      SELECT count(*) FROM t1;
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [wal_file_size 37 1024]]
do_test wal-11.11 {
  execsql {
      SELECT count(*) FROM t1;
    ROLLBACK;
    SELECT count(*) FROM t1;
  }
} {32 16}
do_test wal-11.12 {
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [wal_file_size 37 1024]]
do_test wal-11.13 {
  execsql {
    INSERT INTO t1 VALUES( blob(900) );
    SELECT count(*) FROM t1;
    PRAGMA integrity_check;
  }
} {17 ok}
do_test wal-11.14 {
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [wal_file_size 37 1024]]


#-------------------------------------------------------------------------
# This block of tests, wal-12.*, tests the fix for a problem that 
# could occur if a log that is a prefix of an older log is written 
# into a reused log file.
#
................................................................................
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x, y);
    CREATE TABLE t2(x, y);
    INSERT INTO t1 VALUES('A', 1);
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 1 [wal_file_size 5 1024]]
do_test wal-12.2 {
  db close
  sqlite3 db test.db
  execsql {
    PRAGMA synchronous = normal;
    UPDATE t1 SET y = 0 WHERE x = 'A';
  }
................................................................................
  db function blob blob
  for {set i 0} {$i < 16} {incr i} {
    execsql { INSERT INTO t2 SELECT blob(400), blob(400) FROM t2 }
  }
  execsql { SELECT count(*) FROM t2 }
} [expr int(pow(2, 16))]
do_test wal-13.2.3 {
  expr [file size test.db-wal] > [wal_file_size 33000 1024]
} 1

do_multiclient_test tn {
  incr tn 2

  do_test wal-13.$tn.0 {
    sql1 {
................................................................................

      INSERT INTO t2 VALUES(1, randomblob(1000));
      INSERT INTO t2 VALUES(2, randomblob(1000));
      INSERT INTO t1 SELECT * FROM t2;
    }
  
    list [file size test.db] [file size test.db-wal]
  } [list [expr 1*1024] [wal_file_size 10 1024]]
  do_test wal-16.$tn.3 {
    list [file size test2.db] [file size test2.db-wal]
  } [list [expr 1*1024] [wal_file_size 16 1024]]
  
  do_test wal-16.$tn.4 [list eval $ckpt_cmd] $ckpt_res
  
  do_test wal-16.$tn.5 {
    list [file size test.db] [file size test.db-wal]
  } [list [expr ($ckpt_main ? 7 : 1)*1024] [wal_file_size 10 1024]]

  do_test wal-16.$tn.6 {
    list [file size test2.db] [file size test2.db-wal]
  } [list [expr ($ckpt_aux ? 7 : 1)*1024] [wal_file_size 16 1024]]

  catch { db close }
}

#-------------------------------------------------------------------------
# The following tests - wal-17.* - attempt to verify that the correct
# number of "padding" frames are appended to the log file when a transaction
# is committed in synchronous=FULL mode.
# 
# Do this by creating a database that uses 512 byte pages. Then writing
# a transaction that modifies 171 pages. In synchronous=NORMAL mode, this
# produces a log file of:
#
#   32 + (24+512)*171 = 90312 bytes.
#
# Slightly larger than 11*8192 = 90112 bytes.
#
# Run the test using various different sector-sizes. In each case, the
# WAL code should write the 90300 bytes of log file containing the 
# transaction, then append as may frames as are required to extend the
# log file so that no part of the next transaction will be written into
# a disk-sector used by transaction just committed.
#
set old_pending_byte [sqlite3_test_control_pending_byte 0x10000000]
catch { db close }
foreach {tn sectorsize logsize} "
  1   128  [wal_file_size 172 512]
  2   256  [wal_file_size 172 512]
  3   512  [wal_file_size 172 512] 
  4  1024  [wal_file_size 172 512]
  5  2048  [wal_file_size 172 512]
  6  4096  [wal_file_size 176 512]
  7  8192  [wal_file_size 184 512]
" {
  file delete -force test.db test.db-wal test.db-journal
  sqlite3_simulate_device -sectorsize $sectorsize
  sqlite3 db test.db -vfs devsym

  do_test wal-17.$tn.1 {
    execsql {
      PRAGMA auto_vacuum = 0;
................................................................................
    INSERT INTO t1 VALUES(5, 6);          -- frames 5 and 6
  }

  file copy -force test.db testX.db
  file copy -force test.db-wal testX.db-wal
  db close
  list [file size testX.db] [file size testX.db-wal]
} [list [expr 3*1024] [wal_file_size 6 1024]]

unset -nocomplain nFrame result
foreach {nFrame result} {
         0      {0 0}
         1      {0 0}
         2      {0 0 1 2}
         3      {0 0 1 2}
................................................................................
      #
      set walhdr [binary format IIIIII 931071618 3007000 $pgsz 1234 22 23]
      set framebody [randomblob $pgsz]
      set framehdr  [binary format IIII $pg 5 22 23]
      set c1 0
      set c2 0
      logcksum c1 c2 $walhdr

      append walhdr [binary format II $c1 $c2]
      logcksum c1 c2 [string range $framehdr 0 7]
      logcksum c1 c2 $framebody
      set framehdr [binary format IIIIII $pg 5 22 23 $c1 $c2]

      set fd [open test.db-wal w]
      fconfigure $fd -encoding binary -translation binary
      puts -nonewline $fd $walhdr
      puts -nonewline $fd $framehdr
      puts -nonewline $fd $framebody
      close $fd
  
      file size test.db-wal
    } [wal_file_size 1 $pgsz]
  
    do_test wal-18.2.$tn.$pg.5 {
      sqlite3 db test.db
      set rc [catch { db one {PRAGMA integrity_check} } msg]
      expr { $rc!=0 || $msg!="ok" }
    } $works
  
................................................................................
  sqlite3 db test.db
  execsql { SELECT count(*) FROM t1 }
} {16384}
integrity_check wal-20.5

catch { db2 close }
catch { db close }


do_test wal-21.1 {

  faultsim_delete_and_reopen
  execsql { 
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t1 VALUES(3, 4);
    INSERT INTO t1 VALUES(5, 6);
    INSERT INTO t1 VALUES(7, 8);

Changes to test/wal2.test.

12
13
14
15
16
17
18



19
20
21
22
23
24
25
26
27
28
29
30
31
...
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893

























































894

# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl



ifcapable !wal {finish_test ; return }

proc set_tvfs_hdr {file args} {

  # Set $nHdr to the number of bytes in the wal-index header:
  set nHdr 40
  set nInt [expr {$nHdr/4}]

  if {[llength $args]>2} {
    error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"}
  }

  set blob [tvfs shm $file]
................................................................................

foreach {tn hdr1 hdr2 res} [list                                            \
  3  $wih(1)                $wih(1)                {Barton Deakin}          \
  4  $wih(1)                $wih(2)                {Barton Deakin Watson}   \
  5  $wih(2)                $wih(1)                {Barton Deakin Watson}   \
  6  $wih(2)                $wih(2)                {Barton Deakin Watson}   \
  7  $wih(1)                $wih(1)                {Barton Deakin}          \
  8  {0 0 0 0 0 0 0 0 0 0} {0 0 0 0 0 0 0 0 0 0}   {Barton Deakin Watson}   \
] {
  do_test wal2-9.$tn {
    set_tvfs_hdr $::filename $hdr1 $hdr2
    execsql { SELECT * FROM x } db2
  } $res
}

db2 close
db close


























































finish_test








>
>
>





|







 







|










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
...
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl

ifcapable !wal {finish_test ; return }

proc set_tvfs_hdr {file args} {

  # Set $nHdr to the number of bytes in the wal-index header:
  set nHdr 48
  set nInt [expr {$nHdr/4}]

  if {[llength $args]>2} {
    error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"}
  }

  set blob [tvfs shm $file]
................................................................................

foreach {tn hdr1 hdr2 res} [list                                            \
  3  $wih(1)                $wih(1)                {Barton Deakin}          \
  4  $wih(1)                $wih(2)                {Barton Deakin Watson}   \
  5  $wih(2)                $wih(1)                {Barton Deakin Watson}   \
  6  $wih(2)                $wih(2)                {Barton Deakin Watson}   \
  7  $wih(1)                $wih(1)                {Barton Deakin}          \
  8  {0 0 0 0 0 0 0 0 0 0 0 0} {0 0 0 0 0 0 0 0 0 0 0 0} {Barton Deakin Watson}
] {
  do_test wal2-9.$tn {
    set_tvfs_hdr $::filename $hdr1 $hdr2
    execsql { SELECT * FROM x } db2
  } $res
}

db2 close
db close

#-------------------------------------------------------------------------
# This block of tests - wal2-10.* - focus on the libraries response to
# new versions of the wal or wal-index formats. 
#
#   wal2-10.1.*: Test that the library refuses to "recover" a new WAL 
#                format.
#
#   wal2-10.2.*: Test that the library refuses to read or write a database
#                if the wal-index version is newer than it understands.
#
# At time of writing, the only versions of the wal and wal-index formats
# that exist are versions 3007000 (corresponding to SQLite version 3.7.0,
# the first version of SQLite to feature wal mode).
#
do_test wal2-10.1.1 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(a, b);
    PRAGMA wal_checkpoint;
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t1 VALUES(3, 4);
  }
  faultsim_save_and_close
} {}
do_test wal2-10.1.2 {
  faultsim_restore_and_reopen
  execsql { SELECT * FROM t1 }
} {1 2 3 4}
do_test wal2-10.1.3 {
  faultsim_restore_and_reopen
  set hdr [wal_set_walhdr test.db-wal]
  lindex $hdr 1
} {3007000}
do_test wal2-10.1.4 {
  lset hdr 1 3007001
  wal_set_walhdr test.db-wal $hdr
  catchsql { SELECT * FROM t1 }
} {1 {unable to open database file}}

testvfs tvfs -default 1
do_test wal2-10.2.1 {
  faultsim_restore_and_reopen
  execsql { SELECT * FROM t1 }
} {1 2 3 4}
do_test wal2-10.2.2 { 
  set hdr [set_tvfs_hdr $::filename] 
  lindex $hdr 0 
} {3007000}
breakpoint
do_test wal2-10.2.3 { 
  lset hdr 0 3007001
  wal_fix_walindex_cksum hdr 
  set_tvfs_hdr $::filename $hdr
  catchsql { SELECT * FROM t1 }
} {1 {unable to open database file}}

finish_test

Changes to test/wal_common.tcl.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24






25
26


























































#***********************************************************************
#
# This file contains common code used by many different malloc tests
# within the test suite.
#

proc wal_file_size {nFrame pgsz} {
  expr {24 + ($pgsz+24)*$nFrame}
}

proc wal_frame_count {zFile pgsz} {
  set f [file size $zFile]
  expr {($f - 24) / ($pgsz+24)}
}










































































|




|


>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#***********************************************************************
#
# This file contains common code used by many different malloc tests
# within the test suite.
#

proc wal_file_size {nFrame pgsz} {
  expr {32 + ($pgsz+24)*$nFrame}
}

proc wal_frame_count {zFile pgsz} {
  set f [file size $zFile]
  expr {($f - 32) / ($pgsz+24)}
}

proc wal_cksum_intlist {ckv1 ckv2 intlist} {
  upvar $ckv1 c1
  upvar $ckv2 c2
  foreach {v1 v2} $intlist {
    set c1 [expr {($c1 + $v1 + $c2)&0xFFFFFFFF}]
    set c2 [expr {($c2 + $v2 + $c1)&0xFFFFFFFF}]
  }
}


# This proc calculates checksums in the same way as those used by SQLite 
# in WAL files. If the $endian argument is "big", then checksums are
# calculated by interpreting data as an array of big-endian integers. If
# it is "little", data is interpreted as an array of little-endian integers.
#
proc wal_cksum {endian ckv1 ckv2 blob} {
  upvar $ckv1 c1
  upvar $ckv2 c2

  if {$endian!="big" && $endian!="little"} {
    return -error "Bad value \"$endian\" - must be \"big\" or \"little\""
  }
  set scanpattern I*
  if {$endian == "little"} { set scanpattern i* }

  binary scan $blob $scanpattern values
  wal_cksum_intlist c1 c2 $values
}

proc wal_set_walhdr {filename {intlist {}}} {
  if {[llength $intlist]==6} {
    set blob [binary format I6 $intlist]
    set endian little
    if {[lindex $intlist 0] & 0x00000001} { set endian big }
    set c1 0
    set c2 0
    wal_cksum $endian c1 c2 $blob
    append blob [binary format II $c1 $c2]

    set fd [open $filename r+]
    fconfigure $fd -translation binary
    fconfigure $fd -encoding binary
    seek $fd 0
    puts -nonewline $fd $blob
    close $fd
  }

  set fd [open $filename]
  fconfigure $fd -translation binary
  fconfigure $fd -encoding binary
  set blob [read $fd 24]

  binary scan $blob I6 ints
  set ints
}

proc wal_fix_walindex_cksum {hdrvar} {
  upvar $hdrvar hdr
  set c1 0
  set c2 0
  wal_cksum_intlist c1 c2 [lrange $hdr 0 9]
  lset hdr 10 $c1
  lset hdr 11 $c2
}


Changes to test/walbak.test.

11
12
13
14
15
16
17



18
19
20
21
22
23
24
25
26
27
28
29
30
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl




ifcapable !wal {finish_test ; return }

proc log_file_size {nFrame pgsz} {
  expr {24 + ($pgsz+24)*$nFrame}
}

# Test organization:
# 
#   walback-1.*: Simple tests.
#   walback-2.*: Test backups when the source db is modified mid-backup.
#

................................................................................
  execsql { 
    VACUUM;
    PRAGMA main.journal_mode;
  }
} {wal}
do_test walbak-1.5 {
  list [file size test.db] [file size test.db-wal]
} [list 1024 [log_file_size 6 1024]]
do_test walbak-1.6 {
  execsql { PRAGMA wal_checkpoint }
  list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [log_file_size 6 1024]]
do_test walbak-1.7 {
  execsql { 
    CREATE TABLE t2(a, b);
    INSERT INTO t2 SELECT * FROM t1;
    DROP TABLE t1;
  }
  list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [log_file_size 6 1024]]
do_test walbak-1.8 {
  execsql { VACUUM }
  list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [log_file_size 8 1024]]
do_test walbak-1.9 {
  execsql { PRAGMA wal_checkpoint }
  list [file size test.db] [file size test.db-wal]
} [list [expr 2*1024] [log_file_size 8 1024]]

#-------------------------------------------------------------------------
# Backups when the source db is modified mid-backup.
#
proc sig {{db db}} {
  $db eval { 
    PRAGMA integrity_check;







>
>
>



<
<
<







 







|



|







|



|



|







11
12
13
14
15
16
17
18
19
20
21
22
23



24
25
26
27
28
29
30
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/wal_common.tcl

do_not_use_codec

ifcapable !wal {finish_test ; return }





# Test organization:
# 
#   walback-1.*: Simple tests.
#   walback-2.*: Test backups when the source db is modified mid-backup.
#

................................................................................
  execsql { 
    VACUUM;
    PRAGMA main.journal_mode;
  }
} {wal}
do_test walbak-1.5 {
  list [file size test.db] [file size test.db-wal]
} [list 1024 [wal_file_size 6 1024]]
do_test walbak-1.6 {
  execsql { PRAGMA wal_checkpoint }
  list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [wal_file_size 6 1024]]
do_test walbak-1.7 {
  execsql { 
    CREATE TABLE t2(a, b);
    INSERT INTO t2 SELECT * FROM t1;
    DROP TABLE t1;
  }
  list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [wal_file_size 6 1024]]
do_test walbak-1.8 {
  execsql { VACUUM }
  list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [wal_file_size 8 1024]]
do_test walbak-1.9 {
  execsql { PRAGMA wal_checkpoint }
  list [file size test.db] [file size test.db-wal]
} [list [expr 2*1024] [wal_file_size 8 1024]]

#-------------------------------------------------------------------------
# Backups when the source db is modified mid-backup.
#
proc sig {{db db}} {
  $db eval { 
    PRAGMA integrity_check;

Changes to test/walcksum.test.

9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
..
88
89
90
91
92
93
94

















95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117



118
119
120
121
122
123
124
125









126
127
128
129
130
131
132
...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
...
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
...
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#
#***********************************************************************
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl


ifcapable !wal {finish_test ; return }

# This proc calculates checksums in the same way as those used by SQLite 
# in WAL files. If the $endian argument is "big", then checksums are
# calculated by interpreting data as an array of big-endian integers. If
# it is "little", data is interpreted as an array of little-endian integers.
#
proc log_cksum {endian ckv1 ckv2 blob} {
  upvar $ckv1 c1
  upvar $ckv2 c2

  if {$endian!="big" && $endian!="little"} {
    return -error "Bad value \"$endian\" - must be \"big\" or \"little\""
  }
  set scanpattern I*
  if {$endian == "little"} { set scanpattern i* }

  binary scan $blob $scanpattern values
  foreach {v1 v2} $values {
    set c1 [expr {($c1 + $v1 + $c2)&0xFFFFFFFF}]
    set c2 [expr {($c2 + $v2 + $c1)&0xFFFFFFFF}]
  }
}

proc log_file_size {nFrame pgsz} {
  expr {24 + ($pgsz+24)*$nFrame}
}

# Read and return the contents of file $filename. Treat the content as
# binary data.
#
proc readfile {filename} {
  set fd [open $filename]
  fconfigure $fd -encoding binary
  fconfigure $fd -translation binary
................................................................................
  binary scan [string range $data $offset [expr $offset+7]] II expect1 expect2
  set expect1 [expr $expect1&0xFFFFFFFF]
  set expect2 [expr $expect2&0xFFFFFFFF]

  expr {$c1==$expect1 && $c2==$expect2}
}

#
# File $filename must be a WAL file on disk. Compute the checksum for frame
# $iFrame in the file by interpreting data as $endian-endian integers 
# ($endian must be either "big" or "little"). Then write the computed 
# checksum into the file.
#
proc log_checksum_write {filename iFrame endian} {
  set data [readfile $filename]
................................................................................
  fconfigure $fd -encoding binary
  fconfigure $fd -translation binary
  seek $fd $offset
  puts -nonewline $fd $bin
  close $fd
}


















proc log_checksum_calc {data iFrame endian} {
  
  binary scan [string range $data 8 11] I pgsz
  if {$iFrame > 1} {
    set n [log_file_size [expr $iFrame-2] $pgsz]
    binary scan [string range $data [expr $n+16] [expr $n+23]] II c1 c2
  } else {
    set c1 0
    set c2 0
    log_cksum $endian c1 c2 [string range $data 0 23]
  }

  set n [log_file_size [expr $iFrame-1] $pgsz]
  log_cksum $endian c1 c2 [string range $data $n [expr $n+7]]
  log_cksum $endian c1 c2 [string range $data [expr $n+24] [expr $n+24+$pgsz-1]]

  list [expr $n+16] $c1 $c2
}

#
# File $filename must be a WAL file on disk. Set the 'magic' field of the
# WAL header to indicate that checksums are $endian-endian ($endian must be
# either "big" or "little").



#
proc log_checksum_writemagic {filename endian} {
  set val [expr {0x377f0682 | ($endian == "big" ? 1 : 0)}]
  set bin [binary format I $val]
  set fd [open $filename r+]
  fconfigure $fd -encoding binary
  fconfigure $fd -translation binary
  puts -nonewline $fd $bin









  close $fd
}

#-------------------------------------------------------------------------
# Test cases walcksum-1.* attempt to verify the following:
#
#   * That both native and non-native order checksum log files can 
................................................................................
    }

    file copy -force test.db test2.db
    file copy -force test.db-wal test2.db-wal
    db close

    list [file size test2.db] [file size test2.db-wal]
  } [list [expr 1024*3] [log_file_size 6 1024]]

  # Verify that the checksums are valid for all frames and that they
  # are calculated by interpreting data in native byte-order.
  #
  for {set f 1} {$f <= 6} {incr f} {
    do_test walcksum-1.$endian.2.$f {
      log_checksum_verify test2.db-wal $f $native
................................................................................
  #
  do_test walcksum-1.$endian.5.0 {
    execsql { 
      PRAGMA synchronous = NORMAL;
      INSERT INTO t1 VALUES(34, 'thirtyfour');
    }
    list [file size test.db] [file size test.db-wal]
  } [list [expr 1024*3] [log_file_size 8 1024]]
  for {set f 1} {$f <= 8} {incr f} {
    do_test walcksum-1.$endian.5.$f {
      log_checksum_verify test.db-wal $f $endian
    } {1}
  }

  # Now connect a second connection to the database. Check that this one
................................................................................
  } {ok 1 2 3 5 8 13 21 34}
  do_test walcksum-1.$endian.7.0 {
    execsql { 
      PRAGMA synchronous = NORMAL;
      INSERT INTO t1 VALUES(55, 'fiftyfive');
    } db2
    list [file size test.db] [file size test.db-wal]
  } [list [expr 1024*3] [log_file_size 10 1024]]
  for {set f 1} {$f <= 10} {incr f} {
    do_test walcksum-1.$endian.7.$f {
      log_checksum_verify test.db-wal $f $endian
    } {1}
  }

  # Now that both the recoverer and non-recoverer have added frames to the
................................................................................
    INSERT INTO t1 VALUES(1, randomblob(300));
    INSERT INTO t1 VALUES(2, randomblob(300));
    PRAGMA journal_mode = WAL;
    INSERT INTO t1 VALUES(3, randomblob(300));
  }

  file size test.db-wal
} [log_file_size 1 1024]
do_test walcksum-3.2 {
  file copy -force test.db-wal test2.db-wal
  file copy -force test.db test2.db
  sqlite3 db2 test2.db
  execsql { SELECT a FROM t1 } db2
} {1 2 3}
db2 close
file copy -force test.db test2.db


foreach incr {1 2 3 20 40 60 80 100 120 140 160 180 200 220 240 253 254 255} {
  do_test walcksum-3.3.$incr {
    set FAIL 0
    for {set iOff 0} {$iOff < [log_file_size 1 1024]} {incr iOff} {

      file copy -force test.db-wal test2.db-wal
      set fd [open test2.db-wal r+]
      fconfigure $fd -encoding binary
      fconfigure $fd -translation binary
  
      seek $fd $iOff







>



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|




|


|
|
|








>
>
>








>
>
>
>
>
>
>
>
>







 







|







 







|







 







|







 







|













|







9
10
11
12
13
14
15
16
17
18
19


























20
21
22
23
24
25
26
..
43
44
45
46
47
48
49

50
51
52
53
54
55
56
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
#
#***********************************************************************
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl

ifcapable !wal {finish_test ; return }



























# Read and return the contents of file $filename. Treat the content as
# binary data.
#
proc readfile {filename} {
  set fd [open $filename]
  fconfigure $fd -encoding binary
  fconfigure $fd -translation binary
................................................................................
  binary scan [string range $data $offset [expr $offset+7]] II expect1 expect2
  set expect1 [expr $expect1&0xFFFFFFFF]
  set expect2 [expr $expect2&0xFFFFFFFF]

  expr {$c1==$expect1 && $c2==$expect2}
}


# File $filename must be a WAL file on disk. Compute the checksum for frame
# $iFrame in the file by interpreting data as $endian-endian integers 
# ($endian must be either "big" or "little"). Then write the computed 
# checksum into the file.
#
proc log_checksum_write {filename iFrame endian} {
  set data [readfile $filename]
................................................................................
  fconfigure $fd -encoding binary
  fconfigure $fd -translation binary
  seek $fd $offset
  puts -nonewline $fd $bin
  close $fd
}

# Calculate and return the checksum for a particular frame in a WAL.
#
# Arguments are:
#
#   $data         Blob containing the entire contents of a WAL.
#
#   $iFrame       Frame number within the $data WAL. Frames are numbered 
#                 starting at 1.
#
#   $endian       One of "big" or "little".
#
# Returns a list of three elements, as follows:
#
#   * The byte offset of the checksum belonging to frame $iFrame in the WAL.
#   * The first integer in the calculated version of the checksum.
#   * The second integer in the calculated version of the checksum.
#
proc log_checksum_calc {data iFrame endian} {
  
  binary scan [string range $data 8 11] I pgsz
  if {$iFrame > 1} {
    set n [wal_file_size [expr $iFrame-2] $pgsz]
    binary scan [string range $data [expr $n+16] [expr $n+23]] II c1 c2
  } else {
    set c1 0
    set c2 0
    wal_cksum $endian c1 c2 [string range $data 0 23]
  }

  set n [wal_file_size [expr $iFrame-1] $pgsz]
  wal_cksum $endian c1 c2 [string range $data $n [expr $n+7]]
  wal_cksum $endian c1 c2 [string range $data [expr $n+24] [expr $n+24+$pgsz-1]]

  list [expr $n+16] $c1 $c2
}

#
# File $filename must be a WAL file on disk. Set the 'magic' field of the
# WAL header to indicate that checksums are $endian-endian ($endian must be
# either "big" or "little").
#
# Also update the wal header checksum (since the wal header contents may
# have changed).
#
proc log_checksum_writemagic {filename endian} {
  set val [expr {0x377f0682 | ($endian == "big" ? 1 : 0)}]
  set bin [binary format I $val]
  set fd [open $filename r+]
  fconfigure $fd -encoding binary
  fconfigure $fd -translation binary
  puts -nonewline $fd $bin

  seek $fd 0
  set blob [read $fd 24]
  set c1 0
  set c2 0
  wal_cksum $endian c1 c2 $blob 
  seek $fd 24
  puts -nonewline $fd [binary format II $c1 $c2]

  close $fd
}

#-------------------------------------------------------------------------
# Test cases walcksum-1.* attempt to verify the following:
#
#   * That both native and non-native order checksum log files can 
................................................................................
    }

    file copy -force test.db test2.db
    file copy -force test.db-wal test2.db-wal
    db close

    list [file size test2.db] [file size test2.db-wal]
  } [list [expr 1024*3] [wal_file_size 6 1024]]

  # Verify that the checksums are valid for all frames and that they
  # are calculated by interpreting data in native byte-order.
  #
  for {set f 1} {$f <= 6} {incr f} {
    do_test walcksum-1.$endian.2.$f {
      log_checksum_verify test2.db-wal $f $native
................................................................................
  #
  do_test walcksum-1.$endian.5.0 {
    execsql { 
      PRAGMA synchronous = NORMAL;
      INSERT INTO t1 VALUES(34, 'thirtyfour');
    }
    list [file size test.db] [file size test.db-wal]
  } [list [expr 1024*3] [wal_file_size 8 1024]]
  for {set f 1} {$f <= 8} {incr f} {
    do_test walcksum-1.$endian.5.$f {
      log_checksum_verify test.db-wal $f $endian
    } {1}
  }

  # Now connect a second connection to the database. Check that this one
................................................................................
  } {ok 1 2 3 5 8 13 21 34}
  do_test walcksum-1.$endian.7.0 {
    execsql { 
      PRAGMA synchronous = NORMAL;
      INSERT INTO t1 VALUES(55, 'fiftyfive');
    } db2
    list [file size test.db] [file size test.db-wal]
  } [list [expr 1024*3] [wal_file_size 10 1024]]
  for {set f 1} {$f <= 10} {incr f} {
    do_test walcksum-1.$endian.7.$f {
      log_checksum_verify test.db-wal $f $endian
    } {1}
  }

  # Now that both the recoverer and non-recoverer have added frames to the
................................................................................
    INSERT INTO t1 VALUES(1, randomblob(300));
    INSERT INTO t1 VALUES(2, randomblob(300));
    PRAGMA journal_mode = WAL;
    INSERT INTO t1 VALUES(3, randomblob(300));
  }

  file size test.db-wal
} [wal_file_size 1 1024]
do_test walcksum-3.2 {
  file copy -force test.db-wal test2.db-wal
  file copy -force test.db test2.db
  sqlite3 db2 test2.db
  execsql { SELECT a FROM t1 } db2
} {1 2 3}
db2 close
file copy -force test.db test2.db


foreach incr {1 2 3 20 40 60 80 100 120 140 160 180 200 220 240 253 254 255} {
  do_test walcksum-3.3.$incr {
    set FAIL 0
    for {set iOff 0} {$iOff < [wal_file_size 1 1024]} {incr iOff} {

      file copy -force test.db-wal test2.db-wal
      set fd [open test2.db-wal r+]
      fconfigure $fd -encoding binary
      fconfigure $fd -translation binary
  
      seek $fd $iOff

Changes to test/walcrash2.test.

10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
#***********************************************************************
#


set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl

ifcapable !wal {finish_test ; return }

proc wal_file_size {nFrame pgsz} { 
  expr {24 + ($pgsz+24)*$nFrame} 
}

#-------------------------------------------------------------------------
# This test case demonstrates a flaw in the wal-index manipulation that
# existed at one point: If a process crashes mid-transaction, it may have
# already added some entries to one of the hash-tables in the wal-index.
# If the transaction were to be explicitly rolled back at this point, the
# hash-table entries would be removed as part of the rollback. However,







>


<
<
<







10
11
12
13
14
15
16
17
18
19



20
21
22
23
24
25
26
#***********************************************************************
#


set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
ifcapable !wal {finish_test ; return }





#-------------------------------------------------------------------------
# This test case demonstrates a flaw in the wal-index manipulation that
# existed at one point: If a process crashes mid-transaction, it may have
# already added some entries to one of the hash-tables in the wal-index.
# If the transaction were to be explicitly rolled back at this point, the
# hash-table entries would be removed as part of the rollback. However,

Changes to test/walhook.test.

15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
...
100
101
102
103
104
105
106
107
108
109
110
111
112
# More specifically, this file contains regression tests for the 
# sqlite3_wal_hook() mechanism, including the sqlite3_wal_autocheckpoint()
# and "PRAGMA wal_autocheckpoint" convenience interfaces.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl


ifcapable !wal {finish_test ; return }

proc log_file_size {nFrame pgsz} {
  expr {24 + ($pgsz+24)*$nFrame}
}

set ::wal_hook [list]
proc wal_hook {zDb nEntry} {
  lappend ::wal_hook $zDb $nEntry
  return 0
}
db wal_hook wal_hook

................................................................................
  7 "INSERT INTO t4 VALUES(3, 'three')"   6   9
  8 "INSERT INTO t4 VALUES(4, 'four')"    8  11
  9 "INSERT INTO t4 VALUES(5, 'five')"    8  11
} {
  do_test walhook-2.$tn {
    execsql $sql
    list [file size test.db] [file size test.db-wal]
  } [list [expr $dbpages*1024] [log_file_size $logpages 1024]]
}

catch { db2 close }
catch { db close }
finish_test







>



<
<
<
<







 







|





15
16
17
18
19
20
21
22
23
24
25




26
27
28
29
30
31
32
..
97
98
99
100
101
102
103
104
105
106
107
108
109
# More specifically, this file contains regression tests for the 
# sqlite3_wal_hook() mechanism, including the sqlite3_wal_autocheckpoint()
# and "PRAGMA wal_autocheckpoint" convenience interfaces.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/wal_common.tcl

ifcapable !wal {finish_test ; return }





set ::wal_hook [list]
proc wal_hook {zDb nEntry} {
  lappend ::wal_hook $zDb $nEntry
  return 0
}
db wal_hook wal_hook

................................................................................
  7 "INSERT INTO t4 VALUES(3, 'three')"   6   9
  8 "INSERT INTO t4 VALUES(4, 'four')"    8  11
  9 "INSERT INTO t4 VALUES(5, 'five')"    8  11
} {
  do_test walhook-2.$tn {
    execsql $sql
    list [file size test.db] [file size test.db-wal]
  } [list [expr $dbpages*1024] [wal_file_size $logpages 1024]]
}

catch { db2 close }
catch { db close }
finish_test