xref: /sqlite-3.40.0/test/wal3.test (revision c60f10a2)
1# 2010 April 13
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/wal_common.tcl
20source $testdir/malloc_common.tcl
21ifcapable !wal {finish_test ; return }
22
23set a_string_counter 1
24proc a_string {n} {
25  global a_string_counter
26  incr a_string_counter
27  string range [string repeat "${a_string_counter}." $n] 1 $n
28}
29db func a_string a_string
30
31#-------------------------------------------------------------------------
32# When a rollback or savepoint rollback occurs, the client may remove
33# elements from one of the hash tables in the wal-index. This block
34# of test cases tests that nothing appears to go wrong when this is
35# done.
36#
37do_test wal3-1.0 {
38  execsql {
39    PRAGMA cache_size = 2000;
40    PRAGMA page_size = 1024;
41    PRAGMA auto_vacuum = off;
42    PRAGMA synchronous = normal;
43    PRAGMA journal_mode = WAL;
44    PRAGMA wal_autocheckpoint = 0;
45    BEGIN;
46      CREATE TABLE t1(x);
47      INSERT INTO t1 VALUES( a_string(800) );                  /*    1 */
48      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    2 */
49      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    4 */
50      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    8 */
51      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   16 */
52      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   32 */
53      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   64 */
54      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  128*/
55      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  256 */
56      INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  512 */
57      INSERT INTO t1 SELECT a_string(800) FROM t1;             /* 1024 */
58      INSERT INTO t1 SELECT a_string(800) FROM t1;             /* 2048 */
59      INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 2000;  /* 4048 */
60    COMMIT;
61    PRAGMA cache_size = 10;
62  }
63  wal_frame_count test.db-wal 1024
64} 4086
65
66for {set i 1} {$i < 20} {incr i} {
67
68  do_test wal3-1.$i.1 {
69    set str [a_string 800]
70    execsql { UPDATE t1 SET x = $str WHERE rowid = $i }
71    lappend L [wal_frame_count test.db-wal 1024]
72    execsql {
73      BEGIN;
74        INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 100;
75      ROLLBACK;
76      PRAGMA integrity_check;
77    }
78  } {ok}
79
80  # Check that everything looks OK from the point of view of an
81  # external connection.
82  #
83  sqlite3 db2 test.db
84  do_test wal3-1.$i.2 {
85    execsql { SELECT count(*) FROM t1 } db2
86  } 4048
87  do_test wal3-1.$i.3 {
88    execsql { SELECT x FROM t1 WHERE rowid = $i }
89  } $str
90  do_test wal3-1.$i.4 {
91    execsql { PRAGMA integrity_check } db2
92  } {ok}
93  db2 close
94
95  # Check that the file-system in its current state can be recovered.
96  #
97  file copy -force test.db test2.db
98  file copy -force test.db-wal test2.db-wal
99  file delete -force test2.db-journal
100  sqlite3 db2 test2.db
101  do_test wal3-1.$i.5 {
102    execsql { SELECT count(*) FROM t1 } db2
103  } 4048
104  do_test wal3-1.$i.6 {
105    execsql { SELECT x FROM t1 WHERE rowid = $i }
106  } $str
107  do_test wal3-1.$i.7 {
108    execsql { PRAGMA integrity_check } db2
109  } {ok}
110  db2 close
111}
112
113db close
114foreach code [list {
115  proc code2 {tcl} { uplevel #0 $tcl }
116  proc code3 {tcl} { uplevel #0 $tcl }
117  set tn singleproc
118} {
119  set ::code2_chan [launch_testfixture]
120  set ::code3_chan [launch_testfixture]
121  proc code2 {tcl} { testfixture $::code2_chan $tcl }
122  proc code3 {tcl} { testfixture $::code3_chan $tcl }
123  set tn multiproc
124}] {
125  file delete -force test.db test.db-wal test.db-journal
126  sqlite3 db test.db
127  eval $code
128
129  # Open connections [db2] and [db3]. Depending on which iteration this
130  # is, the connections may be created in this interpreter, or in
131  # interpreters running in other OS processes. As such, the [db2] and [db3]
132  # commands should only be accessed within [code2] and [code3] blocks,
133  # respectively.
134  #
135  code2 { sqlite3 db2 test.db ; db2 eval { PRAGMA journal_mode = WAL } }
136  code3 { sqlite3 db3 test.db ; db3 eval { PRAGMA journal_mode = WAL } }
137
138  # Shorthand commands. Execute SQL using database connection [db], [db2]
139  # or [db3]. Return the results.
140  #
141  proc sql  {sql} { db eval $sql }
142  proc sql2 {sql} { code2 [list db2 eval $sql] }
143  proc sql3 {sql} { code3 [list db3 eval $sql] }
144
145  do_test wal3-2.$tn.1 {
146    sql {
147      PRAGMA page_size = 1024;
148      PRAGMA auto_vacuum = OFF;
149      PRAGMA journal_mode = WAL;
150    }
151    sql {
152      CREATE TABLE t1(a, b);
153      INSERT INTO t1 VALUES(1, 'one');
154      BEGIN;
155        SELECT * FROM t1;
156    }
157  } {1 one}
158  do_test wal3-2.$tn.2 {
159    sql2 {
160      CREATE TABLE t2(a, b);
161      INSERT INTO t2 VALUES(2, 'two');
162      BEGIN;
163        SELECT * FROM t2;
164    }
165  } {2 two}
166  do_test wal3-2.$tn.3 {
167    sql3 {
168      CREATE TABLE t3(a, b);
169      INSERT INTO t3 VALUES(3, 'three');
170      BEGIN;
171        SELECT * FROM t3;
172    }
173  } {3 three}
174
175  # Try to checkpoint the database using [db]. It should be possible to
176  # checkpoint everything except the table added by [db3] (checkpointing
177  # these frames would clobber the snapshot currently being used by [db2]).
178  #
179  # After [db2] has committed, a checkpoint can copy the entire log to the
180  # database file. Checkpointing after [db3] has committed is therefore a
181  # no-op, as the entire log has already been backfilled.
182  #
183  do_test wal3-2.$tn.4 {
184    sql {
185      COMMIT;
186      PRAGMA wal_checkpoint;
187    }
188    file size test.db
189  } [expr $AUTOVACUUM ? 4*1024 : 3*1024]
190  do_test wal3-2.$tn.5 {
191    sql2 {
192      COMMIT;
193      PRAGMA wal_checkpoint;
194    }
195    file size test.db
196  } [expr $AUTOVACUUM ? 5*1024 : 4*1024]
197  do_test wal3-2.$tn.6 {
198    sql3 {
199      COMMIT;
200      PRAGMA wal_checkpoint;
201    }
202    file size test.db
203  } [expr $AUTOVACUUM ? 5*1024 : 4*1024]
204
205  catch { db close }
206  catch { code2 { db2 close } }
207  catch { code3 { db3 close } }
208  catch { close $::code2_chan }
209  catch { close $::code3_chan }
210}
211catch {db close}
212
213#-------------------------------------------------------------------------
214# Test that that for the simple test:
215#
216#   CREATE TABLE x(y);
217#   INSERT INTO x VALUES('z');
218#   PRAGMA wal_checkpoint;
219#
220# in WAL mode the xSync method is invoked as expected for each of
221# synchronous=off, synchronous=normal and synchronous=full.
222#
223foreach {tn syncmode synccount} {
224  1 off
225    {}
226  2 normal
227    {test.db-wal normal test.db normal}
228  3 full
229    {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}
230} {
231
232  proc sync_counter {args} {
233    foreach {method filename id flags} $args break
234    lappend ::syncs [file tail $filename] $flags
235  }
236  do_test wal3-3.$tn {
237    file delete -force test.db test.db-wal test.db-journal
238
239    testvfs T
240    T filter {}
241    T script sync_counter
242    sqlite3 db test.db -vfs T
243
244    execsql "PRAGMA synchronous = $syncmode"
245    execsql { PRAGMA journal_mode = WAL }
246
247    set ::syncs [list]
248    T filter xSync
249    execsql {
250      CREATE TABLE x(y);
251      INSERT INTO x VALUES('z');
252      PRAGMA wal_checkpoint;
253    }
254    T filter {}
255    set ::syncs
256  } $synccount
257
258  db close
259  T delete
260}
261
262#-------------------------------------------------------------------------
263# When recovering the contents of a WAL file, a process obtains the WRITER
264# lock, then locks all other bytes before commencing recovery. If it fails
265# to lock all other bytes (because some other process is holding a read
266# lock) it should retry up to 100 times. Then return SQLITE_PROTOCOL to the
267# caller. Test this (test case wal3-4.3).
268#
269# Also test the effect of hitting an SQLITE_BUSY while attempting to obtain
270# the WRITER lock (should be the same). Test case wal3-4.4.
271#
272proc lock_callback {method filename handle lock} {
273  lappend ::locks $lock
274}
275do_test wal3-4.1 {
276  testvfs T
277  T filter xShmLock
278  T script lock_callback
279  set ::locks [list]
280  sqlite3 db test.db -vfs T
281  execsql { SELECT * FROM x }
282  lrange $::locks 0 3
283} [list {0 1 lock exclusive} {1 7 lock exclusive}      \
284        {1 7 unlock exclusive} {0 1 unlock exclusive}  \
285]
286do_test wal3-4.2 {
287  db close
288  set ::locks [list]
289  sqlite3 db test.db -vfs T
290  execsql { SELECT * FROM x }
291  lrange $::locks 0 3
292} [list {0 1 lock exclusive} {1 7 lock exclusive}      \
293        {1 7 unlock exclusive} {0 1 unlock exclusive}  \
294]
295proc lock_callback {method filename handle lock} {
296  if {$lock == "1 7 lock exclusive"} { return SQLITE_BUSY }
297  return SQLITE_OK
298}
299puts "  Warning: This next test case causes SQLite to call xSleep(1) 100 times."
300puts "  Normally this equates to a 100ms delay, but if SQLite is built on unix"
301puts "  without HAVE_USLEEP defined, it may be 100 seconds."
302do_test wal3-4.3 {
303  db close
304  set ::locks [list]
305  sqlite3 db test.db -vfs T
306  catchsql { SELECT * FROM x }
307} {1 {locking protocol}}
308
309puts "  Warning: Same again!"
310proc lock_callback {method filename handle lock} {
311  if {$lock == "0 1 lock exclusive"} { return SQLITE_BUSY }
312  return SQLITE_OK
313}
314do_test wal3-4.4 {
315  db close
316  set ::locks [list]
317  sqlite3 db test.db -vfs T
318  catchsql { SELECT * FROM x }
319} {1 {locking protocol}}
320db close
321T delete
322
323
324#-------------------------------------------------------------------------
325# Only one client may run recovery at a time. Test this mechanism.
326#
327# When client-2 tries to open a read transaction while client-1 is
328# running recovery, it fails to obtain a lock on an aReadMark[] slot
329# (because they are all locked by recovery). It then tries to obtain
330# a shared lock on the RECOVER lock to see if there really is a
331# recovery running or not.
332#
333# This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR
334# being returned when client-2 attempts a shared lock on the RECOVER byte.
335#
336# An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An
337# SQLITE_IOERR should be returned to the caller.
338#
339do_test wal3-5.1 {
340  faultsim_delete_and_reopen
341  execsql {
342    PRAGMA journal_mode = WAL;
343    CREATE TABLE t1(a, b);
344    INSERT INTO t1 VALUES(1, 2);
345    INSERT INTO t1 VALUES(3, 4);
346  }
347  faultsim_save_and_close
348} {}
349
350testvfs T -default 1
351T script method_callback
352
353proc method_callback {method args} {
354  if {$method == "xShmBarrier"} {
355    incr ::barrier_count
356    if {$::barrier_count == 1} {
357      # This code is executed within the xShmBarrier() callback invoked
358      # by the client running recovery as part of writing the recovered
359      # wal-index header. If a second client attempts to access the
360      # database now, it reads a corrupt (partially written) wal-index
361      # header. But it cannot even get that far, as the first client
362      # is still holding all the locks (recovery takes an exclusive lock
363      # on *all* db locks, preventing access by any other client).
364      #
365      # If global variable ::wal3_do_lockfailure is non-zero, then set
366      # things up so that an IO error occurs within an xShmLock() callback
367      # made by the second client (aka [db2]).
368      #
369      sqlite3 db2 test.db
370      if { $::wal3_do_lockfailure } { T filter xShmLock }
371      set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ]
372      T filter {}
373      db2 close
374    }
375  }
376
377  if {$method == "xShmLock"} {
378    foreach {file handle spec} $args break
379    if { $spec == "2 1 lock shared" } {
380      return SQLITE_IOERR
381    }
382  }
383
384  return SQLITE_OK
385}
386
387# Test a normal SQLITE_BUSY return.
388#
389T filter xShmBarrier
390set testrc ""
391set testmsg ""
392set barrier_count 0
393set wal3_do_lockfailure 0
394do_test wal3-5.2 {
395  faultsim_restore_and_reopen
396  execsql { SELECT * FROM t1 }
397} {1 2 3 4}
398do_test wal3-5.3 {
399  list $::testrc $::testmsg
400} {1 {database is locked}}
401db close
402
403# Test an SQLITE_IOERR return.
404#
405T filter xShmBarrier
406set barrier_count 0
407set wal3_do_lockfailure 1
408set testrc ""
409set testmsg ""
410do_test wal3-5.4 {
411  faultsim_restore_and_reopen
412  execsql { SELECT * FROM t1 }
413} {1 2 3 4}
414do_test wal3-5.5 {
415  list $::testrc $::testmsg
416} {1 {disk I/O error}}
417
418db close
419T delete
420
421#-------------------------------------------------------------------------
422# When opening a read-transaction on a database, if the entire log has
423# already been copied to the database file, the reader grabs a special
424# kind of read lock (on aReadMark[0]). This set of test cases tests the
425# outcome of the following:
426#
427#   + The reader discovering that between the time when it determined
428#     that the log had been completely backfilled and the lock is obtained
429#     that a writer has written to the log. In this case the reader should
430#     acquire a different read-lock (not aReadMark[0]) and read the new
431#     snapshot.
432#
433#   + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY.
434#     This can happen if a checkpoint is ongoing. In this case also simply
435#     obtain a different read-lock.
436#
437catch {db close}
438testvfs T -default 1
439do_test wal3-6.1.1 {
440  file delete -force test.db test.db-journal test.db wal
441  sqlite3 db test.db
442  execsql { PRAGMA journal_mode = WAL }
443  execsql {
444    CREATE TABLE t1(a, b);
445    INSERT INTO t1 VALUES('o', 't');
446    INSERT INTO t1 VALUES('t', 'f');
447  }
448} {}
449do_test wal3-6.1.2 {
450  sqlite3 db2 test.db
451  sqlite3 db3 test.db
452  execsql { BEGIN ; SELECT * FROM t1 } db3
453} {o t t f}
454do_test wal3-6.1.3 {
455  execsql { PRAGMA wal_checkpoint } db2
456} {}
457
458# At this point the log file has been fully checkpointed. However,
459# connection [db3] holds a lock that prevents the log from being wrapped.
460# Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But
461# as it is obtaining the lock, [db2] appends to the log file.
462#
463T filter xShmLock
464T script lock_callback
465proc lock_callback {method file handle spec} {
466  if {$spec == "3 1 lock shared"} {
467    # This is the callback for [db] to obtain the read lock on aReadMark[0].
468    # Disable future callbacks using [T filter {}] and write to the log
469    # file using [db2]. [db3] is preventing [db2] from wrapping the log
470    # here, so this is an append.
471    T filter {}
472    db2 eval { INSERT INTO t1 VALUES('f', 's') }
473  }
474  return SQLITE_OK
475}
476do_test wal3-6.1.4 {
477  execsql {
478    BEGIN;
479    SELECT * FROM t1;
480  }
481} {o t t f f s}
482
483# [db] should be left holding a read-lock on some slot other than
484# aReadMark[0]. Test this by demonstrating that the read-lock is preventing
485# the log from being wrapped.
486#
487do_test wal3-6.1.5 {
488  db3 eval COMMIT
489  db2 eval { PRAGMA wal_checkpoint }
490  set sz1 [file size test.db-wal]
491  db2 eval { INSERT INTO t1 VALUES('s', 'e') }
492  set sz2 [file size test.db-wal]
493  expr {$sz2>$sz1}
494} {1}
495
496# Test that if [db2] had not interfered when [db] was trying to grab
497# aReadMark[0], it would have been possible to wrap the log in 3.6.1.5.
498#
499do_test wal3-6.1.6 {
500  execsql { COMMIT }
501  execsql { PRAGMA wal_checkpoint } db2
502  execsql {
503    BEGIN;
504    SELECT * FROM t1;
505  }
506} {o t t f f s s e}
507do_test wal3-6.1.7 {
508  db2 eval { PRAGMA wal_checkpoint }
509  set sz1 [file size test.db-wal]
510  db2 eval { INSERT INTO t1 VALUES('n', 't') }
511  set sz2 [file size test.db-wal]
512  expr {$sz2==$sz1}
513} {1}
514
515db3 close
516db2 close
517db close
518
519do_test wal3-6.2.1 {
520  file delete -force test.db test.db-journal test.db wal
521  sqlite3 db test.db
522  sqlite3 db2 test.db
523  execsql { PRAGMA journal_mode = WAL }
524  execsql {
525    CREATE TABLE t1(a, b);
526    INSERT INTO t1 VALUES('h', 'h');
527    INSERT INTO t1 VALUES('l', 'b');
528  }
529} {}
530
531T filter xShmLock
532T script lock_callback
533proc lock_callback {method file handle spec} {
534  if {$spec == "3 1 unlock exclusive"} {
535    T filter {}
536    set ::R [db2 eval {
537      BEGIN;
538      SELECT * FROM t1;
539    }]
540  }
541}
542do_test wal3-6.2.2 {
543  execsql { PRAGMA wal_checkpoint }
544} {}
545do_test wal3-6.2.3 {
546  set ::R
547} {h h l b}
548do_test wal3-6.2.4 {
549  set sz1 [file size test.db-wal]
550  execsql { INSERT INTO t1 VALUES('b', 'c'); }
551  set sz2 [file size test.db-wal]
552  expr {$sz2 > $sz1}
553} {1}
554do_test wal3-6.2.5 {
555  db2 eval { COMMIT }
556  execsql { PRAGMA wal_checkpoint }
557  set sz1 [file size test.db-wal]
558  execsql { INSERT INTO t1 VALUES('n', 'o'); }
559  set sz2 [file size test.db-wal]
560  expr {$sz2 == $sz1}
561} {1}
562
563db2 close
564db close
565T delete
566
567#-------------------------------------------------------------------------
568# When opening a read-transaction on a database, if the entire log has
569# not yet been copied to the database file, the reader grabs a read
570# lock on aReadMark[x], where x>0. The following test cases experiment
571# with the outcome of the following:
572#
573#   + The reader discovering that between the time when it read the
574#     wal-index header and the lock was obtained that a writer has
575#     written to the log. In this case the reader should re-read the
576#     wal-index header and lock a snapshot corresponding to the new
577#     header.
578#
579#   + The value in the aReadMark[x] slot has been modified since it was
580#     read.
581#
582catch {db close}
583testvfs T -default 1
584do_test wal3-7.1.1 {
585  file delete -force test.db test.db-journal test.db wal
586  sqlite3 db test.db
587  execsql {
588    PRAGMA journal_mode = WAL;
589    CREATE TABLE blue(red PRIMARY KEY, green);
590  }
591} {wal}
592
593T script method_callback
594T filter xOpen
595proc method_callback {method args} {
596  if {$method == "xOpen"} { return "reader" }
597}
598do_test wal3-7.1.2 {
599  sqlite3 db2 test.db
600  execsql { SELECT * FROM blue } db2
601} {}
602
603T filter xShmLock
604set ::locks [list]
605proc method_callback {method file handle spec} {
606  if {$handle != "reader" } { return }
607  if {$method == "xShmLock"} {
608    catch { execsql { INSERT INTO blue VALUES(1, 2) } }
609    catch { execsql { INSERT INTO blue VALUES(3, 4) } }
610  }
611  lappend ::locks $spec
612}
613do_test wal3-7.1.3 {
614  execsql { SELECT * FROM blue } db2
615} {1 2 3 4}
616do_test wal3-7.1.4 {
617  set ::locks
618} {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}}
619
620set ::locks [list]
621proc method_callback {method file handle spec} {
622  if {$handle != "reader" } { return }
623  if {$method == "xShmLock"} {
624    catch { execsql { INSERT INTO blue VALUES(5, 6) } }
625  }
626  lappend ::locks $spec
627}
628do_test wal3-7.2.1 {
629  execsql { SELECT * FROM blue } db2
630} {1 2 3 4 5 6}
631do_test wal3-7.2.2 {
632  set ::locks
633} {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}}
634
635db close
636db2 close
637T delete
638
639#-------------------------------------------------------------------------
640#
641do_test wal3-8.1 {
642  file delete -force test.db test.db-journal test.db wal
643  sqlite3 db test.db
644  sqlite3 db2 test.db
645  execsql {
646    PRAGMA journal_mode = WAL;
647    CREATE TABLE b(c);
648    INSERT INTO b VALUES('Tehran');
649    INSERT INTO b VALUES('Qom');
650    INSERT INTO b VALUES('Markazi');
651    PRAGMA wal_checkpoint;
652  }
653} {wal}
654do_test wal3-8.2 {
655  execsql { SELECT * FROM b }
656} {Tehran Qom Markazi}
657do_test wal3-8.3 {
658  db eval { SELECT * FROM b } {
659    db eval { INSERT INTO b VALUES('Qazvin') }
660    set r [db2 eval { SELECT * FROM b }]
661    break
662  }
663  set r
664} {Tehran Qom Markazi Qazvin}
665do_test wal3-8.4 {
666  execsql {
667    INSERT INTO b VALUES('Gilan');
668    INSERT INTO b VALUES('Ardabil');
669  }
670} {}
671db2 close
672
673faultsim_save_and_close
674testvfs T -default 1
675faultsim_restore_and_reopen
676T filter xShmLock
677T script lock_callback
678
679proc lock_callback {method file handle spec} {
680  if {$spec == "4 1 unlock exclusive"} {
681    T filter {}
682    set ::r [catchsql { SELECT * FROM b } db2]
683  }
684}
685sqlite3 db test.db
686sqlite3 db2 test.db
687do_test wal3-8.5 {
688  execsql { SELECT * FROM b }
689} {Tehran Qom Markazi Qazvin Gilan Ardabil}
690do_test wal3-8.6 {
691  set ::r
692} {1 {locking protocol}}
693
694db close
695db2 close
696
697faultsim_restore_and_reopen
698sqlite3 db2 test.db
699T filter xShmLock
700T script lock_callback
701proc lock_callback {method file handle spec} {
702  if {$spec == "1 7 unlock exclusive"} {
703    T filter {}
704    set ::r [catchsql { SELECT * FROM b } db2]
705  }
706}
707unset ::r
708do_test wal3-8.5 {
709  execsql { SELECT * FROM b }
710} {Tehran Qom Markazi Qazvin Gilan Ardabil}
711do_test wal3-8.6 {
712  set ::r
713} {1 {locking protocol}}
714
715db close
716db2 close
717T delete
718
719#-------------------------------------------------------------------------
720# When a connection opens a read-lock on the database, it searches for
721# an aReadMark[] slot that is already set to the mxFrame value for the
722# new transaction. If it cannot find one, it attempts to obtain an
723# exclusive lock on an aReadMark[] slot for the purposes of modifying
724# the value, then drops back to a shared-lock for the duration of the
725# transaction.
726#
727# This test case verifies that if an exclusive lock cannot be obtained
728# on any aReadMark[] slot (because there are already several readers),
729# the client takes a shared-lock on a slot without modifying the value
730# and continues.
731#
732do_test wal3-9.0 {
733  file delete -force test.db test.db-journal test.db wal
734  sqlite3 db test.db
735  execsql {
736    PRAGMA journal_mode = WAL;
737    CREATE TABLE whoami(x);
738    INSERT INTO whoami VALUES('nobody');
739  }
740} {wal}
741for {set i 0} {$i < 50} {incr i} {
742  set c db$i
743  do_test wal3-9.1.$i {
744    sqlite3 $c test.db
745    execsql { UPDATE whoami SET x = $c }
746    execsql {
747      BEGIN;
748      SELECT * FROM whoami
749    } $c
750  } $c
751}
752for {set i 0} {$i < 50} {incr i} {
753  set c db$i
754  do_test wal3-9.2.$i {
755    execsql { SELECT * FROM whoami } $c
756  } $c
757}
758do_test wal3-9.3 {
759  for {set i 0} {$i < 49} {incr i} { db$i close }
760  execsql { PRAGMA wal_checkpoint }
761  set sz1 [file size test.db]
762  db49 close
763  execsql { PRAGMA wal_checkpoint }
764  set sz2 [file size test.db]
765  expr {$sz2 > $sz1}
766} {1}
767
768db close
769
770finish_test
771
772