xref: /sqlite-3.40.0/test/wal3.test (revision cf2ad7ae)
1db9d981aSdan# 2010 April 13
2db9d981aSdan#
3db9d981aSdan# The author disclaims copyright to this source code.  In place of
4db9d981aSdan# a legal notice, here is a blessing:
5db9d981aSdan#
6db9d981aSdan#    May you do good and not evil.
7db9d981aSdan#    May you find forgiveness for yourself and forgive others.
8db9d981aSdan#    May you share freely, never taking more than you give.
9db9d981aSdan#
10db9d981aSdan#***********************************************************************
11db9d981aSdan# This file implements regression tests for SQLite library.  The
12db9d981aSdan# focus of this file is testing the operation of the library in
13db9d981aSdan# "PRAGMA journal_mode=WAL" mode.
14db9d981aSdan#
15*cf2ad7aeSdan# TESTRUNNER: slow
16db9d981aSdan
17db9d981aSdanset testdir [file dirname $argv0]
18db9d981aSdansource $testdir/tester.tcl
19db9d981aSdansource $testdir/lock_common.tcl
20db9d981aSdansource $testdir/wal_common.tcl
21db9d981aSdansource $testdir/malloc_common.tcl
22db9d981aSdanifcapable !wal {finish_test ; return }
23db9d981aSdan
24db9d981aSdanset a_string_counter 1
25db9d981aSdanproc a_string {n} {
26db9d981aSdan  global a_string_counter
27db9d981aSdan  incr a_string_counter
28db9d981aSdan  string range [string repeat "${a_string_counter}." $n] 1 $n
29db9d981aSdan}
30db9d981aSdandb func a_string a_string
31db9d981aSdan
32db9d981aSdan#-------------------------------------------------------------------------
33db9d981aSdan# When a rollback or savepoint rollback occurs, the client may remove
34db9d981aSdan# elements from one of the hash tables in the wal-index. This block
35db9d981aSdan# of test cases tests that nothing appears to go wrong when this is
36db9d981aSdan# done.
37db9d981aSdan#
38db9d981aSdando_test wal3-1.0 {
39db9d981aSdan  execsql {
40c60f10a2Sdan    PRAGMA cache_size = 2000;
41db9d981aSdan    PRAGMA page_size = 1024;
42db9d981aSdan    PRAGMA auto_vacuum = off;
43db9d981aSdan    PRAGMA synchronous = normal;
44db9d981aSdan    PRAGMA journal_mode = WAL;
45db9d981aSdan    PRAGMA wal_autocheckpoint = 0;
46db9d981aSdan    BEGIN;
47db9d981aSdan      CREATE TABLE t1(x);
48db9d981aSdan      INSERT INTO t1 VALUES( a_string(800) );                  /*    1 */
49db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    2 */
50db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    4 */
51db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    8 */
52db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   16 */
53db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   32 */
54db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   64 */
55db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  128*/
56db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  256 */
57db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  512 */
58db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /* 1024 */
59db9d981aSdan      INSERT INTO t1 SELECT a_string(800) FROM t1;             /* 2048 */
603a3803b6Sdan      INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 1970;  /* 4018 */
61db9d981aSdan    COMMIT;
62db9d981aSdan    PRAGMA cache_size = 10;
63db9d981aSdan  }
64bec9d65dSdrh  set x [wal_frame_count test.db-wal 1024]
65be7721d1Sdan  if {[permutation]=="memsubsys1"} {
66bec9d65dSdrh    if {$x==4251 || $x==4290} {set x 4056}
67bec9d65dSdrh  }
68bec9d65dSdrh  set x
69bec9d65dSdrh} 4056
70db9d981aSdan
713a3803b6Sdanfor {set i 1} {$i < 50} {incr i} {
72db9d981aSdan
73db9d981aSdan  do_test wal3-1.$i.1 {
74db9d981aSdan    set str [a_string 800]
75db9d981aSdan    execsql { UPDATE t1 SET x = $str WHERE rowid = $i }
76db9d981aSdan    lappend L [wal_frame_count test.db-wal 1024]
77db9d981aSdan    execsql {
78db9d981aSdan      BEGIN;
79db9d981aSdan        INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 100;
80db9d981aSdan      ROLLBACK;
81db9d981aSdan      PRAGMA integrity_check;
82db9d981aSdan    }
83db9d981aSdan  } {ok}
84db9d981aSdan
85db9d981aSdan  # Check that everything looks OK from the point of view of an
86db9d981aSdan  # external connection.
87db9d981aSdan  #
88db9d981aSdan  sqlite3 db2 test.db
89db9d981aSdan  do_test wal3-1.$i.2 {
90db9d981aSdan    execsql { SELECT count(*) FROM t1 } db2
913a3803b6Sdan  } 4018
92db9d981aSdan  do_test wal3-1.$i.3 {
93db9d981aSdan    execsql { SELECT x FROM t1 WHERE rowid = $i }
94db9d981aSdan  } $str
95db9d981aSdan  do_test wal3-1.$i.4 {
96db9d981aSdan    execsql { PRAGMA integrity_check } db2
97db9d981aSdan  } {ok}
98db9d981aSdan  db2 close
99db9d981aSdan
100db9d981aSdan  # Check that the file-system in its current state can be recovered.
101db9d981aSdan  #
102fda06befSmistachkin  forcecopy test.db test2.db
103fda06befSmistachkin  forcecopy test.db-wal test2.db-wal
104fda06befSmistachkin  forcedelete test2.db-journal
105db9d981aSdan  sqlite3 db2 test2.db
106db9d981aSdan  do_test wal3-1.$i.5 {
107db9d981aSdan    execsql { SELECT count(*) FROM t1 } db2
1083a3803b6Sdan  } 4018
109db9d981aSdan  do_test wal3-1.$i.6 {
110db9d981aSdan    execsql { SELECT x FROM t1 WHERE rowid = $i }
111db9d981aSdan  } $str
112db9d981aSdan  do_test wal3-1.$i.7 {
113db9d981aSdan    execsql { PRAGMA integrity_check } db2
114db9d981aSdan  } {ok}
115db9d981aSdan  db2 close
116db9d981aSdan}
117db9d981aSdan
118bfcaec72Sdanproc byte_is_zero {file offset} {
119bfcaec72Sdan  if {[file size test.db] <= $offset} { return 1 }
120bfcaec72Sdan  expr { [hexio_read $file $offset 1] == "00" }
121bfcaec72Sdan}
122bfcaec72Sdan
123a4a9095eSdando_multiclient_test i {
12483f42d1bSdan
125a4a9095eSdan  set testname(1) multiproc
126a4a9095eSdan  set testname(2) singleproc
127a4a9095eSdan  set tn $testname($i)
12883f42d1bSdan
12983f42d1bSdan  do_test wal3-2.$tn.1 {
130a4a9095eSdan    sql1 {
13183f42d1bSdan      PRAGMA page_size = 1024;
13283f42d1bSdan      PRAGMA journal_mode = WAL;
13383f42d1bSdan    }
134a4a9095eSdan    sql1 {
13583f42d1bSdan      CREATE TABLE t1(a, b);
13683f42d1bSdan      INSERT INTO t1 VALUES(1, 'one');
13783f42d1bSdan      BEGIN;
13883f42d1bSdan        SELECT * FROM t1;
13983f42d1bSdan    }
14083f42d1bSdan  } {1 one}
14183f42d1bSdan  do_test wal3-2.$tn.2 {
14283f42d1bSdan    sql2 {
14383f42d1bSdan      CREATE TABLE t2(a, b);
14483f42d1bSdan      INSERT INTO t2 VALUES(2, 'two');
14583f42d1bSdan      BEGIN;
14683f42d1bSdan        SELECT * FROM t2;
14783f42d1bSdan    }
14883f42d1bSdan  } {2 two}
14983f42d1bSdan  do_test wal3-2.$tn.3 {
15083f42d1bSdan    sql3 {
15183f42d1bSdan      CREATE TABLE t3(a, b);
15283f42d1bSdan      INSERT INTO t3 VALUES(3, 'three');
15383f42d1bSdan      BEGIN;
15483f42d1bSdan        SELECT * FROM t3;
15583f42d1bSdan    }
15683f42d1bSdan  } {3 three}
15783f42d1bSdan
15883f42d1bSdan  # Try to checkpoint the database using [db]. It should be possible to
159d764c7deSdan  # checkpoint everything except the table added by [db3] (checkpointing
16083f42d1bSdan  # these frames would clobber the snapshot currently being used by [db2]).
16183f42d1bSdan  #
162d764c7deSdan  # After [db2] has committed, a checkpoint can copy the entire log to the
163d764c7deSdan  # database file. Checkpointing after [db3] has committed is therefore a
164d764c7deSdan  # no-op, as the entire log has already been backfilled.
165d764c7deSdan  #
16683f42d1bSdan  do_test wal3-2.$tn.4 {
167a4a9095eSdan    sql1 {
16883f42d1bSdan      COMMIT;
16983f42d1bSdan      PRAGMA wal_checkpoint;
17083f42d1bSdan    }
171bfcaec72Sdan    byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]
172bfcaec72Sdan  } {1}
173d764c7deSdan  do_test wal3-2.$tn.5 {
174d764c7deSdan    sql2 {
175d764c7deSdan      COMMIT;
176d764c7deSdan      PRAGMA wal_checkpoint;
177d764c7deSdan    }
178bfcaec72Sdan    list [byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]]   \
179bfcaec72Sdan         [byte_is_zero test.db [expr $AUTOVACUUM ? 5*1024 : 4*1024]]
180bfcaec72Sdan  } {0 1}
181d764c7deSdan  do_test wal3-2.$tn.6 {
182d764c7deSdan    sql3 {
183d764c7deSdan      COMMIT;
184d764c7deSdan      PRAGMA wal_checkpoint;
185d764c7deSdan    }
186bfcaec72Sdan    list [byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]]   \
187bfcaec72Sdan         [byte_is_zero test.db [expr $AUTOVACUUM ? 5*1024 : 4*1024]]
188bfcaec72Sdan  } {0 1}
18983f42d1bSdan}
190d764c7deSdancatch {db close}
191d764c7deSdan
192d764c7deSdan#-------------------------------------------------------------------------
193d764c7deSdan# Test that that for the simple test:
194d764c7deSdan#
195d764c7deSdan#   CREATE TABLE x(y);
196d764c7deSdan#   INSERT INTO x VALUES('z');
197d764c7deSdan#   PRAGMA wal_checkpoint;
198d764c7deSdan#
199d764c7deSdan# in WAL mode the xSync method is invoked as expected for each of
200d764c7deSdan# synchronous=off, synchronous=normal and synchronous=full.
201d764c7deSdan#
202d764c7deSdanforeach {tn syncmode synccount} {
203d764c7deSdan  1 off
204d764c7deSdan    {}
205d764c7deSdan  2 normal
206d764c7deSdan    {test.db-wal normal test.db normal}
207d764c7deSdan  3 full
208d764c7deSdan    {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}
209d764c7deSdan} {
210d764c7deSdan
211d764c7deSdan  proc sync_counter {args} {
212d764c7deSdan    foreach {method filename id flags} $args break
213d764c7deSdan    lappend ::syncs [file tail $filename] $flags
214d764c7deSdan  }
215d764c7deSdan  do_test wal3-3.$tn {
216fda06befSmistachkin    forcedelete test.db test.db-wal test.db-journal
217d764c7deSdan
218d764c7deSdan    testvfs T
219d764c7deSdan    T filter {}
220d764c7deSdan    T script sync_counter
221d764c7deSdan    sqlite3 db test.db -vfs T
222d764c7deSdan
223d764c7deSdan    execsql "PRAGMA synchronous = $syncmode"
224108e5a9aSdrh    execsql "PRAGMA checkpoint_fullfsync = 0"
225d764c7deSdan    execsql { PRAGMA journal_mode = WAL }
2264eb02a45Sdrh    execsql { CREATE TABLE filler(a,b,c); }
227d764c7deSdan
228d764c7deSdan    set ::syncs [list]
229d764c7deSdan    T filter xSync
230d764c7deSdan    execsql {
231d764c7deSdan      CREATE TABLE x(y);
232d764c7deSdan      INSERT INTO x VALUES('z');
233d764c7deSdan      PRAGMA wal_checkpoint;
234d764c7deSdan    }
235d764c7deSdan    T filter {}
236d764c7deSdan    set ::syncs
237d764c7deSdan  } $synccount
238d764c7deSdan
239d764c7deSdan  db close
240d764c7deSdan  T delete
241d764c7deSdan}
242d764c7deSdan
243ef4ee8f2Sdan
244ef4ee8f2Sdan#-------------------------------------------------------------------------
245ef4ee8f2Sdan# Only one client may run recovery at a time. Test this mechanism.
246ef4ee8f2Sdan#
247ef4ee8f2Sdan# When client-2 tries to open a read transaction while client-1 is
248ef4ee8f2Sdan# running recovery, it fails to obtain a lock on an aReadMark[] slot
249ef4ee8f2Sdan# (because they are all locked by recovery). It then tries to obtain
250ef4ee8f2Sdan# a shared lock on the RECOVER lock to see if there really is a
251ef4ee8f2Sdan# recovery running or not.
252ef4ee8f2Sdan#
253ef4ee8f2Sdan# This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR
254ef4ee8f2Sdan# being returned when client-2 attempts a shared lock on the RECOVER byte.
255ef4ee8f2Sdan#
256ef4ee8f2Sdan# An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An
257ef4ee8f2Sdan# SQLITE_IOERR should be returned to the caller.
258ef4ee8f2Sdan#
259ef4ee8f2Sdando_test wal3-5.1 {
260ef4ee8f2Sdan  faultsim_delete_and_reopen
261ef4ee8f2Sdan  execsql {
262ef4ee8f2Sdan    PRAGMA journal_mode = WAL;
263ef4ee8f2Sdan    CREATE TABLE t1(a, b);
264ef4ee8f2Sdan    INSERT INTO t1 VALUES(1, 2);
265ef4ee8f2Sdan    INSERT INTO t1 VALUES(3, 4);
266ef4ee8f2Sdan  }
267ef4ee8f2Sdan  faultsim_save_and_close
268ef4ee8f2Sdan} {}
269ef4ee8f2Sdan
270ef4ee8f2Sdantestvfs T -default 1
271ef4ee8f2SdanT script method_callback
272ef4ee8f2Sdan
273ef4ee8f2Sdanproc method_callback {method args} {
274ef4ee8f2Sdan  if {$method == "xShmBarrier"} {
275ef4ee8f2Sdan    incr ::barrier_count
27613a3cb82Sdan    if {$::barrier_count == 2} {
277ef4ee8f2Sdan      # This code is executed within the xShmBarrier() callback invoked
278ef4ee8f2Sdan      # by the client running recovery as part of writing the recovered
279ef4ee8f2Sdan      # wal-index header. If a second client attempts to access the
280ef4ee8f2Sdan      # database now, it reads a corrupt (partially written) wal-index
281ef4ee8f2Sdan      # header. But it cannot even get that far, as the first client
282ef4ee8f2Sdan      # is still holding all the locks (recovery takes an exclusive lock
283ef4ee8f2Sdan      # on *all* db locks, preventing access by any other client).
284ef4ee8f2Sdan      #
285ef4ee8f2Sdan      # If global variable ::wal3_do_lockfailure is non-zero, then set
286ef4ee8f2Sdan      # things up so that an IO error occurs within an xShmLock() callback
287ef4ee8f2Sdan      # made by the second client (aka [db2]).
288ef4ee8f2Sdan      #
289ef4ee8f2Sdan      sqlite3 db2 test.db
290ef4ee8f2Sdan      if { $::wal3_do_lockfailure } { T filter xShmLock }
291ef4ee8f2Sdan      set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ]
292ef4ee8f2Sdan      T filter {}
293ef4ee8f2Sdan      db2 close
294ef4ee8f2Sdan    }
295ef4ee8f2Sdan  }
296ef4ee8f2Sdan
297ef4ee8f2Sdan  if {$method == "xShmLock"} {
298ef4ee8f2Sdan    foreach {file handle spec} $args break
299ef4ee8f2Sdan    if { $spec == "2 1 lock shared" } {
300ef4ee8f2Sdan      return SQLITE_IOERR
301ef4ee8f2Sdan    }
302ef4ee8f2Sdan  }
303ef4ee8f2Sdan
304ef4ee8f2Sdan  return SQLITE_OK
305ef4ee8f2Sdan}
306ef4ee8f2Sdan
307ef4ee8f2Sdan# Test a normal SQLITE_BUSY return.
308ef4ee8f2Sdan#
309ef4ee8f2SdanT filter xShmBarrier
310ef4ee8f2Sdanset testrc ""
311ef4ee8f2Sdanset testmsg ""
312ef4ee8f2Sdanset barrier_count 0
313ef4ee8f2Sdanset wal3_do_lockfailure 0
314ef4ee8f2Sdando_test wal3-5.2 {
315ef4ee8f2Sdan  faultsim_restore_and_reopen
316ef4ee8f2Sdan  execsql { SELECT * FROM t1 }
317ef4ee8f2Sdan} {1 2 3 4}
318ef4ee8f2Sdando_test wal3-5.3 {
319ef4ee8f2Sdan  list $::testrc $::testmsg
320ef4ee8f2Sdan} {1 {database is locked}}
321ef4ee8f2Sdandb close
322ef4ee8f2Sdan
323ef4ee8f2Sdan# Test an SQLITE_IOERR return.
324ef4ee8f2Sdan#
325ef4ee8f2SdanT filter xShmBarrier
326ef4ee8f2Sdanset barrier_count 0
327ef4ee8f2Sdanset wal3_do_lockfailure 1
328ef4ee8f2Sdanset testrc ""
329ef4ee8f2Sdanset testmsg ""
330ef4ee8f2Sdando_test wal3-5.4 {
331ef4ee8f2Sdan  faultsim_restore_and_reopen
332ef4ee8f2Sdan  execsql { SELECT * FROM t1 }
333ef4ee8f2Sdan} {1 2 3 4}
334ef4ee8f2Sdando_test wal3-5.5 {
335ef4ee8f2Sdan  list $::testrc $::testmsg
336ef4ee8f2Sdan} {1 {disk I/O error}}
3370153a9bcSdan
3380dc7b74fSdandb close
3390dc7b74fSdanT delete
3400dc7b74fSdan
341ef4ee8f2Sdan#-------------------------------------------------------------------------
342493cc590Sdan# When opening a read-transaction on a database, if the entire log has
343493cc590Sdan# already been copied to the database file, the reader grabs a special
344640aac48Sdan# kind of read lock (on aReadMark[0]). This set of test cases tests the
345640aac48Sdan# outcome of the following:
346ef4ee8f2Sdan#
347493cc590Sdan#   + The reader discovering that between the time when it determined
348493cc590Sdan#     that the log had been completely backfilled and the lock is obtained
349493cc590Sdan#     that a writer has written to the log. In this case the reader should
350493cc590Sdan#     acquire a different read-lock (not aReadMark[0]) and read the new
351493cc590Sdan#     snapshot.
352493cc590Sdan#
353493cc590Sdan#   + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY.
354493cc590Sdan#     This can happen if a checkpoint is ongoing. In this case also simply
355493cc590Sdan#     obtain a different read-lock.
356493cc590Sdan#
357493cc590Sdancatch {db close}
358493cc590Sdantestvfs T -default 1
359493cc590Sdando_test wal3-6.1.1 {
360fda06befSmistachkin  forcedelete test.db test.db-journal test.db wal
361493cc590Sdan  sqlite3 db test.db
3627fa65fbfSdan  execsql { PRAGMA auto_vacuum = off }
363493cc590Sdan  execsql { PRAGMA journal_mode = WAL }
364493cc590Sdan  execsql {
365493cc590Sdan    CREATE TABLE t1(a, b);
366493cc590Sdan    INSERT INTO t1 VALUES('o', 't');
367493cc590Sdan    INSERT INTO t1 VALUES('t', 'f');
368493cc590Sdan  }
369493cc590Sdan} {}
370493cc590Sdando_test wal3-6.1.2 {
371493cc590Sdan  sqlite3 db2 test.db
372493cc590Sdan  sqlite3 db3 test.db
373493cc590Sdan  execsql { BEGIN ; SELECT * FROM t1 } db3
374493cc590Sdan} {o t t f}
375493cc590Sdando_test wal3-6.1.3 {
376493cc590Sdan  execsql { PRAGMA wal_checkpoint } db2
3770774bb59Sdan} {0 4 4}
378493cc590Sdan
379493cc590Sdan# At this point the log file has been fully checkpointed. However,
380493cc590Sdan# connection [db3] holds a lock that prevents the log from being wrapped.
381493cc590Sdan# Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But
382493cc590Sdan# as it is obtaining the lock, [db2] appends to the log file.
383493cc590Sdan#
384493cc590SdanT filter xShmLock
385493cc590SdanT script lock_callback
386493cc590Sdanproc lock_callback {method file handle spec} {
387493cc590Sdan  if {$spec == "3 1 lock shared"} {
388493cc590Sdan    # This is the callback for [db] to obtain the read lock on aReadMark[0].
389493cc590Sdan    # Disable future callbacks using [T filter {}] and write to the log
390493cc590Sdan    # file using [db2]. [db3] is preventing [db2] from wrapping the log
391493cc590Sdan    # here, so this is an append.
392493cc590Sdan    T filter {}
393493cc590Sdan    db2 eval { INSERT INTO t1 VALUES('f', 's') }
394493cc590Sdan  }
395493cc590Sdan  return SQLITE_OK
396493cc590Sdan}
397493cc590Sdando_test wal3-6.1.4 {
398493cc590Sdan  execsql {
399493cc590Sdan    BEGIN;
400493cc590Sdan    SELECT * FROM t1;
401493cc590Sdan  }
402493cc590Sdan} {o t t f f s}
403493cc590Sdan
404493cc590Sdan# [db] should be left holding a read-lock on some slot other than
405493cc590Sdan# aReadMark[0]. Test this by demonstrating that the read-lock is preventing
406493cc590Sdan# the log from being wrapped.
407493cc590Sdan#
408493cc590Sdando_test wal3-6.1.5 {
409493cc590Sdan  db3 eval COMMIT
410493cc590Sdan  db2 eval { PRAGMA wal_checkpoint }
411493cc590Sdan  set sz1 [file size test.db-wal]
412493cc590Sdan  db2 eval { INSERT INTO t1 VALUES('s', 'e') }
413493cc590Sdan  set sz2 [file size test.db-wal]
414493cc590Sdan  expr {$sz2>$sz1}
415493cc590Sdan} {1}
416493cc590Sdan
417493cc590Sdan# Test that if [db2] had not interfered when [db] was trying to grab
418493cc590Sdan# aReadMark[0], it would have been possible to wrap the log in 3.6.1.5.
419493cc590Sdan#
420493cc590Sdando_test wal3-6.1.6 {
421493cc590Sdan  execsql { COMMIT }
422493cc590Sdan  execsql { PRAGMA wal_checkpoint } db2
423493cc590Sdan  execsql {
424493cc590Sdan    BEGIN;
425493cc590Sdan    SELECT * FROM t1;
426493cc590Sdan  }
427493cc590Sdan} {o t t f f s s e}
428493cc590Sdando_test wal3-6.1.7 {
429493cc590Sdan  db2 eval { PRAGMA wal_checkpoint }
430493cc590Sdan  set sz1 [file size test.db-wal]
431493cc590Sdan  db2 eval { INSERT INTO t1 VALUES('n', 't') }
432493cc590Sdan  set sz2 [file size test.db-wal]
433493cc590Sdan  expr {$sz2==$sz1}
434493cc590Sdan} {1}
435493cc590Sdan
436493cc590Sdandb3 close
437493cc590Sdandb2 close
438493cc590Sdandb close
439493cc590Sdan
440493cc590Sdando_test wal3-6.2.1 {
441fda06befSmistachkin  forcedelete test.db test.db-journal test.db wal
442493cc590Sdan  sqlite3 db test.db
443493cc590Sdan  sqlite3 db2 test.db
4447fa65fbfSdan  execsql { PRAGMA auto_vacuum = off }
445493cc590Sdan  execsql { PRAGMA journal_mode = WAL }
446493cc590Sdan  execsql {
447493cc590Sdan    CREATE TABLE t1(a, b);
448493cc590Sdan    INSERT INTO t1 VALUES('h', 'h');
449493cc590Sdan    INSERT INTO t1 VALUES('l', 'b');
450493cc590Sdan  }
451493cc590Sdan} {}
452493cc590Sdan
453493cc590SdanT filter xShmLock
454493cc590SdanT script lock_callback
455493cc590Sdanproc lock_callback {method file handle spec} {
456493cc590Sdan  if {$spec == "3 1 unlock exclusive"} {
457493cc590Sdan    T filter {}
458493cc590Sdan    set ::R [db2 eval {
459493cc590Sdan      BEGIN;
460493cc590Sdan      SELECT * FROM t1;
461493cc590Sdan    }]
462493cc590Sdan  }
463493cc590Sdan}
464493cc590Sdando_test wal3-6.2.2 {
465493cc590Sdan  execsql { PRAGMA wal_checkpoint }
4660774bb59Sdan} {0 4 4}
467493cc590Sdando_test wal3-6.2.3 {
468493cc590Sdan  set ::R
469493cc590Sdan} {h h l b}
470493cc590Sdando_test wal3-6.2.4 {
471493cc590Sdan  set sz1 [file size test.db-wal]
472493cc590Sdan  execsql { INSERT INTO t1 VALUES('b', 'c'); }
473493cc590Sdan  set sz2 [file size test.db-wal]
474493cc590Sdan  expr {$sz2 > $sz1}
475493cc590Sdan} {1}
476493cc590Sdando_test wal3-6.2.5 {
477493cc590Sdan  db2 eval { COMMIT }
478493cc590Sdan  execsql { PRAGMA wal_checkpoint }
479493cc590Sdan  set sz1 [file size test.db-wal]
480493cc590Sdan  execsql { INSERT INTO t1 VALUES('n', 'o'); }
481493cc590Sdan  set sz2 [file size test.db-wal]
482493cc590Sdan  expr {$sz2 == $sz1}
483493cc590Sdan} {1}
484493cc590Sdan
485493cc590Sdandb2 close
486493cc590Sdandb close
487493cc590SdanT delete
488493cc590Sdan
489640aac48Sdan#-------------------------------------------------------------------------
490640aac48Sdan# When opening a read-transaction on a database, if the entire log has
491640aac48Sdan# not yet been copied to the database file, the reader grabs a read
492640aac48Sdan# lock on aReadMark[x], where x>0. The following test cases experiment
493640aac48Sdan# with the outcome of the following:
494640aac48Sdan#
495640aac48Sdan#   + The reader discovering that between the time when it read the
496640aac48Sdan#     wal-index header and the lock was obtained that a writer has
497640aac48Sdan#     written to the log. In this case the reader should re-read the
498640aac48Sdan#     wal-index header and lock a snapshot corresponding to the new
499640aac48Sdan#     header.
500640aac48Sdan#
501640aac48Sdan#   + The value in the aReadMark[x] slot has been modified since it was
502640aac48Sdan#     read.
503640aac48Sdan#
504640aac48Sdancatch {db close}
505640aac48Sdantestvfs T -default 1
506640aac48Sdando_test wal3-7.1.1 {
507fda06befSmistachkin  forcedelete test.db test.db-journal test.db wal
508640aac48Sdan  sqlite3 db test.db
509640aac48Sdan  execsql {
510640aac48Sdan    PRAGMA journal_mode = WAL;
511640aac48Sdan    CREATE TABLE blue(red PRIMARY KEY, green);
512640aac48Sdan  }
513640aac48Sdan} {wal}
514640aac48Sdan
515640aac48SdanT script method_callback
516640aac48SdanT filter xOpen
517640aac48Sdanproc method_callback {method args} {
518640aac48Sdan  if {$method == "xOpen"} { return "reader" }
519640aac48Sdan}
520640aac48Sdando_test wal3-7.1.2 {
521640aac48Sdan  sqlite3 db2 test.db
522640aac48Sdan  execsql { SELECT * FROM blue } db2
523640aac48Sdan} {}
524640aac48Sdan
525640aac48SdanT filter xShmLock
526640aac48Sdanset ::locks [list]
527640aac48Sdanproc method_callback {method file handle spec} {
528640aac48Sdan  if {$handle != "reader" } { return }
529640aac48Sdan  if {$method == "xShmLock"} {
530640aac48Sdan    catch { execsql { INSERT INTO blue VALUES(1, 2) } }
531640aac48Sdan    catch { execsql { INSERT INTO blue VALUES(3, 4) } }
532640aac48Sdan  }
533640aac48Sdan  lappend ::locks $spec
534640aac48Sdan}
535640aac48Sdando_test wal3-7.1.3 {
536640aac48Sdan  execsql { SELECT * FROM blue } db2
537640aac48Sdan} {1 2 3 4}
538640aac48Sdando_test wal3-7.1.4 {
539640aac48Sdan  set ::locks
540640aac48Sdan} {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}}
541640aac48Sdan
542640aac48Sdanset ::locks [list]
543640aac48Sdanproc method_callback {method file handle spec} {
544640aac48Sdan  if {$handle != "reader" } { return }
545640aac48Sdan  if {$method == "xShmLock"} {
546640aac48Sdan    catch { execsql { INSERT INTO blue VALUES(5, 6) } }
547640aac48Sdan  }
548640aac48Sdan  lappend ::locks $spec
549640aac48Sdan}
550640aac48Sdando_test wal3-7.2.1 {
551640aac48Sdan  execsql { SELECT * FROM blue } db2
552640aac48Sdan} {1 2 3 4 5 6}
553640aac48Sdando_test wal3-7.2.2 {
554640aac48Sdan  set ::locks
555640aac48Sdan} {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}}
556640aac48Sdan
557640aac48Sdandb close
558640aac48Sdandb2 close
559640aac48SdanT delete
560db9d981aSdan
56123f71920Sdan
56223f71920Sdan#-------------------------------------------------------------------------
56323f71920Sdan# When a connection opens a read-lock on the database, it searches for
56423f71920Sdan# an aReadMark[] slot that is already set to the mxFrame value for the
56523f71920Sdan# new transaction. If it cannot find one, it attempts to obtain an
56623f71920Sdan# exclusive lock on an aReadMark[] slot for the purposes of modifying
56723f71920Sdan# the value, then drops back to a shared-lock for the duration of the
56823f71920Sdan# transaction.
56923f71920Sdan#
57023f71920Sdan# This test case verifies that if an exclusive lock cannot be obtained
57123f71920Sdan# on any aReadMark[] slot (because there are already several readers),
57223f71920Sdan# the client takes a shared-lock on a slot without modifying the value
57323f71920Sdan# and continues.
57423f71920Sdan#
575675f85c4Sdanset nConn 50
576fe349965Sdrhif { [string match *BSD $tcl_platform(os)] } { set nConn 25 }
57723f71920Sdando_test wal3-9.0 {
578fda06befSmistachkin  forcedelete test.db test.db-journal test.db wal
57923f71920Sdan  sqlite3 db test.db
58023f71920Sdan  execsql {
581bfcaec72Sdan    PRAGMA page_size = 1024;
58223f71920Sdan    PRAGMA journal_mode = WAL;
58323f71920Sdan    CREATE TABLE whoami(x);
58423f71920Sdan    INSERT INTO whoami VALUES('nobody');
58523f71920Sdan  }
58623f71920Sdan} {wal}
587675f85c4Sdanfor {set i 0} {$i < $nConn} {incr i} {
58823f71920Sdan  set c db$i
58923f71920Sdan  do_test wal3-9.1.$i {
59023f71920Sdan    sqlite3 $c test.db
59123f71920Sdan    execsql { UPDATE whoami SET x = $c }
59223f71920Sdan    execsql {
59323f71920Sdan      BEGIN;
59423f71920Sdan      SELECT * FROM whoami
59523f71920Sdan    } $c
59623f71920Sdan  } $c
59723f71920Sdan}
598675f85c4Sdanfor {set i 0} {$i < $nConn} {incr i} {
59923f71920Sdan  set c db$i
60023f71920Sdan  do_test wal3-9.2.$i {
60123f71920Sdan    execsql { SELECT * FROM whoami } $c
60223f71920Sdan  } $c
60323f71920Sdan}
604bfcaec72Sdan
605bfcaec72Sdanset sz [expr 1024 * (2+$AUTOVACUUM)]
60623f71920Sdando_test wal3-9.3 {
607675f85c4Sdan  for {set i 0} {$i < ($nConn-1)} {incr i} { db$i close }
60823f71920Sdan  execsql { PRAGMA wal_checkpoint }
609bfcaec72Sdan  byte_is_zero test.db [expr $sz-1024]
610bfcaec72Sdan} {1}
611bfcaec72Sdando_test wal3-9.4 {
612675f85c4Sdan  db[expr $nConn-1] close
61323f71920Sdan  execsql { PRAGMA wal_checkpoint }
61423f71920Sdan  set sz2 [file size test.db]
615bfcaec72Sdan  byte_is_zero test.db [expr $sz-1024]
616bfcaec72Sdan} {0}
61723f71920Sdan
618aac1bf94Sdando_multiclient_test tn {
619aac1bf94Sdan  do_test wal3-10.$tn.1 {
620aac1bf94Sdan    sql1 {
621aac1bf94Sdan      PRAGMA page_size = 1024;
622aac1bf94Sdan      CREATE TABLE t1(x);
623aac1bf94Sdan      PRAGMA journal_mode = WAL;
624aac1bf94Sdan      PRAGMA wal_autocheckpoint = 100000;
625aac1bf94Sdan      BEGIN;
626aac1bf94Sdan        INSERT INTO t1 VALUES(randomblob(800));
627aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 2
628aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 4
629aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 8
630aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 16
631aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 32
632aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 64
633aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 128
634aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 256
635aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 512
636aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 1024
637aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 2048
638aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 4096
639aac1bf94Sdan        INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 8192
640aac1bf94Sdan      COMMIT;
641aac1bf94Sdan      CREATE INDEX i1 ON t1(x);
642aac1bf94Sdan    }
643aac1bf94Sdan
644aac1bf94Sdan    expr {[file size test.db-wal] > [expr 1032*9000]}
645aac1bf94Sdan  } 1
646aac1bf94Sdan
647aac1bf94Sdan  do_test wal3-10.$tn.2 {
648aac1bf94Sdan    sql2 {PRAGMA integrity_check}
649aac1bf94Sdan  } {ok}
650aac1bf94Sdan}
65123f71920Sdan
652ef4ee8f2Sdanfinish_test
653