Annotation of embedaddon/sqlite3/test/wal2.test, revision 1.1.1.1
1.1 misho 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:
16: set testdir [file dirname $argv0]
17: source $testdir/tester.tcl
18: source $testdir/lock_common.tcl
19: source $testdir/malloc_common.tcl
20: source $testdir/wal_common.tcl
21:
22: set testprefix wal2
23:
24: ifcapable !wal {finish_test ; return }
25:
26: set sqlite_sync_count 0
27: proc 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:
38: proc 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:
65: proc 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: #
94: do_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}
119: do_test wal2-1.1 {
120: execsql { SELECT count(a), sum(a) FROM t1 } db2
121: } {4 10}
122:
123: set RECOVER [list \
124: {0 1 lock exclusive} {1 7 lock exclusive} \
125: {1 7 unlock exclusive} {0 1 unlock exclusive} \
126: ]
127: set READ [list \
128: {4 1 lock exclusive} {4 1 unlock exclusive} \
129: {4 1 lock shared} {4 1 unlock shared} \
130: ]
131:
132: foreach {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: }
164: db close
165: db2 close
166: tvfs delete
167: forcedelete 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: #
183: set WRITER [list 0 1 lock exclusive]
184: set 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: ]
189: do_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}
214: do_test wal2-2.1 {
215: execsql { SELECT count(a), sum(a) FROM t1 } db2
216: } {4 10}
217:
218: foreach {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: }
271: db close
272: db2 close
273: tvfs delete
274: forcedelete test.db test.db-wal test.db-journal
275:
276:
277: if 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: #
285: do_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}
315: do_test wal2-3.1 {
316: execsql { SELECT count(a), sum(a) FROM t1 }
317: } {4 10}
318: do_test wal2-3.2 {
319: info exists ::locked
320: } {0}
321:
322: do_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}
339: do_test wal2-3.4 {
340: execsql { SELECT count(a), sum(a) FROM t1 }
341: } {4 10}
342: do_test wal2-3.5 {
343: list [info exists ::sabotage] [info exists ::locked]
344: } {0 0}
345: db close
346: tvfs delete
347: forcedelete 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: #
355: do_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}
367: do_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}}
373: do_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}}}
379: db close
380: tvfs 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: #
390: set expected_locks [list]
391: lappend expected_locks {1 1 lock exclusive} ;# Lock checkpoint
392: lappend expected_locks {0 1 lock exclusive} ;# Lock writer
393: lappend expected_locks {2 6 lock exclusive} ;# Lock recovery & all aReadMark[]
394: lappend expected_locks {2 6 unlock exclusive} ;# Unlock recovery & aReadMark[]
395: lappend expected_locks {0 1 unlock exclusive} ;# Unlock writer
396: lappend expected_locks {3 1 lock exclusive} ;# Lock aReadMark[0]
397: lappend expected_locks {3 1 unlock exclusive} ;# Unlock aReadMark[0]
398: lappend expected_locks {1 1 unlock exclusive} ;# Unlock checkpoint
399: do_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
422: db close
423: tvfs 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: #
445: do_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}
452: do_test wal2-6.1.2 {
453: execsql { PRAGMA lock_status }
454: } {main unlocked temp closed}
455: do_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}
468: do_test wal2-6.1.4 {
469: execsql {
470: PRAGMA locking_mode = normal;
471: PRAGMA lock_status;
472: }
473: } {normal main exclusive temp closed}
474: do_test wal2-6.1.5 {
475: execsql {
476: SELECT * FROM t1;
477: PRAGMA lock_status;
478: }
479: } {1 2 main shared temp closed}
480: do_test wal2-6.1.6 {
481: execsql {
482: INSERT INTO t1 VALUES(3, 4);
483: PRAGMA lock_status;
484: }
485: } {main shared temp closed}
486: db close
487:
488: do_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}
497: do_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}
506: do_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}
512: do_test wal2-6.2.4 {
513: execsql {
514: SELECT * FROM t1;
515: pragma lock_status;
516: }
517: } {1 2 main shared temp closed}
518: do_test wal2-6.2.5 {
519: execsql {
520: INSERT INTO t1 VALUES(3, 4);
521: pragma lock_status;
522: }
523: } {main exclusive temp closed}
524: do_test wal2-6.2.6 {
525: execsql {
526: PRAGMA locking_mode = NORMAL;
527: pragma lock_status;
528: }
529: } {normal main exclusive temp closed}
530: do_test wal2-6.2.7 {
531: execsql {
532: BEGIN IMMEDIATE; COMMIT;
533: pragma lock_status;
534: }
535: } {main shared temp closed}
536: do_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}
547: do_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}
554: db close
555:
556: do_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}
570: do_test wal2-6.3.2 {
571: execsql { PRAGMA journal_mode = DELETE }
572: file exists test.db-wal
573: } {0}
574: do_test wal2-6.3.3 {
575: execsql { PRAGMA lock_status }
576: } {main exclusive temp closed}
577: do_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}
584: do_test wal2-6.3.5 {
585: execsql { PRAGMA lock_status }
586: } {main exclusive temp closed}
587: do_test wal2-6.3.6 {
588: execsql { COMMIT }
589: list [file exists test.db-wal] [file exists test.db-journal]
590: } {0 1}
591: do_test wal2-6.3.7 {
592: execsql { PRAGMA lock_status }
593: } {main exclusive temp closed}
594: db 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: #
601: do_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:
614: set RECOVERY {
615: {0 1 lock exclusive} {1 7 lock exclusive}
616: {1 7 unlock exclusive} {0 1 unlock exclusive}
617: }
618: set READMARK0_READ {
619: {3 1 lock shared} {3 1 unlock shared}
620: }
621: set 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: }
627: set READMARK1_SET {
628: {4 1 lock exclusive} {4 1 unlock exclusive}
629: }
630: set READMARK1_READ {
631: {4 1 lock shared} {4 1 unlock shared}
632: }
633: set READMARK1_WRITE {
634: {4 1 lock shared}
635: {0 1 lock exclusive} {0 1 unlock exclusive}
636: {4 1 unlock shared}
637: }
638:
639: foreach {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:
721: db close
722: tvfs delete
723:
724: do_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}
736: do_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}
744: do_test wal2-6.5.3 {
745: execsql { PRAGMA wal_checkpoint }
746: } {0 2 2}
747: db close
748:
749: proc 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: }
754: do_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: } {}
763: do_test wal2-6.6.2 {
764: execsql { PRAGMA locking_mode = normal }
765: T filter xShmLock
766: execsql { INSERT INTO t2 VALUES('VII', 'VIII') }
767: } {}
768: do_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}}
778: do_test wal2-6.6.2 {
779: db2 close
780: T filter {}
781: execsql { INSERT INTO t2 VALUES('IX', 'X') }
782: } {}
783: do_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:
790: db close
791: db2 close
792: T delete
793:
794: #-------------------------------------------------------------------------
795: # Test a theory about the checksum algorithm. Theory was false and this
796: # test did not provoke a bug.
797: #
798: forcedelete test.db test.db-wal test.db-journal
799: do_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}
808: do_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}
813: do_test wal2-7.1.3 {
814: sqlite3 db2 test2.db
815: execsql { PRAGMA wal_checkpoint } db2
816: execsql { SELECT * FROM sqlite_master } db2
817: } {}
818: db close
819: db2 close
820: forcedelete test.db test.db-wal test.db-journal
821: do_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}
836: do_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: } {}
858: do_test wal2-8.1.4 {
859: sqlite3 db2 test.db
860: execsql { SELECT * FROM t2 }
861: } {goodbye}
862: db2 close
863: db 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: #
871: proc get_name {method args} { set ::filename [lindex $args 0] ; tvfs filter {} }
872: testvfs tvfs
873: tvfs script get_name
874: tvfs filter xShmOpen
875:
876: forcedelete test.db test.db-wal test.db-journal
877: do_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:
905: foreach {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:
919: db2 close
920: db 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: #
936: do_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: } {}
947: do_test wal2-10.1.2 {
948: faultsim_restore_and_reopen
949: execsql { SELECT * FROM t1 }
950: } {1 2 3 4}
951: do_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}
956: do_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:
962: testvfs tvfs -default 1
963: do_test wal2-10.2.1 {
964: faultsim_restore_and_reopen
965: execsql { SELECT * FROM t1 }
966: } {1 2 3 4}
967: do_test wal2-10.2.2 {
968: set hdr [set_tvfs_hdr $::filename]
969: lindex $hdr 0
970: } {3007000}
971: do_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}}
977: db close
978: tvfs 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: #
990: testvfs tvfs -default 1
991: do_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:
1003: do_test wal2-11.1.1 {
1004: sqlite3 db2 test.db
1005: execsql { SELECT name FROM sqlite_master } db2
1006: } {t1}
1007:
1008: if {$::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:
1034: db close
1035: db2 close
1036: tvfs 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: #
1043: if {$::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: #
1091: if {$::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: #
1180: foreach {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:
1233: catch { db close }
1234:
1235: # PRAGMA checkpoint_fullsync
1236: # PRAGMA fullfsync
1237: # PRAGMA synchronous
1238: #
1239: foreach {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:
1276: if { $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:
1311: finish_test
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>