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