xref: /sqlite-3.40.0/test/wal2.test (revision 0774bb59)
1# 2010 May 5
2#
3# The author disclaims copyright to this source code.  In place of
4# a legal notice, here is a blessing:
5#
6#    May you do good and not evil.
7#    May you find forgiveness for yourself and forgive others.
8#    May you share freely, never taking more than you give.
9#
10#***********************************************************************
11# This file implements regression tests for SQLite library.  The
12# focus of this file is testing the operation of the library in
13# "PRAGMA journal_mode=WAL" mode.
14#
15
16set testdir [file dirname $argv0]
17source $testdir/tester.tcl
18source $testdir/lock_common.tcl
19source $testdir/malloc_common.tcl
20source $testdir/wal_common.tcl
21
22set testprefix wal2
23
24ifcapable !wal {finish_test ; return }
25
26set sqlite_sync_count 0
27proc cond_incr_sync_count {adj} {
28  global sqlite_sync_count
29  if {$::tcl_platform(platform) == "windows"} {
30    incr sqlite_sync_count $adj
31  } {
32    ifcapable !dirsync {
33      incr sqlite_sync_count $adj
34    }
35  }
36}
37
38proc set_tvfs_hdr {file args} {
39
40  # Set $nHdr to the number of bytes in the wal-index header:
41  set nHdr 48
42  set nInt [expr {$nHdr/4}]
43
44  if {[llength $args]>2} {
45    error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"}
46  }
47
48  set blob [tvfs shm $file]
49
50  if {[llength $args]} {
51    set ia [lindex $args 0]
52    set ib $ia
53    if {[llength $args]==2} {
54      set ib [lindex $args 1]
55    }
56    binary scan $blob a[expr $nHdr*2]a* dummy tail
57    set blob [binary format i${nInt}i${nInt}a* $ia $ib $tail]
58    tvfs shm $file $blob
59  }
60
61  binary scan $blob i${nInt} ints
62  return $ints
63}
64
65proc incr_tvfs_hdr {file idx incrval} {
66  set ints [set_tvfs_hdr $file]
67  set v [lindex $ints $idx]
68  incr v $incrval
69  lset ints $idx $v
70  set_tvfs_hdr $file $ints
71}
72
73
74#-------------------------------------------------------------------------
75# Test case wal2-1.*:
76#
77# Set up a small database containing a single table. The database is not
78# checkpointed during the test - all content resides in the log file.
79#
80# Two connections are established to the database file - a writer ([db])
81# and a reader ([db2]). For each of the 8 integer fields in the wal-index
82# header (6 fields and 2 checksum values), do the following:
83#
84#   1. Modify the database using the writer.
85#
86#   2. Attempt to read the database using the reader. Before the reader
87#      has a chance to snapshot the wal-index header, increment one
88#      of the the integer fields (so that the reader ends up with a corrupted
89#      header).
90#
91#   3. Check that the reader recovers the wal-index and reads the correct
92#      database content.
93#
94do_test wal2-1.0 {
95  proc tvfs_cb {method filename args} {
96    set ::filename $filename
97    return SQLITE_OK
98  }
99
100  testvfs tvfs
101  tvfs script tvfs_cb
102  tvfs filter xShmOpen
103
104  sqlite3 db  test.db -vfs tvfs
105  sqlite3 db2 test.db -vfs tvfs
106
107  execsql {
108    PRAGMA journal_mode = WAL;
109    CREATE TABLE t1(a);
110  } db2
111  execsql {
112    INSERT INTO t1 VALUES(1);
113    INSERT INTO t1 VALUES(2);
114    INSERT INTO t1 VALUES(3);
115    INSERT INTO t1 VALUES(4);
116    SELECT count(a), sum(a) FROM t1;
117  }
118} {4 10}
119do_test wal2-1.1 {
120  execsql { SELECT count(a), sum(a) FROM t1 } db2
121} {4 10}
122
123set RECOVER [list                                      \
124  {0 1 lock exclusive}   {1 7 lock exclusive}          \
125  {1 7 unlock exclusive} {0 1 unlock exclusive}        \
126]
127set READ [list                                         \
128  {4 1 lock exclusive} {4 1 unlock exclusive}          \
129  {4 1 lock shared}    {4 1 unlock shared}             \
130]
131
132foreach {tn iInsert res wal_index_hdr_mod wal_locks} "
133         2    5   {5 15}    0             {$RECOVER $READ}
134         3    6   {6 21}    1             {$RECOVER $READ}
135         4    7   {7 28}    2             {$RECOVER $READ}
136         5    8   {8 36}    3             {$RECOVER $READ}
137         6    9   {9 45}    4             {$RECOVER $READ}
138         7   10   {10 55}   5             {$RECOVER $READ}
139         8   11   {11 66}   6             {$RECOVER $READ}
140         9   12   {12 78}   7             {$RECOVER $READ}
141        10   13   {13 91}   8             {$RECOVER $READ}
142        11   14   {14 105}  9             {$RECOVER $READ}
143        12   15   {15 120}  -1            {$READ}
144" {
145
146  do_test wal2-1.$tn.1 {
147    execsql { INSERT INTO t1 VALUES($iInsert) }
148    set ::locks [list]
149    proc tvfs_cb {method args} {
150      lappend ::locks [lindex $args 2]
151      return SQLITE_OK
152    }
153    tvfs filter xShmLock
154    if {$::wal_index_hdr_mod >= 0} {
155      incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1
156    }
157    execsql { SELECT count(a), sum(a) FROM t1 } db2
158  } $res
159
160  do_test wal2-1.$tn.2 {
161    set ::locks
162  } $wal_locks
163}
164db close
165db2 close
166tvfs delete
167forcedelete test.db test.db-wal test.db-journal
168
169#-------------------------------------------------------------------------
170# This test case is very similar to the previous one, except, after
171# the reader reads the corrupt wal-index header, but before it has
172# a chance to re-read it under the cover of the RECOVER lock, the
173# wal-index header is replaced with a valid, but out-of-date, header.
174#
175# Because the header checksum looks Ok, the reader does not run recovery,
176# it simply drops back to a READ lock and proceeds. But because the
177# header is out-of-date, the reader reads the out-of-date snapshot.
178#
179# After this, the header is corrupted again and the reader is allowed
180# to run recovery. This time, it sees an up-to-date snapshot of the
181# database file.
182#
183set WRITER [list 0 1 lock exclusive]
184set LOCKS  [list \
185  {0 1 lock exclusive} {0 1 unlock exclusive} \
186  {4 1 lock exclusive} {4 1 unlock exclusive} \
187  {4 1 lock shared}    {4 1 unlock shared}    \
188]
189do_test wal2-2.0 {
190
191  testvfs tvfs
192  tvfs script tvfs_cb
193  tvfs filter xShmOpen
194  proc tvfs_cb {method args} {
195    set ::filename [lindex $args 0]
196    return SQLITE_OK
197  }
198
199  sqlite3 db  test.db -vfs tvfs
200  sqlite3 db2 test.db -vfs tvfs
201
202  execsql {
203    PRAGMA journal_mode = WAL;
204    CREATE TABLE t1(a);
205  } db2
206  execsql {
207    INSERT INTO t1 VALUES(1);
208    INSERT INTO t1 VALUES(2);
209    INSERT INTO t1 VALUES(3);
210    INSERT INTO t1 VALUES(4);
211    SELECT count(a), sum(a) FROM t1;
212  }
213} {4 10}
214do_test wal2-2.1 {
215  execsql { SELECT count(a), sum(a) FROM t1 } db2
216} {4 10}
217
218foreach {tn iInsert res0 res1 wal_index_hdr_mod} {
219         2    5   {4 10}   {5 15}    0
220         3    6   {5 15}   {6 21}    1
221         4    7   {6 21}   {7 28}    2
222         5    8   {7 28}   {8 36}    3
223         6    9   {8 36}   {9 45}    4
224         7   10   {9 45}   {10 55}   5
225         8   11   {10 55}  {11 66}   6
226         9   12   {11 66}  {12 78}   7
227} {
228  tvfs filter xShmLock
229
230  do_test wal2-2.$tn.1 {
231    set oldhdr [set_tvfs_hdr $::filename]
232    execsql { INSERT INTO t1 VALUES($iInsert) }
233    execsql { SELECT count(a), sum(a) FROM t1 }
234  } $res1
235
236  do_test wal2-2.$tn.2 {
237    set ::locks [list]
238    proc tvfs_cb {method args} {
239      set lock [lindex $args 2]
240      lappend ::locks $lock
241      if {$lock == $::WRITER} {
242        set_tvfs_hdr $::filename $::oldhdr
243      }
244      return SQLITE_OK
245    }
246
247    if {$::wal_index_hdr_mod >= 0} {
248      incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1
249    }
250    execsql { SELECT count(a), sum(a) FROM t1 } db2
251  } $res0
252
253  do_test wal2-2.$tn.3 {
254    set ::locks
255  } $LOCKS
256
257  do_test wal2-2.$tn.4 {
258    set ::locks [list]
259    proc tvfs_cb {method args} {
260      set lock [lindex $args 2]
261      lappend ::locks $lock
262      return SQLITE_OK
263    }
264
265    if {$::wal_index_hdr_mod >= 0} {
266      incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1
267    }
268    execsql { SELECT count(a), sum(a) FROM t1 } db2
269  } $res1
270}
271db close
272db2 close
273tvfs delete
274forcedelete test.db test.db-wal test.db-journal
275
276
277if 0 {
278#-------------------------------------------------------------------------
279# This test case - wal2-3.* - tests the response of the library to an
280# SQLITE_BUSY when attempting to obtain a READ or RECOVER lock.
281#
282#   wal2-3.0 - 2: SQLITE_BUSY when obtaining a READ lock
283#   wal2-3.3 - 6: SQLITE_BUSY when obtaining a RECOVER lock
284#
285do_test wal2-3.0 {
286  proc tvfs_cb {method args} {
287    if {$method == "xShmLock"} {
288      if {[info exists ::locked]} { return SQLITE_BUSY }
289    }
290    return SQLITE_OK
291  }
292
293  proc busyhandler x {
294    if {$x>3} { unset -nocomplain ::locked }
295    return 0
296  }
297
298  testvfs tvfs
299  tvfs script tvfs_cb
300  sqlite3 db test.db -vfs tvfs
301  db busy busyhandler
302
303  execsql {
304    PRAGMA journal_mode = WAL;
305    CREATE TABLE t1(a);
306    INSERT INTO t1 VALUES(1);
307    INSERT INTO t1 VALUES(2);
308    INSERT INTO t1 VALUES(3);
309    INSERT INTO t1 VALUES(4);
310  }
311
312  set ::locked 1
313  info exists ::locked
314} {1}
315do_test wal2-3.1 {
316  execsql { SELECT count(a), sum(a) FROM t1 }
317} {4 10}
318do_test wal2-3.2 {
319  info exists ::locked
320} {0}
321
322do_test wal2-3.3 {
323  proc tvfs_cb {method args} {
324    if {$method == "xShmLock"} {
325      if {[info exists ::sabotage]} {
326        unset -nocomplain ::sabotage
327        incr_tvfs_hdr [lindex $args 0] 1 1
328      }
329      if {[info exists ::locked] && [lindex $args 2] == "RECOVER"} {
330        return SQLITE_BUSY
331      }
332    }
333    return SQLITE_OK
334  }
335  set ::sabotage 1
336  set ::locked 1
337  list [info exists ::sabotage] [info exists ::locked]
338} {1 1}
339do_test wal2-3.4 {
340  execsql { SELECT count(a), sum(a) FROM t1 }
341} {4 10}
342do_test wal2-3.5 {
343  list [info exists ::sabotage] [info exists ::locked]
344} {0 0}
345db close
346tvfs delete
347forcedelete test.db test.db-wal test.db-journal
348
349}
350
351#-------------------------------------------------------------------------
352# Test that a database connection using a VFS that does not support the
353# xShmXXX interfaces cannot open a WAL database.
354#
355do_test wal2-4.1 {
356  sqlite3 db test.db
357  execsql {
358    PRAGMA auto_vacuum = 0;
359    PRAGMA journal_mode = WAL;
360    CREATE TABLE data(x);
361    INSERT INTO data VALUES('need xShmOpen to see this');
362    PRAGMA wal_checkpoint;
363  }
364  # Three pages in the WAL file at this point: One copy of page 1 and two
365  # of the root page for table "data".
366} {wal 0 3 3}
367do_test wal2-4.2 {
368  db close
369  testvfs tvfs -noshm 1
370  sqlite3 db test.db -vfs tvfs
371  catchsql { SELECT * FROM data }
372} {1 {unable to open database file}}
373do_test wal2-4.3 {
374  db close
375  testvfs tvfs
376  sqlite3 db test.db -vfs tvfs
377  catchsql { SELECT * FROM data }
378} {0 {{need xShmOpen to see this}}}
379db close
380tvfs delete
381
382#-------------------------------------------------------------------------
383# Test that if a database connection is forced to run recovery before it
384# can perform a checkpoint, it does not transition into RECOVER state.
385#
386# UPDATE: This has now changed. When running a checkpoint, if recovery is
387# required the client grabs all exclusive locks (just as it would for a
388# recovery performed as a pre-cursor to a normal database transaction).
389#
390set expected_locks [list]
391lappend expected_locks {1 1 lock exclusive}   ;# Lock checkpoint
392lappend expected_locks {0 1 lock exclusive}   ;# Lock writer
393lappend expected_locks {2 6 lock exclusive}   ;# Lock recovery & all aReadMark[]
394lappend expected_locks {2 6 unlock exclusive} ;# Unlock recovery & aReadMark[]
395lappend expected_locks {0 1 unlock exclusive} ;# Unlock writer
396lappend expected_locks {3 1 lock exclusive}   ;# Lock aReadMark[0]
397lappend expected_locks {3 1 unlock exclusive} ;# Unlock aReadMark[0]
398lappend expected_locks {1 1 unlock exclusive} ;# Unlock checkpoint
399do_test wal2-5.1 {
400  proc tvfs_cb {method args} {
401    set ::shm_file [lindex $args 0]
402    if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] }
403    return $::tvfs_cb_return
404  }
405  set tvfs_cb_return SQLITE_OK
406
407  testvfs tvfs
408  tvfs script tvfs_cb
409
410  sqlite3 db test.db -vfs tvfs
411  execsql {
412    PRAGMA journal_mode = WAL;
413    CREATE TABLE x(y);
414    INSERT INTO x VALUES(1);
415  }
416
417  incr_tvfs_hdr $::shm_file 1 1
418  set ::locks [list]
419  execsql { PRAGMA wal_checkpoint }
420  set ::locks
421} $expected_locks
422db close
423tvfs delete
424
425#-------------------------------------------------------------------------
426# This block, test cases wal2-6.*, tests the operation of WAL with
427# "PRAGMA locking_mode=EXCLUSIVE" set.
428#
429#   wal2-6.1.*: Changing to WAL mode before setting locking_mode=exclusive.
430#
431#   wal2-6.2.*: Changing to WAL mode after setting locking_mode=exclusive.
432#
433#   wal2-6.3.*: Changing back to rollback mode from WAL mode after setting
434#               locking_mode=exclusive.
435#
436#   wal2-6.4.*: Check that xShmLock calls are omitted in exclusive locking
437#               mode.
438#
439#   wal2-6.5.*:
440#
441#   wal2-6.6.*: Check that if the xShmLock() to reaquire a WAL read-lock when
442#               exiting exclusive mode fails (i.e. SQLITE_IOERR), then the
443#               connection silently remains in exclusive mode.
444#
445do_test wal2-6.1.1 {
446  forcedelete test.db test.db-wal test.db-journal
447  sqlite3 db test.db
448  execsql {
449    Pragma Journal_Mode = Wal;
450  }
451} {wal}
452do_test wal2-6.1.2 {
453  execsql { PRAGMA lock_status }
454} {main unlocked temp closed}
455do_test wal2-6.1.3 {
456  execsql {
457    SELECT * FROM sqlite_master;
458    Pragma Locking_Mode = Exclusive;
459  }
460  execsql {
461    BEGIN;
462      CREATE TABLE t1(a, b);
463      INSERT INTO t1 VALUES(1, 2);
464    COMMIT;
465    PRAGMA lock_status;
466  }
467} {main exclusive temp closed}
468do_test wal2-6.1.4 {
469  execsql {
470    PRAGMA locking_mode = normal;
471    PRAGMA lock_status;
472  }
473} {normal main exclusive temp closed}
474do_test wal2-6.1.5 {
475  execsql {
476    SELECT * FROM t1;
477    PRAGMA lock_status;
478  }
479} {1 2 main shared temp closed}
480do_test wal2-6.1.6 {
481  execsql {
482    INSERT INTO t1 VALUES(3, 4);
483    PRAGMA lock_status;
484  }
485} {main shared temp closed}
486db close
487
488do_test wal2-6.2.1 {
489  forcedelete test.db test.db-wal test.db-journal
490  sqlite3 db test.db
491  execsql {
492    Pragma Locking_Mode = Exclusive;
493    Pragma Journal_Mode = Wal;
494    Pragma Lock_Status;
495  }
496} {exclusive wal main exclusive temp closed}
497do_test wal2-6.2.2 {
498  execsql {
499    BEGIN;
500      CREATE TABLE t1(a, b);
501      INSERT INTO t1 VALUES(1, 2);
502    COMMIT;
503    Pragma loCK_STATus;
504  }
505} {main exclusive temp closed}
506do_test wal2-6.2.3 {
507  db close
508  sqlite3 db test.db
509  execsql { SELECT * FROM sqlite_master }
510  execsql { PRAGMA LOCKING_MODE = EXCLUSIVE }
511} {exclusive}
512do_test wal2-6.2.4 {
513  execsql {
514    SELECT * FROM t1;
515    pragma lock_status;
516  }
517} {1 2 main shared temp closed}
518do_test wal2-6.2.5 {
519  execsql {
520    INSERT INTO t1 VALUES(3, 4);
521    pragma lock_status;
522  }
523} {main exclusive temp closed}
524do_test wal2-6.2.6 {
525  execsql {
526    PRAGMA locking_mode = NORMAL;
527    pragma lock_status;
528  }
529} {normal main exclusive temp closed}
530do_test wal2-6.2.7 {
531  execsql {
532    BEGIN IMMEDIATE; COMMIT;
533    pragma lock_status;
534  }
535} {main shared temp closed}
536do_test wal2-6.2.8 {
537  execsql {
538    PRAGMA locking_mode = EXCLUSIVE;
539    BEGIN IMMEDIATE; COMMIT;
540    PRAGMA locking_mode = NORMAL;
541  }
542  execsql {
543    SELECT * FROM t1;
544    pragma lock_status;
545  }
546} {1 2 3 4 main shared temp closed}
547do_test wal2-6.2.9 {
548  execsql {
549    INSERT INTO t1 VALUES(5, 6);
550    SELECT * FROM t1;
551    pragma lock_status;
552  }
553} {1 2 3 4 5 6 main shared temp closed}
554db close
555
556do_test wal2-6.3.1 {
557  forcedelete test.db test.db-wal test.db-journal
558  sqlite3 db test.db
559  execsql {
560    PRAGMA journal_mode = WAL;
561    PRAGMA locking_mode = exclusive;
562    BEGIN;
563      CREATE TABLE t1(x);
564      INSERT INTO t1 VALUES('Chico');
565      INSERT INTO t1 VALUES('Harpo');
566    COMMIT;
567  }
568  list [file exists test.db-wal] [file exists test.db-journal]
569} {1 0}
570do_test wal2-6.3.2 {
571  execsql { PRAGMA journal_mode = DELETE }
572  file exists test.db-wal
573} {0}
574do_test wal2-6.3.3 {
575  execsql { PRAGMA lock_status }
576} {main exclusive temp closed}
577do_test wal2-6.3.4 {
578  execsql {
579    BEGIN;
580      INSERT INTO t1 VALUES('Groucho');
581  }
582  list [file exists test.db-wal] [file exists test.db-journal]
583} {0 1}
584do_test wal2-6.3.5 {
585  execsql { PRAGMA lock_status }
586} {main exclusive temp closed}
587do_test wal2-6.3.6 {
588  execsql { COMMIT }
589  list [file exists test.db-wal] [file exists test.db-journal]
590} {0 1}
591do_test wal2-6.3.7 {
592  execsql { PRAGMA lock_status }
593} {main exclusive temp closed}
594db close
595
596
597# This test - wal2-6.4.* - uses a single database connection and the
598# [testvfs] instrumentation to test that xShmLock() is being called
599# as expected when a WAL database is used with locking_mode=exclusive.
600#
601do_test wal2-6.4.1 {
602  forcedelete test.db test.db-wal test.db-journal
603  proc tvfs_cb {method args} {
604    set ::shm_file [lindex $args 0]
605    if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] }
606    return "SQLITE_OK"
607  }
608  testvfs tvfs
609  tvfs script tvfs_cb
610  sqlite3 db test.db -vfs tvfs
611  set {} {}
612} {}
613
614set RECOVERY {
615  {0 1 lock exclusive} {1 7 lock exclusive}
616  {1 7 unlock exclusive} {0 1 unlock exclusive}
617}
618set READMARK0_READ {
619  {3 1 lock shared} {3 1 unlock shared}
620}
621set READMARK0_WRITE {
622  {3 1 lock shared}
623  {0 1 lock exclusive} {3 1 unlock shared}
624  {4 1 lock exclusive} {4 1 unlock exclusive} {4 1 lock shared}
625  {0 1 unlock exclusive} {4 1 unlock shared}
626}
627set READMARK1_SET {
628  {4 1 lock exclusive} {4 1 unlock exclusive}
629}
630set READMARK1_READ {
631  {4 1 lock shared} {4 1 unlock shared}
632}
633set READMARK1_WRITE {
634  {4 1 lock shared}
635    {0 1 lock exclusive} {0 1 unlock exclusive}
636  {4 1 unlock shared}
637}
638
639foreach {tn sql res expected_locks} {
640  2 {
641    PRAGMA auto_vacuum = 0;
642    PRAGMA journal_mode = WAL;
643    BEGIN;
644      CREATE TABLE t1(x);
645      INSERT INTO t1 VALUES('Leonard');
646      INSERT INTO t1 VALUES('Arthur');
647    COMMIT;
648  } {wal} {
649    $RECOVERY
650    $READMARK0_WRITE
651  }
652
653  3 {
654    # This test should do the READMARK1_SET locking to populate the
655    # aReadMark[1] slot with the current mxFrame value. Followed by
656    # READMARK1_READ to read the database.
657    #
658    SELECT * FROM t1
659  } {Leonard Arthur} {
660    $READMARK1_SET
661    $READMARK1_READ
662  }
663
664  4 {
665    # aReadMark[1] is already set to mxFrame. So just READMARK1_READ
666    # this time, not READMARK1_SET.
667    #
668    SELECT * FROM t1 ORDER BY x
669  } {Arthur Leonard} {
670    $READMARK1_READ
671  }
672
673  5 {
674    PRAGMA locking_mode = exclusive
675  } {exclusive} { }
676
677  6 {
678    INSERT INTO t1 VALUES('Julius Henry');
679    SELECT * FROM t1;
680  } {Leonard Arthur {Julius Henry}} {
681    $READMARK1_READ
682  }
683
684  7 {
685    INSERT INTO t1 VALUES('Karl');
686    SELECT * FROM t1;
687  } {Leonard Arthur {Julius Henry} Karl} { }
688
689  8 {
690    PRAGMA locking_mode = normal
691  } {normal} { }
692
693  9 {
694    SELECT * FROM t1 ORDER BY x
695  } {Arthur {Julius Henry} Karl Leonard} $READMARK1_READ
696
697  10 { DELETE FROM t1 } {} $READMARK1_WRITE
698
699  11 {
700    SELECT * FROM t1
701  } {} {
702    $READMARK1_SET
703    $READMARK1_READ
704  }
705} {
706
707  set L [list]
708  foreach el [subst $expected_locks] { lappend L $el }
709
710  set S ""
711  foreach sq [split $sql "\n"] {
712    set sq [string trim $sq]
713    if {[string match {#*} $sq]==0} {append S "$sq\n"}
714  }
715
716  set ::locks [list]
717  do_test wal2-6.4.$tn.1 { execsql $S } $res
718  do_test wal2-6.4.$tn.2 { set ::locks  } $L
719}
720
721db close
722tvfs delete
723
724do_test wal2-6.5.1 {
725  sqlite3 db test.db
726  execsql {
727    PRAGMA auto_vacuum = 0;
728    PRAGMA journal_mode = wal;
729    PRAGMA locking_mode = exclusive;
730    CREATE TABLE t2(a, b);
731    PRAGMA wal_checkpoint;
732    INSERT INTO t2 VALUES('I', 'II');
733    PRAGMA journal_mode;
734  }
735} {wal exclusive 0 2 2 wal}
736do_test wal2-6.5.2 {
737  execsql {
738    PRAGMA locking_mode = normal;
739    INSERT INTO t2 VALUES('III', 'IV');
740    PRAGMA locking_mode = exclusive;
741    SELECT * FROM t2;
742  }
743} {normal exclusive I II III IV}
744do_test wal2-6.5.3 {
745  execsql { PRAGMA wal_checkpoint }
746} {0 2 2}
747db close
748
749proc lock_control {method filename handle spec} {
750  foreach {start n op type} $spec break
751  if {$op == "lock"} { return SQLITE_IOERR }
752  return SQLITE_OK
753}
754do_test wal2-6.6.1 {
755  testvfs T
756  T script lock_control
757  T filter {}
758  sqlite3 db test.db -vfs T
759  execsql { SELECT * FROM sqlite_master }
760  execsql { PRAGMA locking_mode = exclusive }
761  execsql { INSERT INTO t2 VALUES('V', 'VI') }
762} {}
763do_test wal2-6.6.2 {
764  execsql { PRAGMA locking_mode = normal }
765  T filter xShmLock
766  execsql { INSERT INTO t2 VALUES('VII', 'VIII') }
767} {}
768do_test wal2-6.6.3 {
769  # At this point the connection should still be in exclusive-mode, even
770  # though it tried to exit exclusive-mode when committing the INSERT
771  # statement above. To exit exclusive mode, SQLite has to take a read-lock
772  # on the WAL file using xShmLock(). Since that call failed, it remains
773  # in exclusive mode.
774  #
775  sqlite3 db2 test.db -vfs T
776  catchsql { SELECT * FROM t2 } db2
777} {1 {database is locked}}
778do_test wal2-6.6.2 {
779  db2 close
780  T filter {}
781  execsql { INSERT INTO t2 VALUES('IX', 'X') }
782} {}
783do_test wal2-6.6.4 {
784  # This time, we have successfully exited exclusive mode. So the second
785  # connection can read the database.
786  sqlite3 db2 test.db -vfs T
787  catchsql { SELECT * FROM t2 } db2
788} {0 {I II III IV V VI VII VIII IX X}}
789
790db close
791db2 close
792T delete
793
794#-------------------------------------------------------------------------
795# Test a theory about the checksum algorithm. Theory was false and this
796# test did not provoke a bug.
797#
798forcedelete test.db test.db-wal test.db-journal
799do_test wal2-7.1.1 {
800  sqlite3 db test.db
801  execsql {
802    PRAGMA page_size = 4096;
803    PRAGMA journal_mode = WAL;
804    CREATE TABLE t1(a, b);
805  }
806  file size test.db
807} {4096}
808do_test wal2-7.1.2 {
809  forcecopy test.db test2.db
810  forcecopy test.db-wal test2.db-wal
811  hexio_write test2.db-wal 48 FF
812} {1}
813do_test wal2-7.1.3 {
814  sqlite3 db2 test2.db
815  execsql { PRAGMA wal_checkpoint } db2
816  execsql { SELECT * FROM sqlite_master } db2
817} {}
818db close
819db2 close
820forcedelete test.db test.db-wal test.db-journal
821do_test wal2-8.1.2 {
822  sqlite3 db test.db
823  execsql {
824    PRAGMA auto_vacuum=OFF;
825    PRAGMA page_size = 1024;
826    PRAGMA journal_mode = WAL;
827    CREATE TABLE t1(x);
828    INSERT INTO t1 VALUES(zeroblob(8188*1020));
829    CREATE TABLE t2(y);
830    PRAGMA wal_checkpoint;
831  }
832  execsql {
833    SELECT rootpage>=8192 FROM sqlite_master WHERE tbl_name = 't2';
834  }
835} {1}
836do_test wal2-8.1.3 {
837  execsql {
838    PRAGMA cache_size = 10;
839    CREATE TABLE t3(z);
840    BEGIN;
841      INSERT INTO t3 VALUES(randomblob(900));
842      INSERT INTO t3 SELECT randomblob(900) FROM t3;
843      INSERT INTO t2 VALUES('hello');
844      INSERT INTO t3 SELECT randomblob(900) FROM t3;
845      INSERT INTO t3 SELECT randomblob(900) FROM t3;
846      INSERT INTO t3 SELECT randomblob(900) FROM t3;
847      INSERT INTO t3 SELECT randomblob(900) FROM t3;
848      INSERT INTO t3 SELECT randomblob(900) FROM t3;
849      INSERT INTO t3 SELECT randomblob(900) FROM t3;
850    ROLLBACK;
851  }
852  execsql {
853    INSERT INTO t2 VALUES('goodbye');
854    INSERT INTO t3 SELECT randomblob(900) FROM t3;
855    INSERT INTO t3 SELECT randomblob(900) FROM t3;
856  }
857} {}
858do_test wal2-8.1.4 {
859  sqlite3 db2 test.db
860  execsql { SELECT * FROM t2 }
861} {goodbye}
862db2 close
863db close
864
865#-------------------------------------------------------------------------
866# Test that even if the checksums for both are valid, if the two copies
867# of the wal-index header in the wal-index do not match, the client
868# runs (or at least tries to run) database recovery.
869#
870#
871proc get_name {method args} { set ::filename [lindex $args 0] ; tvfs filter {} }
872testvfs tvfs
873tvfs script get_name
874tvfs filter xShmOpen
875
876forcedelete test.db test.db-wal test.db-journal
877do_test wal2-9.1 {
878  sqlite3 db test.db -vfs tvfs
879  execsql {
880    PRAGMA journal_mode = WAL;
881    CREATE TABLE x(y);
882    INSERT INTO x VALUES('Barton');
883    INSERT INTO x VALUES('Deakin');
884  }
885
886  # Set $wih(1) to the contents of the wal-index header after
887  # the frames associated with the first two rows in table 'x' have
888  # been inserted. Then insert one more row and set $wih(2)
889  # to the new value of the wal-index header.
890  #
891  # If the $wih(1) is written into the wal-index before running
892  # a read operation, the client will see only the first two rows. If
893  # $wih(2) is written into the wal-index, the client will see
894  # three rows. If an invalid header is written into the wal-index, then
895  # the client will run recovery and see three rows.
896  #
897  set wih(1) [set_tvfs_hdr $::filename]
898  execsql { INSERT INTO x VALUES('Watson') }
899  set wih(2) [set_tvfs_hdr $::filename]
900
901  sqlite3 db2 test.db -vfs tvfs
902  execsql { SELECT * FROM x } db2
903} {Barton Deakin Watson}
904
905foreach {tn hdr1 hdr2 res} [list                                            \
906  3  $wih(1)                $wih(1)                {Barton Deakin}          \
907  4  $wih(1)                $wih(2)                {Barton Deakin Watson}   \
908  5  $wih(2)                $wih(1)                {Barton Deakin Watson}   \
909  6  $wih(2)                $wih(2)                {Barton Deakin Watson}   \
910  7  $wih(1)                $wih(1)                {Barton Deakin}          \
911  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}
912] {
913  do_test wal2-9.$tn {
914    set_tvfs_hdr $::filename $hdr1 $hdr2
915    execsql { SELECT * FROM x } db2
916  } $res
917}
918
919db2 close
920db close
921
922#-------------------------------------------------------------------------
923# This block of tests - wal2-10.* - focus on the libraries response to
924# new versions of the wal or wal-index formats.
925#
926#   wal2-10.1.*: Test that the library refuses to "recover" a new WAL
927#                format.
928#
929#   wal2-10.2.*: Test that the library refuses to read or write a database
930#                if the wal-index version is newer than it understands.
931#
932# At time of writing, the only versions of the wal and wal-index formats
933# that exist are versions 3007000 (corresponding to SQLite version 3.7.0,
934# the first version of SQLite to feature wal mode).
935#
936do_test wal2-10.1.1 {
937  faultsim_delete_and_reopen
938  execsql {
939    PRAGMA journal_mode = WAL;
940    CREATE TABLE t1(a, b);
941    PRAGMA wal_checkpoint;
942    INSERT INTO t1 VALUES(1, 2);
943    INSERT INTO t1 VALUES(3, 4);
944  }
945  faultsim_save_and_close
946} {}
947do_test wal2-10.1.2 {
948  faultsim_restore_and_reopen
949  execsql { SELECT * FROM t1 }
950} {1 2 3 4}
951do_test wal2-10.1.3 {
952  faultsim_restore_and_reopen
953  set hdr [wal_set_walhdr test.db-wal]
954  lindex $hdr 1
955} {3007000}
956do_test wal2-10.1.4 {
957  lset hdr 1 3007001
958  wal_set_walhdr test.db-wal $hdr
959  catchsql { SELECT * FROM t1 }
960} {1 {unable to open database file}}
961
962testvfs tvfs -default 1
963do_test wal2-10.2.1 {
964  faultsim_restore_and_reopen
965  execsql { SELECT * FROM t1 }
966} {1 2 3 4}
967do_test wal2-10.2.2 {
968  set hdr [set_tvfs_hdr $::filename]
969  lindex $hdr 0
970} {3007000}
971do_test wal2-10.2.3 {
972  lset hdr 0 3007001
973  wal_fix_walindex_cksum hdr
974  set_tvfs_hdr $::filename $hdr
975  catchsql { SELECT * FROM t1 }
976} {1 {unable to open database file}}
977db close
978tvfs delete
979
980#-------------------------------------------------------------------------
981# This block of tests - wal2-11.* - tests that it is not possible to put
982# the library into an infinite loop by presenting it with a corrupt
983# hash table (one that appears to contain a single chain of infinite
984# length).
985#
986#   wal2-11.1.*: While reading the hash-table.
987#
988#   wal2-11.2.*: While writing the hash-table.
989#
990testvfs tvfs -default 1
991do_test wal2-11.0 {
992  faultsim_delete_and_reopen
993  execsql {
994    PRAGMA journal_mode = WAL;
995    CREATE TABLE t1(a, b, c);
996    INSERT INTO t1 VALUES(1, 2, 3);
997    INSERT INTO t1 VALUES(4, 5, 6);
998    INSERT INTO t1 VALUES(7, 8, 9);
999    SELECT * FROM t1;
1000  }
1001} {wal 1 2 3 4 5 6 7 8 9}
1002
1003do_test wal2-11.1.1 {
1004  sqlite3 db2 test.db
1005  execsql { SELECT name FROM sqlite_master } db2
1006} {t1}
1007
1008if {$::tcl_version>=8.5} {
1009  # Set all zeroed slots in the first hash table to invalid values.
1010  #
1011  set blob [string range [tvfs shm $::filename] 0 16383]
1012  set I [string range [tvfs shm $::filename] 16384 end]
1013  binary scan $I t* L
1014  set I [list]
1015  foreach p $L {
1016    lappend I [expr $p ? $p : 400]
1017  }
1018  append blob [binary format t* $I]
1019  tvfs shm $::filename $blob
1020  do_test wal2-11.2 {
1021    catchsql { INSERT INTO t1 VALUES(10, 11, 12) }
1022  } {1 {database disk image is malformed}}
1023
1024  # Fill up the hash table on the first page of shared memory with 0x55 bytes.
1025  #
1026  set blob [string range [tvfs shm $::filename] 0 16383]
1027  append blob [string repeat [binary format c 55] 16384]
1028  tvfs shm $::filename $blob
1029  do_test wal2-11.3 {
1030    catchsql { SELECT * FROM t1 } db2
1031  } {1 {database disk image is malformed}}
1032}
1033
1034db close
1035db2 close
1036tvfs delete
1037
1038#-------------------------------------------------------------------------
1039# If a connection is required to create a WAL or SHM file, it creates
1040# the new files with the same file-system permissions as the database
1041# file itself. Test this.
1042#
1043if {$::tcl_platform(platform) == "unix"} {
1044  faultsim_delete_and_reopen
1045  set umask [exec /bin/sh -c umask]
1046
1047  do_test wal2-12.1 {
1048    sqlite3 db test.db
1049    execsql {
1050      CREATE TABLE tx(y, z);
1051      PRAGMA journal_mode = WAL;
1052    }
1053    db close
1054    list [file exists test.db-wal] [file exists test.db-shm]
1055  } {0 0}
1056
1057  foreach {tn permissions} {
1058   1 00644
1059   2 00666
1060   3 00600
1061   4 00755
1062  } {
1063    set effective [format %.5o [expr $permissions & ~$umask]]
1064    do_test wal2-12.2.$tn.1 {
1065      file attributes test.db -permissions $permissions
1066      file attributes test.db -permissions
1067    } $permissions
1068    do_test wal2-12.2.$tn.2 {
1069      list [file exists test.db-wal] [file exists test.db-shm]
1070    } {0 0}
1071    do_test wal2-12.2.$tn.3 {
1072      sqlite3 db test.db
1073      execsql { INSERT INTO tx DEFAULT VALUES }
1074      list [file exists test.db-wal] [file exists test.db-shm]
1075    } {1 1}
1076    do_test wal2-12.2.$tn.4 {
1077      list [file attr test.db-wal -perm] [file attr test.db-shm -perm]
1078    } [list $effective $effective]
1079    do_test wal2-12.2.$tn.5 {
1080      db close
1081      list [file exists test.db-wal] [file exists test.db-shm]
1082    } {0 0}
1083  }
1084}
1085
1086#-------------------------------------------------------------------------
1087# Test the libraries response to discovering that one or more of the
1088# database, wal or shm files cannot be opened, or can only be opened
1089# read-only.
1090#
1091if {$::tcl_platform(platform) == "unix"} {
1092  proc perm {} {
1093    set L [list]
1094    foreach f {test.db test.db-wal test.db-shm} {
1095      if {[file exists $f]} {
1096        lappend L [file attr $f -perm]
1097      } else {
1098        lappend L {}
1099      }
1100    }
1101    set L
1102  }
1103
1104  faultsim_delete_and_reopen
1105  execsql {
1106    PRAGMA journal_mode = WAL;
1107    CREATE TABLE t1(a, b);
1108    PRAGMA wal_checkpoint;
1109    INSERT INTO t1 VALUES('3.14', '2.72');
1110  }
1111  do_test wal2-13.1.1 {
1112    list [file exists test.db-shm] [file exists test.db-wal]
1113  } {1 1}
1114  faultsim_save_and_close
1115
1116  foreach {tn db_perm wal_perm shm_perm can_open can_read can_write} {
1117    2   00644   00644   00644   1   1   1
1118    3   00644   00400   00644   1   1   0
1119    4   00644   00644   00400   1   0   0
1120    5   00400   00644   00644   1   1   0
1121
1122    7   00644   00000   00644   1   0   0
1123    8   00644   00644   00000   1   0   0
1124    9   00000   00644   00644   0   0   0
1125  } {
1126    faultsim_restore
1127    do_test wal2-13.$tn.1 {
1128      file attr test.db     -perm $db_perm
1129      file attr test.db-wal -perm $wal_perm
1130      file attr test.db-shm -perm $shm_perm
1131
1132      set     L [file attr test.db -perm]
1133      lappend L [file attr test.db-wal -perm]
1134      lappend L [file attr test.db-shm -perm]
1135    } [list $db_perm $wal_perm $shm_perm]
1136
1137    # If $can_open is true, then it should be possible to open a database
1138    # handle. Otherwise, if $can_open is 0, attempting to open the db
1139    # handle throws an "unable to open database file" exception.
1140    #
1141    set r(1) {0 ok}
1142    set r(0) {1 {unable to open database file}}
1143    do_test wal2-13.$tn.2 {
1144      list [catch {sqlite3 db test.db ; set {} ok} msg] $msg
1145    } $r($can_open)
1146
1147    if {$can_open} {
1148
1149      # If $can_read is true, then the client should be able to read from
1150      # the database file. If $can_read is false, attempting to read should
1151      # throw the "unable to open database file" exception.
1152      #
1153      set a(0) {1 {unable to open database file}}
1154      set a(1) {0 {3.14 2.72}}
1155      do_test wal2-13.$tn.3 {
1156        catchsql { SELECT * FROM t1 }
1157      } $a($can_read)
1158
1159      # Now try to write to the db file. If the client can read but not
1160      # write, then it should throw the familiar "unable to open db file"
1161      # exception. If it can read but not write, the exception should
1162      # be "attempt to write a read only database".
1163      #
1164      # If the client can read and write, the operation should succeed.
1165      #
1166      set b(0,0) {1 {unable to open database file}}
1167      set b(1,0) {1 {attempt to write a readonly database}}
1168      set b(1,1) {0 {}}
1169      do_test wal2-13.$tn.4 {
1170        catchsql { INSERT INTO t1 DEFAULT VALUES }
1171      } $b($can_read,$can_write)
1172    }
1173    catch { db close }
1174  }
1175}
1176
1177#-------------------------------------------------------------------------
1178# Test that "PRAGMA checkpoint_fullsync" appears to be working.
1179#
1180foreach {tn sql reslist} {
1181  1 { }                                 {10 0 4 0 6 0}
1182  2 { PRAGMA checkpoint_fullfsync = 1 } {10 4 4 2 6 2}
1183  3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0}
1184} {
1185  faultsim_delete_and_reopen
1186
1187  execsql {PRAGMA auto_vacuum = 0}
1188  execsql $sql
1189  do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 }   {}
1190  do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal}
1191
1192  set sqlite_sync_count 0
1193  set sqlite_fullsync_count 0
1194
1195  do_execsql_test wal2-14.$tn.2 {
1196    PRAGMA wal_autocheckpoint = 10;
1197    CREATE TABLE t1(a, b);                -- 2 wal syncs
1198    INSERT INTO t1 VALUES(1, 2);          -- 2 wal sync
1199    PRAGMA wal_checkpoint;                -- 1 wal sync, 1 db sync
1200    BEGIN;
1201      INSERT INTO t1 VALUES(3, 4);
1202      INSERT INTO t1 VALUES(5, 6);
1203    COMMIT;                               -- 2 wal sync
1204    PRAGMA wal_checkpoint;                -- 1 wal sync, 1 db sync
1205  } {10 0 3 3 0 1 1}
1206
1207  do_test wal2-14.$tn.3 {
1208    cond_incr_sync_count 1
1209    list $sqlite_sync_count $sqlite_fullsync_count
1210  } [lrange $reslist 0 1]
1211
1212  set sqlite_sync_count 0
1213  set sqlite_fullsync_count 0
1214
1215  do_test wal2-14.$tn.4 {
1216    execsql { INSERT INTO t1 VALUES(7, zeroblob(12*4096)) }
1217    list $sqlite_sync_count $sqlite_fullsync_count
1218  } [lrange $reslist 2 3]
1219
1220  set sqlite_sync_count 0
1221  set sqlite_fullsync_count 0
1222
1223  do_test wal2-14.$tn.5 {
1224    execsql { PRAGMA wal_autocheckpoint = 1000 }
1225    execsql { INSERT INTO t1 VALUES(9, 10) }
1226    execsql { INSERT INTO t1 VALUES(11, 12) }
1227    execsql { INSERT INTO t1 VALUES(13, 14) }
1228    db close
1229    list $sqlite_sync_count $sqlite_fullsync_count
1230  } [lrange $reslist 4 5]
1231}
1232
1233catch { db close }
1234
1235# PRAGMA checkpoint_fullsync
1236# PRAGMA fullfsync
1237# PRAGMA synchronous
1238#
1239foreach {tn settings restart_sync commit_sync ckpt_sync} {
1240  1  {0 0 off}     {0 0}  {0 0}  {0 0}
1241  2  {0 0 normal}  {1 0}  {0 0}  {2 0}
1242  3  {0 0 full}    {2 0}  {1 0}  {2 0}
1243
1244  4  {0 1 off}     {0 0}  {0 0}  {0 0}
1245  5  {0 1 normal}  {0 1}  {0 0}  {0 2}
1246  6  {0 1 full}    {0 2}  {0 1}  {0 2}
1247
1248  7  {1 0 off}     {0 0}  {0 0}  {0 0}
1249  8  {1 0 normal}  {1 0}  {0 0}  {0 2}
1250  9  {1 0 full}    {2 0}  {1 0}  {0 2}
1251
1252  10 {1 1 off}     {0 0}  {0 0}  {0 0}
1253  11 {1 1 normal}  {0 1}  {0 0}  {0 2}
1254  12 {1 1 full}    {0 2}  {0 1}  {0 2}
1255} {
1256  forcedelete test.db
1257
1258  testvfs tvfs -default 1
1259  tvfs filter xSync
1260  tvfs script xSyncCb
1261  proc xSyncCb {method file fileid flags} {
1262    incr ::sync($flags)
1263  }
1264
1265  sqlite3 db test.db
1266  do_execsql_test 15.$tn.1 "
1267    PRAGMA page_size = 4096;
1268    CREATE TABLE t1(x);
1269    PRAGMA wal_autocheckpoint = OFF;
1270    PRAGMA journal_mode = WAL;
1271    PRAGMA checkpoint_fullfsync = [lindex $settings 0];
1272    PRAGMA fullfsync = [lindex $settings 1];
1273    PRAGMA synchronous = [lindex $settings 2];
1274  " {0 wal}
1275
1276if { $tn==2} breakpoint
1277  do_test 15.$tn.2 {
1278    set sync(normal) 0
1279    set sync(full) 0
1280    execsql { INSERT INTO t1 VALUES('abc') }
1281    list $::sync(normal) $::sync(full)
1282  } $restart_sync
1283
1284  do_test 15.$tn.3 {
1285    set sync(normal) 0
1286    set sync(full) 0
1287    execsql { INSERT INTO t1 VALUES('abc') }
1288    list $::sync(normal) $::sync(full)
1289  } $commit_sync
1290
1291  do_test 15.$tn.4 {
1292    set sync(normal) 0
1293    set sync(full) 0
1294    execsql { INSERT INTO t1 VALUES('def') }
1295    list $::sync(normal) $::sync(full)
1296  } $commit_sync
1297
1298  do_test 15.$tn.5 {
1299    set sync(normal) 0
1300    set sync(full) 0
1301    execsql { PRAGMA wal_checkpoint }
1302    list $::sync(normal) $::sync(full)
1303  } $ckpt_sync
1304
1305  db close
1306  tvfs delete
1307}
1308
1309
1310
1311finish_test
1312