blob: 951e8c39c2e548bebb439f6411957a3d3d24ee8e [file] [log] [blame]
dan9a6b4e92010-05-06 11:32:09 +00001# 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
dan10f5a502010-06-23 15:55:43 +000019source $testdir/malloc_common.tcl
20source $testdir/wal_common.tcl
21
dan9a6b4e92010-05-06 11:32:09 +000022ifcapable !wal {finish_test ; return }
23
24proc set_tvfs_hdr {file args} {
dan71d89912010-05-24 13:57:42 +000025
26 # Set $nHdr to the number of bytes in the wal-index header:
dan10f5a502010-06-23 15:55:43 +000027 set nHdr 48
dan71d89912010-05-24 13:57:42 +000028 set nInt [expr {$nHdr/4}]
29
dan23f71922010-06-07 06:11:39 +000030 if {[llength $args]>2} {
31 error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"}
dan9a6b4e92010-05-06 11:32:09 +000032 }
33
34 set blob [tvfs shm $file]
dand0aa3422010-05-31 16:41:53 +000035
dan9a6b4e92010-05-06 11:32:09 +000036 if {[llength $args]} {
dan94b7f762010-05-29 06:18:54 +000037 set ia [lindex $args 0]
dan23f71922010-06-07 06:11:39 +000038 set ib $ia
39 if {[llength $args]==2} {
40 set ib [lindex $args 1]
41 }
dand0aa3422010-05-31 16:41:53 +000042 binary scan $blob a[expr $nHdr*2]a* dummy tail
dan23f71922010-06-07 06:11:39 +000043 set blob [binary format i${nInt}i${nInt}a* $ia $ib $tail]
dan9a6b4e92010-05-06 11:32:09 +000044 tvfs shm $file $blob
45 }
46
dan71d89912010-05-24 13:57:42 +000047 binary scan $blob i${nInt} ints
dan9a6b4e92010-05-06 11:32:09 +000048 return $ints
49}
50
51proc incr_tvfs_hdr {file idx incrval} {
52 set ints [set_tvfs_hdr $file]
53 set v [lindex $ints $idx]
54 incr v $incrval
55 lset ints $idx $v
56 set_tvfs_hdr $file $ints
57}
58
59
60#-------------------------------------------------------------------------
61# Test case wal2-1.*:
62#
63# Set up a small database containing a single table. The database is not
64# checkpointed during the test - all content resides in the log file.
65#
66# Two connections are established to the database file - a writer ([db])
67# and a reader ([db2]). For each of the 8 integer fields in the wal-index
68# header (6 fields and 2 checksum values), do the following:
69#
70# 1. Modify the database using the writer.
71#
72# 2. Attempt to read the database using the reader. Before the reader
73# has a chance to snapshot the wal-index header, increment one
74# of the the integer fields (so that the reader ends up with a corrupted
75# header).
76#
77# 3. Check that the reader recovers the wal-index and reads the correct
78# database content.
79#
80do_test wal2-1.0 {
dan13a3cb82010-06-11 19:04:21 +000081 proc tvfs_cb {method filename args} {
82 set ::filename $filename
83 return SQLITE_OK
84 }
85
dan1f55e282010-06-03 09:25:10 +000086 testvfs tvfs
87 tvfs script tvfs_cb
dan13a3cb82010-06-11 19:04:21 +000088 tvfs filter xShmOpen
dan9a6b4e92010-05-06 11:32:09 +000089
90 sqlite3 db test.db -vfs tvfs
91 sqlite3 db2 test.db -vfs tvfs
92
93 execsql {
94 PRAGMA journal_mode = WAL;
95 CREATE TABLE t1(a);
96 } db2
97 execsql {
98 INSERT INTO t1 VALUES(1);
99 INSERT INTO t1 VALUES(2);
100 INSERT INTO t1 VALUES(3);
101 INSERT INTO t1 VALUES(4);
102 SELECT count(a), sum(a) FROM t1;
103 }
104} {4 10}
105do_test wal2-1.1 {
106 execsql { SELECT count(a), sum(a) FROM t1 } db2
107} {4 10}
108
dand0aa3422010-05-31 16:41:53 +0000109set RECOVER [list \
110 {0 1 lock exclusive} {1 7 lock exclusive} \
111 {1 7 unlock exclusive} {0 1 unlock exclusive} \
112]
113set READ [list \
114 {4 1 lock exclusive} {4 1 unlock exclusive} \
115 {4 1 lock shared} {4 1 unlock shared} \
116]
117
118foreach {tn iInsert res wal_index_hdr_mod wal_locks} "
119 2 5 {5 15} 0 {$RECOVER $READ}
120 3 6 {6 21} 1 {$RECOVER $READ}
121 4 7 {7 28} 2 {$RECOVER $READ}
122 5 8 {8 36} 3 {$RECOVER $READ}
123 6 9 {9 45} 4 {$RECOVER $READ}
124 7 10 {10 55} 5 {$RECOVER $READ}
125 8 11 {11 66} 6 {$RECOVER $READ}
126 9 12 {12 78} 7 {$RECOVER $READ}
127 10 13 {13 91} 8 {$RECOVER $READ}
128 11 14 {14 105} 9 {$RECOVER $READ}
129 12 15 {15 120} -1 {$READ}
130" {
dan9a6b4e92010-05-06 11:32:09 +0000131
132 do_test wal2-1.$tn.1 {
133 execsql { INSERT INTO t1 VALUES($iInsert) }
dan9a6b4e92010-05-06 11:32:09 +0000134 set ::locks [list]
dan9a6b4e92010-05-06 11:32:09 +0000135 proc tvfs_cb {method args} {
dan13a3cb82010-06-11 19:04:21 +0000136 lappend ::locks [lindex $args 2]
dan9a6b4e92010-05-06 11:32:09 +0000137 return SQLITE_OK
138 }
dan13a3cb82010-06-11 19:04:21 +0000139 tvfs filter xShmLock
140 if {$::wal_index_hdr_mod >= 0} {
141 incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1
142 }
dan9a6b4e92010-05-06 11:32:09 +0000143 execsql { SELECT count(a), sum(a) FROM t1 } db2
144 } $res
145
146 do_test wal2-1.$tn.2 {
147 set ::locks
148 } $wal_locks
149}
150db close
151db2 close
152tvfs delete
153file delete -force test.db test.db-wal test.db-journal
154
155#-------------------------------------------------------------------------
156# This test case is very similar to the previous one, except, after
157# the reader reads the corrupt wal-index header, but before it has
158# a chance to re-read it under the cover of the RECOVER lock, the
159# wal-index header is replaced with a valid, but out-of-date, header.
160#
161# Because the header checksum looks Ok, the reader does not run recovery,
162# it simply drops back to a READ lock and proceeds. But because the
163# header is out-of-date, the reader reads the out-of-date snapshot.
164#
165# After this, the header is corrupted again and the reader is allowed
166# to run recovery. This time, it sees an up-to-date snapshot of the
167# database file.
168#
dand0aa3422010-05-31 16:41:53 +0000169set WRITER [list 0 1 lock exclusive]
170set LOCKS [list \
171 {0 1 lock exclusive} {0 1 unlock exclusive} \
drhdb7f6472010-06-09 14:45:12 +0000172 {4 1 lock exclusive} {4 1 unlock exclusive} \
dand0aa3422010-05-31 16:41:53 +0000173 {4 1 lock shared} {4 1 unlock shared} \
174]
dan9a6b4e92010-05-06 11:32:09 +0000175do_test wal2-2.0 {
176
dan1f55e282010-06-03 09:25:10 +0000177 testvfs tvfs
178 tvfs script tvfs_cb
dan13a3cb82010-06-11 19:04:21 +0000179 tvfs filter xShmOpen
dan9a6b4e92010-05-06 11:32:09 +0000180 proc tvfs_cb {method args} {
dan13a3cb82010-06-11 19:04:21 +0000181 set ::filename [lindex $args 0]
dan9a6b4e92010-05-06 11:32:09 +0000182 return SQLITE_OK
183 }
184
185 sqlite3 db test.db -vfs tvfs
186 sqlite3 db2 test.db -vfs tvfs
187
188 execsql {
189 PRAGMA journal_mode = WAL;
190 CREATE TABLE t1(a);
191 } db2
192 execsql {
193 INSERT INTO t1 VALUES(1);
194 INSERT INTO t1 VALUES(2);
195 INSERT INTO t1 VALUES(3);
196 INSERT INTO t1 VALUES(4);
197 SELECT count(a), sum(a) FROM t1;
198 }
199} {4 10}
200do_test wal2-2.1 {
201 execsql { SELECT count(a), sum(a) FROM t1 } db2
202} {4 10}
203
204foreach {tn iInsert res0 res1 wal_index_hdr_mod} {
205 2 5 {4 10} {5 15} 0
206 3 6 {5 15} {6 21} 1
207 4 7 {6 21} {7 28} 2
208 5 8 {7 28} {8 36} 3
209 6 9 {8 36} {9 45} 4
210 7 10 {9 45} {10 55} 5
211 8 11 {10 55} {11 66} 6
212 9 12 {11 66} {12 78} 7
213} {
dan13a3cb82010-06-11 19:04:21 +0000214 tvfs filter xShmLock
215
drh7e263722010-05-20 21:21:09 +0000216 do_test wal2-2.$tn.1 {
dan13a3cb82010-06-11 19:04:21 +0000217 set oldhdr [set_tvfs_hdr $::filename]
dan9a6b4e92010-05-06 11:32:09 +0000218 execsql { INSERT INTO t1 VALUES($iInsert) }
219 execsql { SELECT count(a), sum(a) FROM t1 }
220 } $res1
221
222 do_test wal2-2.$tn.2 {
223 set ::locks [list]
dan9a6b4e92010-05-06 11:32:09 +0000224 proc tvfs_cb {method args} {
dan13a3cb82010-06-11 19:04:21 +0000225 set lock [lindex $args 2]
226 lappend ::locks $lock
227 if {$lock == $::WRITER} {
228 set_tvfs_hdr $::filename $::oldhdr
dan9a6b4e92010-05-06 11:32:09 +0000229 }
230 return SQLITE_OK
231 }
232
dan13a3cb82010-06-11 19:04:21 +0000233 if {$::wal_index_hdr_mod >= 0} {
234 incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1
235 }
dan9a6b4e92010-05-06 11:32:09 +0000236 execsql { SELECT count(a), sum(a) FROM t1 } db2
237 } $res0
238
239 do_test wal2-2.$tn.3 {
240 set ::locks
dand0aa3422010-05-31 16:41:53 +0000241 } $LOCKS
dan9a6b4e92010-05-06 11:32:09 +0000242
243 do_test wal2-2.$tn.4 {
244 set ::locks [list]
dan9a6b4e92010-05-06 11:32:09 +0000245 proc tvfs_cb {method args} {
dan13a3cb82010-06-11 19:04:21 +0000246 set lock [lindex $args 2]
247 lappend ::locks $lock
dan9a6b4e92010-05-06 11:32:09 +0000248 return SQLITE_OK
249 }
250
dan13a3cb82010-06-11 19:04:21 +0000251 if {$::wal_index_hdr_mod >= 0} {
252 incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1
253 }
dan9a6b4e92010-05-06 11:32:09 +0000254 execsql { SELECT count(a), sum(a) FROM t1 } db2
255 } $res1
256}
257db close
258db2 close
259tvfs delete
260file delete -force test.db test.db-wal test.db-journal
261
dand0aa3422010-05-31 16:41:53 +0000262
263if 0 {
danff6dfc72010-05-06 12:15:48 +0000264#-------------------------------------------------------------------------
265# This test case - wal2-3.* - tests the response of the library to an
266# SQLITE_BUSY when attempting to obtain a READ or RECOVER lock.
267#
268# wal2-3.0 - 2: SQLITE_BUSY when obtaining a READ lock
269# wal2-3.3 - 6: SQLITE_BUSY when obtaining a RECOVER lock
270#
271do_test wal2-3.0 {
272 proc tvfs_cb {method args} {
273 if {$method == "xShmLock"} {
274 if {[info exists ::locked]} { return SQLITE_BUSY }
275 }
276 return SQLITE_OK
277 }
278
279 proc busyhandler x {
280 if {$x>3} { unset -nocomplain ::locked }
281 return 0
282 }
283
dan1f55e282010-06-03 09:25:10 +0000284 testvfs tvfs
285 tvfs script tvfs_cb
danff6dfc72010-05-06 12:15:48 +0000286 sqlite3 db test.db -vfs tvfs
287 db busy busyhandler
288
289 execsql {
290 PRAGMA journal_mode = WAL;
291 CREATE TABLE t1(a);
292 INSERT INTO t1 VALUES(1);
293 INSERT INTO t1 VALUES(2);
294 INSERT INTO t1 VALUES(3);
295 INSERT INTO t1 VALUES(4);
296 }
297
298 set ::locked 1
299 info exists ::locked
300} {1}
301do_test wal2-3.1 {
302 execsql { SELECT count(a), sum(a) FROM t1 }
303} {4 10}
304do_test wal2-3.2 {
305 info exists ::locked
306} {0}
307
308do_test wal2-3.3 {
309 proc tvfs_cb {method args} {
310 if {$method == "xShmLock"} {
311 if {[info exists ::sabotage]} {
312 unset -nocomplain ::sabotage
313 incr_tvfs_hdr [lindex $args 0] 1 1
314 }
315 if {[info exists ::locked] && [lindex $args 2] == "RECOVER"} {
316 return SQLITE_BUSY
317 }
318 }
319 return SQLITE_OK
320 }
danff6dfc72010-05-06 12:15:48 +0000321 set ::sabotage 1
322 set ::locked 1
323 list [info exists ::sabotage] [info exists ::locked]
324} {1 1}
325do_test wal2-3.4 {
326 execsql { SELECT count(a), sum(a) FROM t1 }
327} {4 10}
328do_test wal2-3.5 {
329 list [info exists ::sabotage] [info exists ::locked]
330} {0 0}
331db close
332tvfs delete
333file delete -force test.db test.db-wal test.db-journal
334
dand0aa3422010-05-31 16:41:53 +0000335}
336
dan576bc322010-05-06 18:04:50 +0000337#-------------------------------------------------------------------------
338# Test that a database connection using a VFS that does not support the
339# xShmXXX interfaces cannot open a WAL database.
340#
341do_test wal2-4.1 {
342 sqlite3 db test.db
343 execsql {
344 PRAGMA journal_mode = WAL;
345 CREATE TABLE data(x);
346 INSERT INTO data VALUES('need xShmOpen to see this');
347 PRAGMA wal_checkpoint;
348 }
349} {wal}
350do_test wal2-4.2 {
351 db close
dan1f55e282010-06-03 09:25:10 +0000352 testvfs tvfs -noshm 1
dan576bc322010-05-06 18:04:50 +0000353 sqlite3 db test.db -vfs tvfs
354 catchsql { SELECT * FROM data }
355} {1 {unable to open database file}}
356do_test wal2-4.3 {
357 db close
dan1f55e282010-06-03 09:25:10 +0000358 testvfs tvfs
dan576bc322010-05-06 18:04:50 +0000359 sqlite3 db test.db -vfs tvfs
360 catchsql { SELECT * FROM data }
361} {0 {{need xShmOpen to see this}}}
362db close
363tvfs delete
364
dan5273f582010-05-06 18:27:19 +0000365#-------------------------------------------------------------------------
366# Test that if a database connection is forced to run recovery before it
dan65be0d82010-05-06 18:48:27 +0000367# can perform a checkpoint, it does not transition into RECOVER state.
dan5273f582010-05-06 18:27:19 +0000368#
dand0aa3422010-05-31 16:41:53 +0000369# UPDATE: This has now changed. When running a checkpoint, if recovery is
370# required the client grabs all exclusive locks (just as it would for a
371# recovery performed as a pre-cursor to a normal database transaction).
372#
dan5f3f3b22010-06-01 07:51:47 +0000373set expected_locks [list]
374lappend expected_locks {1 1 lock exclusive} ;# Lock checkpoint
375lappend expected_locks {0 1 lock exclusive} ;# Lock writer
376lappend expected_locks {2 6 lock exclusive} ;# Lock recovery & all aReadMark[]
377lappend expected_locks {2 6 unlock exclusive} ;# Unlock recovery & aReadMark[]
378lappend expected_locks {0 1 unlock exclusive} ;# Unlock writer
379lappend expected_locks {3 1 lock exclusive} ;# Lock aReadMark[0]
380lappend expected_locks {3 1 unlock exclusive} ;# Unlock aReadMark[0]
381lappend expected_locks {1 1 unlock exclusive} ;# Unlock checkpoint
dan5273f582010-05-06 18:27:19 +0000382do_test wal2-5.1 {
383 proc tvfs_cb {method args} {
384 set ::shm_file [lindex $args 0]
385 if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] }
386 return $::tvfs_cb_return
387 }
388 set tvfs_cb_return SQLITE_OK
389
dan1f55e282010-06-03 09:25:10 +0000390 testvfs tvfs
391 tvfs script tvfs_cb
dan5273f582010-05-06 18:27:19 +0000392
393 sqlite3 db test.db -vfs tvfs
394 execsql {
395 PRAGMA journal_mode = WAL;
396 CREATE TABLE x(y);
397 INSERT INTO x VALUES(1);
398 }
399
400 incr_tvfs_hdr $::shm_file 1 1
401 set ::locks [list]
402 execsql { PRAGMA wal_checkpoint }
403 set ::locks
dan5f3f3b22010-06-01 07:51:47 +0000404} $expected_locks
dan65be0d82010-05-06 18:48:27 +0000405db close
406tvfs delete
dan5273f582010-05-06 18:27:19 +0000407
dan55437592010-05-11 12:19:26 +0000408#-------------------------------------------------------------------------
409# This block, test cases wal2-6.*, tests the operation of WAL with
410# "PRAGMA locking_mode=EXCLUSIVE" set.
411#
412# wal2-6.1.*: Changing to WAL mode before setting locking_mode=exclusive.
413#
414# wal2-6.2.*: Changing to WAL mode after setting locking_mode=exclusive.
415#
416# wal2-6.3.*: Changing back to rollback mode from WAL mode after setting
417# locking_mode=exclusive.
418#
419# wal2-6.4.*: Check that xShmLock calls are omitted in exclusive locking
420# mode.
421#
dan3cac5dc2010-06-04 18:37:59 +0000422# wal2-6.5.*:
423#
424# wal2-6.6.*: Check that if the xShmLock() to reaquire a WAL read-lock when
425# exiting exclusive mode fails (i.e. SQLITE_IOERR), then the
426# connection silently remains in exclusive mode.
427#
dan55437592010-05-11 12:19:26 +0000428do_test wal2-6.1.1 {
429 file delete -force test.db test.db-wal test.db-journal
430 sqlite3 db test.db
431 execsql {
432 Pragma Journal_Mode = Wal;
433 Pragma Locking_Mode = Exclusive;
434 }
435} {wal exclusive}
436do_test wal2-6.1.2 {
437 execsql { PRAGMA lock_status }
438} {main unlocked temp closed}
439do_test wal2-6.1.3 {
440 execsql {
441 BEGIN;
442 CREATE TABLE t1(a, b);
443 INSERT INTO t1 VALUES(1, 2);
444 COMMIT;
445 PRAGMA lock_status;
446 }
447} {main exclusive temp closed}
448do_test wal2-6.1.4 {
449 execsql {
450 PRAGMA locking_mode = normal;
451 PRAGMA lock_status;
452 }
453} {normal main exclusive temp closed}
454do_test wal2-6.1.5 {
455 execsql {
456 SELECT * FROM t1;
457 PRAGMA lock_status;
458 }
459} {1 2 main exclusive temp closed}
460do_test wal2-6.1.6 {
461 execsql {
462 INSERT INTO t1 VALUES(3, 4);
463 PRAGMA lock_status;
464 }
465} {main shared temp closed}
466db close
467
468do_test wal2-6.2.1 {
469 file delete -force test.db test.db-wal test.db-journal
470 sqlite3 db test.db
471 execsql {
472 Pragma Locking_Mode = Exclusive;
473 Pragma Journal_Mode = Wal;
474 Pragma Lock_Status;
475 }
476} {exclusive wal main exclusive temp closed}
477do_test wal2-6.2.2 {
478 execsql {
479 BEGIN;
480 CREATE TABLE t1(a, b);
481 INSERT INTO t1 VALUES(1, 2);
482 COMMIT;
483 Pragma loCK_STATus;
484 }
485} {main exclusive temp closed}
486do_test wal2-6.2.3 {
487 db close
488 sqlite3 db test.db
489 execsql { PRAGMA LOCKING_MODE = EXCLUSIVE }
490} {exclusive}
491do_test wal2-6.2.4 {
492 execsql {
493 SELECT * FROM t1;
494 pragma lock_status;
495 }
496} {1 2 main shared temp closed}
497do_test wal2-6.2.5 {
498 execsql {
499 INSERT INTO t1 VALUES(3, 4);
500 pragma lock_status;
501 }
502} {main exclusive temp closed}
503do_test wal2-6.2.6 {
504 execsql {
505 PRAGMA locking_mode = NORMAL;
506 pragma lock_status;
507 }
508} {normal main exclusive temp closed}
509do_test wal2-6.2.7 {
510 execsql {
511 BEGIN IMMEDIATE; COMMIT;
512 pragma lock_status;
513 }
514} {main shared temp closed}
515do_test wal2-6.2.8 {
516 execsql {
517 PRAGMA locking_mode = EXCLUSIVE;
518 BEGIN IMMEDIATE; COMMIT;
519 PRAGMA locking_mode = NORMAL;
520 }
521 execsql {
522 SELECT * FROM t1;
523 pragma lock_status;
524 }
525} {1 2 3 4 main exclusive temp closed}
526do_test wal2-6.2.9 {
527 execsql {
528 INSERT INTO t1 VALUES(5, 6);
529 SELECT * FROM t1;
530 pragma lock_status;
531 }
532} {1 2 3 4 5 6 main shared temp closed}
533db close
534
535do_test wal2-6.3.1 {
536 file delete -force test.db test.db-wal test.db-journal
537 sqlite3 db test.db
538 execsql {
539 PRAGMA journal_mode = WAL;
540 PRAGMA locking_mode = exclusive;
541 BEGIN;
542 CREATE TABLE t1(x);
543 INSERT INTO t1 VALUES('Chico');
544 INSERT INTO t1 VALUES('Harpo');
545 COMMIT;
546 }
547 list [file exists test.db-wal] [file exists test.db-journal]
548} {1 0}
549do_test wal2-6.3.2 {
550 execsql { PRAGMA journal_mode = DELETE }
551 file exists test.db-wal
552} {0}
553do_test wal2-6.3.3 {
554 execsql { PRAGMA lock_status }
555} {main exclusive temp closed}
556do_test wal2-6.3.4 {
557 execsql {
558 BEGIN;
559 INSERT INTO t1 VALUES('Groucho');
560 }
561 list [file exists test.db-wal] [file exists test.db-journal]
562} {0 1}
563do_test wal2-6.3.5 {
564 execsql { PRAGMA lock_status }
565} {main exclusive temp closed}
566do_test wal2-6.3.6 {
567 execsql { COMMIT }
568 list [file exists test.db-wal] [file exists test.db-journal]
569} {0 1}
570do_test wal2-6.3.7 {
571 execsql { PRAGMA lock_status }
572} {main exclusive temp closed}
573db close
574
dan5f3f3b22010-06-01 07:51:47 +0000575
576# This test - wal2-6.4.* - uses a single database connection and the
577# [testvfs] instrumentation to test that xShmLock() is being called
578# as expected when a WAL database is used with locking_mode=exclusive.
579#
dan55437592010-05-11 12:19:26 +0000580do_test wal2-6.4.1 {
581 file delete -force test.db test.db-wal test.db-journal
582 proc tvfs_cb {method args} {
583 set ::shm_file [lindex $args 0]
584 if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] }
585 return "SQLITE_OK"
586 }
dan1f55e282010-06-03 09:25:10 +0000587 testvfs tvfs
588 tvfs script tvfs_cb
dan55437592010-05-11 12:19:26 +0000589 sqlite3 db test.db -vfs tvfs
dan5f3f3b22010-06-01 07:51:47 +0000590} {}
dan55437592010-05-11 12:19:26 +0000591
dan5f3f3b22010-06-01 07:51:47 +0000592set RECOVERY {
593 {0 1 lock exclusive} {1 7 lock exclusive}
594 {1 7 unlock exclusive} {0 1 unlock exclusive}
595}
596set READMARK0_READ {
597 {3 1 lock shared} {3 1 unlock shared}
598}
599set READMARK0_WRITE {
600 {3 1 lock shared}
601 {0 1 lock exclusive} {3 1 unlock shared}
602 {4 1 lock exclusive} {4 1 unlock exclusive} {4 1 lock shared}
603 {0 1 unlock exclusive} {4 1 unlock shared}
604}
605set READMARK1_SET {
606 {4 1 lock exclusive} {4 1 unlock exclusive}
607}
608set READMARK1_READ {
609 {4 1 lock shared} {4 1 unlock shared}
610}
611
612foreach {tn sql res expected_locks} {
613 2 {
dan55437592010-05-11 12:19:26 +0000614 PRAGMA journal_mode = WAL;
dan5f3f3b22010-06-01 07:51:47 +0000615 BEGIN;
616 CREATE TABLE t1(x);
617 INSERT INTO t1 VALUES('Leonard');
618 INSERT INTO t1 VALUES('Arthur');
619 COMMIT;
620 } {wal} {
621 $RECOVERY
dan5f3f3b22010-06-01 07:51:47 +0000622 $READMARK0_WRITE
dan55437592010-05-11 12:19:26 +0000623 }
624
dan5f3f3b22010-06-01 07:51:47 +0000625 3 {
626 # This test should do the READMARK1_SET locking to populate the
627 # aReadMark[1] slot with the current mxFrame value. Followed by
628 # READMARK1_READ to read the database.
629 #
630 SELECT * FROM t1
631 } {Leonard Arthur} {
632 $READMARK1_SET
633 $READMARK1_READ
634 }
635
636 4 {
637 # aReadMark[1] is already set to mxFrame. So just READMARK1_READ
638 # this time, not READMARK1_SET.
639 #
640 SELECT * FROM t1 ORDER BY x
641 } {Arthur Leonard} {
642 $READMARK1_READ
643 }
644
645 5 {
646 PRAGMA locking_mode = exclusive
647 } {exclusive} { }
648
649 6 {
dan55437592010-05-11 12:19:26 +0000650 INSERT INTO t1 VALUES('Julius Henry');
651 SELECT * FROM t1;
dan5f3f3b22010-06-01 07:51:47 +0000652 } {Leonard Arthur {Julius Henry}} {
653 $READMARK1_READ
dan55437592010-05-11 12:19:26 +0000654 }
dan5f3f3b22010-06-01 07:51:47 +0000655
656 7 {
657 INSERT INTO t1 VALUES('Karl');
658 SELECT * FROM t1;
659 } {Leonard Arthur {Julius Henry} Karl} { }
660
661 8 {
662 PRAGMA locking_mode = normal
663 } {normal} { }
664
665 9 {
666 SELECT * FROM t1 ORDER BY x
667 } {Arthur {Julius Henry} Karl Leonard} { }
668
669 10 {
670 DELETE FROM t1
671 } {} {
672 $READMARK1_READ
dan55437592010-05-11 12:19:26 +0000673 }
dan5f3f3b22010-06-01 07:51:47 +0000674
675 11 {
676 SELECT * FROM t1
677 } {} {
678 $READMARK1_SET
679 $READMARK1_READ
680 }
681} {
682
683 set L [list]
684 foreach el [subst $expected_locks] { lappend L $el }
685
686 set S ""
687 foreach sq [split $sql "\n"] {
688 set sq [string trim $sq]
689 if {[string match {#*} $sq]==0} {append S "$sq\n"}
690 }
691
dan55437592010-05-11 12:19:26 +0000692 set ::locks [list]
dan5f3f3b22010-06-01 07:51:47 +0000693 do_test wal2-6.4.$tn.1 { execsql $S } $res
694 do_test wal2-6.4.$tn.2 { set ::locks } $L
695}
696
dan55437592010-05-11 12:19:26 +0000697db close
daned360202010-05-12 06:54:31 +0000698tvfs delete
699
700do_test wal2-6.5.1 {
701 sqlite3 db test.db
702 execsql {
703 PRAGMA journal_mode = wal;
704 PRAGMA locking_mode = exclusive;
705 CREATE TABLE t2(a, b);
706 PRAGMA wal_checkpoint;
707 INSERT INTO t2 VALUES('I', 'II');
708 PRAGMA journal_mode;
709 }
710} {wal exclusive wal}
dan14740f12010-05-14 08:39:48 +0000711do_test wal2-6.5.2 {
712 execsql {
713 PRAGMA locking_mode = normal;
714 INSERT INTO t2 VALUES('III', 'IV');
715 PRAGMA locking_mode = exclusive;
716 SELECT * FROM t2;
717 }
718} {normal exclusive I II III IV}
719do_test wal2-6.5.3 {
720 execsql { PRAGMA wal_checkpoint }
721} {}
daned360202010-05-12 06:54:31 +0000722db close
dan55437592010-05-11 12:19:26 +0000723
dan3cac5dc2010-06-04 18:37:59 +0000724proc lock_control {method filename handle spec} {
725 foreach {start n op type} $spec break
726 if {$op == "lock"} { return SQLITE_IOERR }
727 return SQLITE_OK
728}
729do_test wal2-6.6.1 {
730 testvfs T
731 T script lock_control
732 T filter {}
733 sqlite3 db test.db -vfs T
734 execsql { PRAGMA locking_mode = exclusive }
735 execsql { INSERT INTO t2 VALUES('V', 'VI') }
736} {}
737do_test wal2-6.6.2 {
738 execsql { PRAGMA locking_mode = normal }
739 T filter xShmLock
740 execsql { INSERT INTO t2 VALUES('VII', 'VIII') }
741} {}
742do_test wal2-6.6.3 {
743 # At this point the connection should still be in exclusive-mode, even
744 # though it tried to exit exclusive-mode when committing the INSERT
745 # statement above. To exit exclusive mode, SQLite has to take a read-lock
746 # on the WAL file using xShmLock(). Since that call failed, it remains
747 # in exclusive mode.
748 #
749 sqlite3 db2 test.db -vfs T
750 catchsql { SELECT * FROM t2 } db2
751} {1 {database is locked}}
752do_test wal2-6.6.2 {
753 db2 close
754 T filter {}
755 execsql { INSERT INTO t2 VALUES('IX', 'X') }
756} {}
757do_test wal2-6.6.3 {
758 # This time, we have successfully exited exclusive mode. So the second
759 # connection can read the database.
760 sqlite3 db2 test.db -vfs T
761 catchsql { SELECT * FROM t2 } db2
762} {0 {I II III IV V VI VII VIII IX X}}
763
764db close
765db2 close
766T delete
767
dan6f150142010-05-21 15:31:56 +0000768#-------------------------------------------------------------------------
769# Test a theory about the checksum algorithm. Theory was false and this
770# test did not provoke a bug.
dan8067adb2010-05-21 16:23:43 +0000771#
dan6f150142010-05-21 15:31:56 +0000772file delete -force test.db test.db-wal test.db-journal
773do_test wal2-7.1.1 {
774 sqlite3 db test.db
775 execsql {
776 PRAGMA page_size = 4096;
777 PRAGMA journal_mode = WAL;
778 CREATE TABLE t1(a, b);
779 }
780 file size test.db
781} {4096}
782do_test wal2-7.1.2 {
783 file copy -force test.db test2.db
784 file copy -force test.db-wal test2.db-wal
785 hexio_write test2.db-wal 48 FF
786} {1}
787do_test wal2-7.1.3 {
788 sqlite3 db2 test2.db
789 execsql { PRAGMA wal_checkpoint } db2
790 execsql { SELECT * FROM sqlite_master } db2
791} {}
shaneha1a889e2010-06-02 16:39:53 +0000792db close
dan6f150142010-05-21 15:31:56 +0000793db2 close
dan8067adb2010-05-21 16:23:43 +0000794file delete -force test.db test.db-wal test.db-journal
795do_test wal2-8.1.2 {
796 sqlite3 db test.db
797 execsql {
drhc2857bf2010-05-24 17:00:19 +0000798 PRAGMA auto_vacuum=OFF;
dan8067adb2010-05-21 16:23:43 +0000799 PRAGMA page_size = 1024;
800 PRAGMA journal_mode = WAL;
801 CREATE TABLE t1(x);
802 INSERT INTO t1 VALUES(zeroblob(8188*1020));
803 CREATE TABLE t2(y);
804 }
805 execsql {
806 PRAGMA wal_checkpoint;
dan47ee3862010-06-22 15:18:44 +0000807 SELECT rootpage>=8192 FROM sqlite_master WHERE tbl_name = 't2';
dan8067adb2010-05-21 16:23:43 +0000808 }
dan47ee3862010-06-22 15:18:44 +0000809} {1}
dan8067adb2010-05-21 16:23:43 +0000810do_test wal2-8.1.3 {
811 execsql {
812 PRAGMA cache_size = 10;
813 CREATE TABLE t3(z);
814 BEGIN;
815 INSERT INTO t3 VALUES(randomblob(900));
816 INSERT INTO t3 SELECT randomblob(900) FROM t3;
817 INSERT INTO t2 VALUES('hello');
818 INSERT INTO t3 SELECT randomblob(900) FROM t3;
819 INSERT INTO t3 SELECT randomblob(900) FROM t3;
820 INSERT INTO t3 SELECT randomblob(900) FROM t3;
821 INSERT INTO t3 SELECT randomblob(900) FROM t3;
822 INSERT INTO t3 SELECT randomblob(900) FROM t3;
823 INSERT INTO t3 SELECT randomblob(900) FROM t3;
824 ROLLBACK;
825 }
826 execsql {
827 INSERT INTO t2 VALUES('goodbye');
828 INSERT INTO t3 SELECT randomblob(900) FROM t3;
829 INSERT INTO t3 SELECT randomblob(900) FROM t3;
830 }
831} {}
832do_test wal2-8.1.4 {
833 sqlite3 db2 test.db
834 execsql { SELECT * FROM t2 }
835} {goodbye}
836db2 close
dan23dced32010-06-04 12:22:35 +0000837db close
dan8067adb2010-05-21 16:23:43 +0000838
dan23dced32010-06-04 12:22:35 +0000839#-------------------------------------------------------------------------
840# Test that even if the checksums for both are valid, if the two copies
841# of the wal-index header in the wal-index do not match, the client
842# runs (or at least tries to run) database recovery.
dan23f71922010-06-07 06:11:39 +0000843#
dan23dced32010-06-04 12:22:35 +0000844#
dan23f71922010-06-07 06:11:39 +0000845proc get_name {method args} { set ::filename [lindex $args 0] ; tvfs filter {} }
dan23dced32010-06-04 12:22:35 +0000846testvfs tvfs
847tvfs script get_name
848tvfs filter xShmOpen
849
850file delete -force test.db test.db-wal test.db-journal
851do_test wal2-9.1 {
852 sqlite3 db test.db -vfs tvfs
853 execsql {
854 PRAGMA journal_mode = WAL;
855 CREATE TABLE x(y);
856 INSERT INTO x VALUES('Barton');
857 INSERT INTO x VALUES('Deakin');
858 }
dan23f71922010-06-07 06:11:39 +0000859
860 # Set $wih(1) to the contents of the wal-index header after
861 # the frames associated with the first two rows in table 'x' have
862 # been inserted. Then insert one more row and set $wih(2)
863 # to the new value of the wal-index header.
864 #
865 # If the $wih(1) is written into the wal-index before running
866 # a read operation, the client will see only the first two rows. If
867 # $wih(2) is written into the wal-index, the client will see
868 # three rows. If an invalid header is written into the wal-index, then
869 # the client will run recovery and see three rows.
870 #
871 set wih(1) [set_tvfs_hdr $::filename]
dan23dced32010-06-04 12:22:35 +0000872 execsql { INSERT INTO x VALUES('Watson') }
dan23f71922010-06-07 06:11:39 +0000873 set wih(2) [set_tvfs_hdr $::filename]
874
dan23dced32010-06-04 12:22:35 +0000875 sqlite3 db2 test.db -vfs tvfs
876 execsql { SELECT * FROM x } db2
877} {Barton Deakin Watson}
dan23f71922010-06-07 06:11:39 +0000878
879foreach {tn hdr1 hdr2 res} [list \
880 3 $wih(1) $wih(1) {Barton Deakin} \
881 4 $wih(1) $wih(2) {Barton Deakin Watson} \
882 5 $wih(2) $wih(1) {Barton Deakin Watson} \
883 6 $wih(2) $wih(2) {Barton Deakin Watson} \
884 7 $wih(1) $wih(1) {Barton Deakin} \
dan10f5a502010-06-23 15:55:43 +0000885 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}
dan23f71922010-06-07 06:11:39 +0000886] {
887 do_test wal2-9.$tn {
888 set_tvfs_hdr $::filename $hdr1 $hdr2
889 execsql { SELECT * FROM x } db2
890 } $res
891}
dan23dced32010-06-04 12:22:35 +0000892
893db2 close
894db close
dan8067adb2010-05-21 16:23:43 +0000895
dan10f5a502010-06-23 15:55:43 +0000896#-------------------------------------------------------------------------
897# This block of tests - wal2-10.* - focus on the libraries response to
898# new versions of the wal or wal-index formats.
899#
900# wal2-10.1.*: Test that the library refuses to "recover" a new WAL
901# format.
902#
903# wal2-10.2.*: Test that the library refuses to read or write a database
904# if the wal-index version is newer than it understands.
905#
906# At time of writing, the only versions of the wal and wal-index formats
907# that exist are versions 3007000 (corresponding to SQLite version 3.7.0,
908# the first version of SQLite to feature wal mode).
909#
910do_test wal2-10.1.1 {
911 faultsim_delete_and_reopen
912 execsql {
913 PRAGMA journal_mode = WAL;
914 CREATE TABLE t1(a, b);
915 PRAGMA wal_checkpoint;
916 INSERT INTO t1 VALUES(1, 2);
917 INSERT INTO t1 VALUES(3, 4);
918 }
919 faultsim_save_and_close
920} {}
921do_test wal2-10.1.2 {
922 faultsim_restore_and_reopen
923 execsql { SELECT * FROM t1 }
924} {1 2 3 4}
925do_test wal2-10.1.3 {
926 faultsim_restore_and_reopen
927 set hdr [wal_set_walhdr test.db-wal]
928 lindex $hdr 1
929} {3007000}
930do_test wal2-10.1.4 {
931 lset hdr 1 3007001
932 wal_set_walhdr test.db-wal $hdr
933 catchsql { SELECT * FROM t1 }
934} {1 {unable to open database file}}
935
936testvfs tvfs -default 1
937do_test wal2-10.2.1 {
938 faultsim_restore_and_reopen
939 execsql { SELECT * FROM t1 }
940} {1 2 3 4}
941do_test wal2-10.2.2 {
942 set hdr [set_tvfs_hdr $::filename]
943 lindex $hdr 0
944} {3007000}
945breakpoint
946do_test wal2-10.2.3 {
947 lset hdr 0 3007001
948 wal_fix_walindex_cksum hdr
949 set_tvfs_hdr $::filename $hdr
950 catchsql { SELECT * FROM t1 }
951} {1 {unable to open database file}}
danc3857932010-07-14 07:06:47 +0000952db close
953tvfs delete
954
955#-------------------------------------------------------------------------
956# This block of tests - wal2-11.* - tests that it is not possible to put
957# the library into an infinite loop by presenting it with a corrupt
958# hash table (one that appears to contain a single chain of infinite
959# length).
960#
961# wal2-11.1.*: While reading the hash-table.
962#
963# wal2-11.2.*: While writing the hash-table.
964#
965testvfs tvfs -default 1
966do_test wal2-11.0 {
967 faultsim_delete_and_reopen
968 execsql {
969 PRAGMA journal_mode = WAL;
970 CREATE TABLE t1(a, b, c);
971 INSERT INTO t1 VALUES(1, 2, 3);
972 INSERT INTO t1 VALUES(4, 5, 6);
973 INSERT INTO t1 VALUES(7, 8, 9);
974 SELECT * FROM t1;
975 }
976} {wal 1 2 3 4 5 6 7 8 9}
977
978do_test wal2-11.1.1 {
979 sqlite3 db2 test.db
980 execsql { SELECT name FROM sqlite_master } db2
981} {t1}
982
983# Fill up the hash table on the first page of shared memory with 0x55 bytes.
984#
985set blob [string range [tvfs shm $::filename] 0 16383]
986append blob [string repeat [binary format c 0x55] 16384]
987tvfs shm $::filename $blob
988
989do_test wal2-11.2 {
990 catchsql { SELECT * FROM t1 } db2
991} {1 {database disk image is malformed}}
992
993do_test wal2-12.1 {
994 catchsql { INSERT INTO t1 VALUES(10, 11, 12) }
995} {1 {database disk image is malformed}}
996
997db close
998db2 close
999tvfs delete
dan10f5a502010-06-23 15:55:43 +00001000
danddb0ac42010-07-14 14:48:58 +00001001#-------------------------------------------------------------------------
1002# If a connection is required to create a WAL or SHM file, it creates
1003# the new files with the same file-system permissions as the database
1004# file itself. Test this.
1005#
1006if {$::tcl_platform(platform) == "unix"} {
1007 faultsim_delete_and_reopen
danddb0ac42010-07-14 14:48:58 +00001008 set umask [exec /bin/sh -c umask]
dan50833e32010-07-14 16:37:17 +00001009
danddb0ac42010-07-14 14:48:58 +00001010 do_test wal2-12.1 {
1011 sqlite3 db test.db
1012 execsql {
1013 CREATE TABLE tx(y, z);
1014 PRAGMA journal_mode = WAL;
1015 }
1016 db close
1017 list [file exists test.db-wal] [file exists test.db-shm]
1018 } {0 0}
1019
1020 foreach {tn permissions} {
1021 1 00644
1022 2 00666
1023 3 00600
1024 4 00755
1025 } {
1026 set effective [format %.5o [expr $permissions & ~$umask]]
1027 do_test wal2-12.2.$tn.1 {
1028 file attributes test.db -permissions $permissions
1029 file attributes test.db -permissions
1030 } $permissions
1031 do_test wal2-12.2.$tn.2 {
1032 list [file exists test.db-wal] [file exists test.db-shm]
1033 } {0 0}
1034 do_test wal2-12.2.$tn.3 {
1035 sqlite3 db test.db
1036 execsql { INSERT INTO tx DEFAULT VALUES }
1037 list [file exists test.db-wal] [file exists test.db-shm]
1038 } {1 1}
1039 do_test wal2-12.2.$tn.4 {
1040 list [file attr test.db-wal -perm] [file attr test.db-shm -perm]
1041 } [list $effective $effective]
1042 do_test wal2-12.2.$tn.5 {
1043 db close
1044 list [file exists test.db-wal] [file exists test.db-shm]
1045 } {0 0}
1046 }
1047}
1048
dan50833e32010-07-14 16:37:17 +00001049#-------------------------------------------------------------------------
1050# Test the libraries response to discovering that one or more of the
1051# database, wal or shm files cannot be opened, or can only be opened
1052# read-only.
1053#
1054if {$::tcl_platform(platform) == "unix"} {
1055 proc perm {} {
1056 set L [list]
1057 foreach f {test.db test.db-wal test.db-shm} {
1058 if {[file exists $f]} {
1059 lappend L [file attr $f -perm]
1060 } else {
1061 lappend L {}
1062 }
1063 }
1064 set L
1065 }
1066
1067 faultsim_delete_and_reopen
1068 execsql {
1069 PRAGMA journal_mode = WAL;
1070 CREATE TABLE t1(a, b);
1071 PRAGMA wal_checkpoint;
1072 INSERT INTO t1 VALUES('3.14', '2.72');
1073 }
1074 do_test wal2-13.1.1 {
1075 list [file exists test.db-shm] [file exists test.db-wal]
1076 } {1 1}
1077 faultsim_save_and_close
1078
1079 foreach {tn db_perm wal_perm shm_perm can_open can_read can_write} {
1080 2 00644 00644 00644 1 1 1
1081 3 00644 00400 00644 1 0 0
1082 4 00644 00644 00400 1 0 0
1083 5 00400 00644 00644 1 1 0
1084
1085 7 00644 00000 00644 1 0 0
1086 8 00644 00644 00000 1 0 0
1087 9 00000 00644 00644 0 0 0
1088 } {
1089 faultsim_restore
1090 do_test wal2-13.$tn.1 {
1091 file attr test.db -perm $db_perm
1092 file attr test.db-wal -perm $wal_perm
1093 file attr test.db-shm -perm $shm_perm
1094
1095 set L [file attr test.db -perm]
1096 lappend L [file attr test.db-wal -perm]
1097 lappend L [file attr test.db-shm -perm]
1098 } [list $db_perm $wal_perm $shm_perm]
1099
1100 # If $can_open is true, then it should be possible to open a database
1101 # handle. Otherwise, if $can_open is 0, attempting to open the db
1102 # handle throws an "unable to open database file" exception.
1103 #
1104 set r(1) {0 ok}
1105 set r(0) {1 {unable to open database file}}
1106 do_test wal2-13.$tn.2 {
1107 list [catch {sqlite3 db test.db ; set {} ok} msg] $msg
1108 } $r($can_open)
1109
1110 if {$can_open} {
1111
1112 # If $can_read is true, then the client should be able to read from
1113 # the database file. If $can_read is false, attempting to read should
1114 # throw the "unable to open database file" exception.
1115 #
1116 set a(0) {1 {unable to open database file}}
1117 set a(1) {0 {3.14 2.72}}
1118 do_test wal2-13.$tn.3 {
1119 catchsql { SELECT * FROM t1 }
1120 } $a($can_read)
1121
1122 # Now try to write to the db file. If the client can read but not
1123 # write, then it should throw the familiar "unable to open db file"
1124 # exception. If it can read but not write, the exception should
1125 # be "attempt to write a read only database".
1126 #
1127 # If the client can read and write, the operation should succeed.
1128 #
1129 set b(0,0) {1 {unable to open database file}}
1130 set b(1,0) {1 {attempt to write a readonly database}}
1131 set b(1,1) {0 {}}
1132 do_test wal2-13.$tn.4 {
1133 catchsql { INSERT INTO t1 DEFAULT VALUES }
1134 } $b($can_read,$can_write)
1135 }
1136 catch { db close }
1137 }
1138}
1139
dan9a6b4e92010-05-06 11:32:09 +00001140finish_test
dan10f5a502010-06-23 15:55:43 +00001141