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