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 7 lock exclusive} \ 126 {1 7 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 6 lock exclusive} ;# Lock recovery & all aReadMark[] 397lappend expected_locks {2 6 unlock exclusive} ;# Unlock recovery & aReadMark[] 398lappend expected_locks {0 1 unlock exclusive} ;# Unlock writer 399lappend expected_locks {3 1 lock exclusive} ;# Lock aReadMark[0] 400lappend expected_locks {3 1 unlock exclusive} ;# Unlock aReadMark[0] 401lappend expected_locks {1 1 unlock exclusive} ;# Unlock checkpoint 402do_test wal2-5.1 { 403 proc tvfs_cb {method args} { 404 set ::shm_file [lindex $args 0] 405 if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] } 406 return $::tvfs_cb_return 407 } 408 set tvfs_cb_return SQLITE_OK 409 410 testvfs tvfs 411 tvfs script tvfs_cb 412 413 sqlite3 db test.db -vfs tvfs 414 execsql { 415 PRAGMA journal_mode = WAL; 416 CREATE TABLE x(y); 417 INSERT INTO x VALUES(1); 418 } 419 420 incr_tvfs_hdr $::shm_file 1 1 421 set ::locks [list] 422 execsql { PRAGMA wal_checkpoint } 423 set ::locks 424} $expected_locks 425db close 426tvfs delete 427 428#------------------------------------------------------------------------- 429# This block, test cases wal2-6.*, tests the operation of WAL with 430# "PRAGMA locking_mode=EXCLUSIVE" set. 431# 432# wal2-6.1.*: Changing to WAL mode before setting locking_mode=exclusive. 433# 434# wal2-6.2.*: Changing to WAL mode after setting locking_mode=exclusive. 435# 436# wal2-6.3.*: Changing back to rollback mode from WAL mode after setting 437# locking_mode=exclusive. 438# 439# wal2-6.4.*: Check that xShmLock calls are omitted in exclusive locking 440# mode. 441# 442# wal2-6.5.*: 443# 444# wal2-6.6.*: Check that if the xShmLock() to reaquire a WAL read-lock when 445# exiting exclusive mode fails (i.e. SQLITE_IOERR), then the 446# connection silently remains in exclusive mode. 447# 448do_test wal2-6.1.1 { 449 forcedelete test.db test.db-wal test.db-journal 450 sqlite3 db test.db 451 execsql { 452 Pragma Journal_Mode = Wal; 453 } 454} {wal} 455do_test wal2-6.1.2 { 456 execsql { PRAGMA lock_status } 457} {main unlocked temp closed} 458do_test wal2-6.1.3 { 459 execsql { 460 SELECT * FROM sqlite_master; 461 Pragma Locking_Mode = Exclusive; 462 } 463 execsql { 464 BEGIN; 465 CREATE TABLE t1(a, b); 466 INSERT INTO t1 VALUES(1, 2); 467 COMMIT; 468 PRAGMA lock_status; 469 } 470} {main exclusive temp closed} 471do_test wal2-6.1.4 { 472 execsql { 473 PRAGMA locking_mode = normal; 474 PRAGMA lock_status; 475 } 476} {normal main exclusive temp closed} 477do_test wal2-6.1.5 { 478 execsql { 479 SELECT * FROM t1; 480 PRAGMA lock_status; 481 } 482} {1 2 main shared temp closed} 483do_test wal2-6.1.6 { 484 execsql { 485 INSERT INTO t1 VALUES(3, 4); 486 PRAGMA lock_status; 487 } 488} {main shared temp closed} 489db close 490 491do_test wal2-6.2.1 { 492 forcedelete test.db test.db-wal test.db-journal 493 sqlite3 db test.db 494 execsql { 495 Pragma Locking_Mode = Exclusive; 496 Pragma Journal_Mode = Wal; 497 Pragma Lock_Status; 498 } 499} {exclusive wal main exclusive temp closed} 500do_test wal2-6.2.2 { 501 execsql { 502 BEGIN; 503 CREATE TABLE t1(a, b); 504 INSERT INTO t1 VALUES(1, 2); 505 COMMIT; 506 Pragma loCK_STATus; 507 } 508} {main exclusive temp closed} 509do_test wal2-6.2.3 { 510 db close 511 sqlite3 db test.db 512 execsql { SELECT * FROM sqlite_master } 513 execsql { PRAGMA LOCKING_MODE = EXCLUSIVE } 514} {exclusive} 515do_test wal2-6.2.4 { 516 execsql { 517 SELECT * FROM t1; 518 pragma lock_status; 519 } 520} {1 2 main shared temp closed} 521do_test wal2-6.2.5 { 522 execsql { 523 INSERT INTO t1 VALUES(3, 4); 524 pragma lock_status; 525 } 526} {main exclusive temp closed} 527do_test wal2-6.2.6 { 528 execsql { 529 PRAGMA locking_mode = NORMAL; 530 pragma lock_status; 531 } 532} {normal main exclusive temp closed} 533do_test wal2-6.2.7 { 534 execsql { 535 BEGIN IMMEDIATE; COMMIT; 536 pragma lock_status; 537 } 538} {main shared temp closed} 539do_test wal2-6.2.8 { 540 execsql { 541 PRAGMA locking_mode = EXCLUSIVE; 542 BEGIN IMMEDIATE; COMMIT; 543 PRAGMA locking_mode = NORMAL; 544 } 545 execsql { 546 SELECT * FROM t1; 547 pragma lock_status; 548 } 549} {1 2 3 4 main shared temp closed} 550do_test wal2-6.2.9 { 551 execsql { 552 INSERT INTO t1 VALUES(5, 6); 553 SELECT * FROM t1; 554 pragma lock_status; 555 } 556} {1 2 3 4 5 6 main shared temp closed} 557db close 558 559do_test wal2-6.3.1 { 560 forcedelete test.db test.db-wal test.db-journal 561 sqlite3 db test.db 562 execsql { 563 PRAGMA journal_mode = WAL; 564 PRAGMA locking_mode = exclusive; 565 BEGIN; 566 CREATE TABLE t1(x); 567 INSERT INTO t1 VALUES('Chico'); 568 INSERT INTO t1 VALUES('Harpo'); 569 COMMIT; 570 } 571 list [file exists test.db-wal] [file exists test.db-journal] 572} {1 0} 573do_test wal2-6.3.2 { 574 execsql { PRAGMA journal_mode = DELETE } 575 file exists test.db-wal 576} {0} 577do_test wal2-6.3.3 { 578 execsql { PRAGMA lock_status } 579} {main exclusive temp closed} 580do_test wal2-6.3.4 { 581 execsql { 582 BEGIN; 583 INSERT INTO t1 VALUES('Groucho'); 584 } 585 list [file exists test.db-wal] [file exists test.db-journal] 586} {0 1} 587do_test wal2-6.3.5 { 588 execsql { PRAGMA lock_status } 589} {main exclusive temp closed} 590do_test wal2-6.3.6 { 591 execsql { COMMIT } 592 list [file exists test.db-wal] [file exists test.db-journal] 593} {0 1} 594do_test wal2-6.3.7 { 595 execsql { PRAGMA lock_status } 596} {main exclusive temp closed} 597db close 598 599 600# This test - wal2-6.4.* - uses a single database connection and the 601# [testvfs] instrumentation to test that xShmLock() is being called 602# as expected when a WAL database is used with locking_mode=exclusive. 603# 604do_test wal2-6.4.1 { 605 forcedelete test.db test.db-wal test.db-journal 606 proc tvfs_cb {method args} { 607 set ::shm_file [lindex $args 0] 608 if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] } 609 return "SQLITE_OK" 610 } 611 testvfs tvfs 612 tvfs script tvfs_cb 613 sqlite3 db test.db -vfs tvfs 614 set {} {} 615} {} 616 617set RECOVERY { 618 {0 1 lock exclusive} {1 7 lock exclusive} 619 {1 7 unlock exclusive} {0 1 unlock exclusive} 620} 621set READMARK0_READ { 622 {3 1 lock shared} {3 1 unlock shared} 623} 624set READMARK0_WRITE { 625 {3 1 lock shared} 626 {0 1 lock exclusive} {3 1 unlock shared} 627 {4 1 lock exclusive} {4 1 unlock exclusive} {4 1 lock shared} 628 {0 1 unlock exclusive} {4 1 unlock shared} 629} 630set READMARK1_SET { 631 {4 1 lock exclusive} {4 1 unlock exclusive} 632} 633set READMARK1_READ { 634 {4 1 lock shared} {4 1 unlock shared} 635} 636set READMARK1_WRITE { 637 {4 1 lock shared} 638 {0 1 lock exclusive} {0 1 unlock exclusive} 639 {4 1 unlock shared} 640} 641 642foreach {tn sql res expected_locks} { 643 2 { 644 PRAGMA auto_vacuum = 0; 645 PRAGMA journal_mode = WAL; 646 BEGIN; 647 CREATE TABLE t1(x); 648 INSERT INTO t1 VALUES('Leonard'); 649 INSERT INTO t1 VALUES('Arthur'); 650 COMMIT; 651 } {wal} { 652 $RECOVERY 653 $READMARK0_WRITE 654 } 655 656 3 { 657 # This test should do the READMARK1_SET locking to populate the 658 # aReadMark[1] slot with the current mxFrame value. Followed by 659 # READMARK1_READ to read the database. 660 # 661 SELECT * FROM t1 662 } {Leonard Arthur} { 663 $READMARK1_SET 664 $READMARK1_READ 665 } 666 667 4 { 668 # aReadMark[1] is already set to mxFrame. So just READMARK1_READ 669 # this time, not READMARK1_SET. 670 # 671 SELECT * FROM t1 ORDER BY x 672 } {Arthur Leonard} { 673 $READMARK1_READ 674 } 675 676 5 { 677 PRAGMA locking_mode = exclusive 678 } {exclusive} { } 679 680 6 { 681 INSERT INTO t1 VALUES('Julius Henry'); 682 SELECT * FROM t1; 683 } {Leonard Arthur {Julius Henry}} { 684 $READMARK1_READ 685 } 686 687 7 { 688 INSERT INTO t1 VALUES('Karl'); 689 SELECT * FROM t1; 690 } {Leonard Arthur {Julius Henry} Karl} { } 691 692 8 { 693 PRAGMA locking_mode = normal 694 } {normal} { } 695 696 9 { 697 SELECT * FROM t1 ORDER BY x 698 } {Arthur {Julius Henry} Karl Leonard} $READMARK1_READ 699 700 10 { DELETE FROM t1 } {} $READMARK1_WRITE 701 702 11 { 703 SELECT * FROM t1 704 } {} { 705 $READMARK1_SET 706 $READMARK1_READ 707 } 708} { 709 710 set L [list] 711 foreach el [subst $expected_locks] { lappend L $el } 712 713 set S "" 714 foreach sq [split $sql "\n"] { 715 set sq [string trim $sq] 716 if {[string match {#*} $sq]==0} {append S "$sq\n"} 717 } 718 719 set ::locks [list] 720 do_test wal2-6.4.$tn.1 { execsql $S } $res 721 do_test wal2-6.4.$tn.2 { set ::locks } $L 722} 723 724db close 725tvfs delete 726 727do_test wal2-6.5.1 { 728 sqlite3 db test.db 729 execsql { 730 PRAGMA auto_vacuum = 0; 731 PRAGMA journal_mode = wal; 732 PRAGMA locking_mode = exclusive; 733 CREATE TABLE t2(a, b); 734 PRAGMA wal_checkpoint; 735 INSERT INTO t2 VALUES('I', 'II'); 736 PRAGMA journal_mode; 737 } 738} {wal exclusive 0 2 2 wal} 739do_test wal2-6.5.2 { 740 execsql { 741 PRAGMA locking_mode = normal; 742 INSERT INTO t2 VALUES('III', 'IV'); 743 PRAGMA locking_mode = exclusive; 744 SELECT * FROM t2; 745 } 746} {normal exclusive I II III IV} 747do_test wal2-6.5.3 { 748 execsql { PRAGMA wal_checkpoint } 749} {0 2 2} 750db close 751 752proc lock_control {method filename handle spec} { 753 foreach {start n op type} $spec break 754 if {$op == "lock"} { return SQLITE_IOERR } 755 return SQLITE_OK 756} 757do_test wal2-6.6.1 { 758 testvfs T 759 T script lock_control 760 T filter {} 761 sqlite3 db test.db -vfs T 762 execsql { SELECT * FROM sqlite_master } 763 execsql { PRAGMA locking_mode = exclusive } 764 execsql { INSERT INTO t2 VALUES('V', 'VI') } 765} {} 766do_test wal2-6.6.2 { 767 execsql { PRAGMA locking_mode = normal } 768 T filter xShmLock 769 execsql { INSERT INTO t2 VALUES('VII', 'VIII') } 770} {} 771do_test wal2-6.6.3 { 772 # At this point the connection should still be in exclusive-mode, even 773 # though it tried to exit exclusive-mode when committing the INSERT 774 # statement above. To exit exclusive mode, SQLite has to take a read-lock 775 # on the WAL file using xShmLock(). Since that call failed, it remains 776 # in exclusive mode. 777 # 778 sqlite3 db2 test.db -vfs T 779 catchsql { SELECT * FROM t2 } db2 780} {1 {database is locked}} 781do_test wal2-6.6.2 { 782 db2 close 783 T filter {} 784 execsql { INSERT INTO t2 VALUES('IX', 'X') } 785} {} 786do_test wal2-6.6.4 { 787 # This time, we have successfully exited exclusive mode. So the second 788 # connection can read the database. 789 sqlite3 db2 test.db -vfs T 790 catchsql { SELECT * FROM t2 } db2 791} {0 {I II III IV V VI VII VIII IX X}} 792 793db close 794db2 close 795T delete 796 797#------------------------------------------------------------------------- 798# Test a theory about the checksum algorithm. Theory was false and this 799# test did not provoke a bug. 800# 801forcedelete test.db test.db-wal test.db-journal 802do_test wal2-7.1.1 { 803 sqlite3 db test.db 804 execsql { 805 PRAGMA page_size = 4096; 806 PRAGMA journal_mode = WAL; 807 CREATE TABLE t1(a, b); 808 } 809 file size test.db 810} {4096} 811do_test wal2-7.1.2 { 812 forcecopy test.db test2.db 813 forcecopy test.db-wal test2.db-wal 814 # The first 32 bytes of the WAL file contain the WAL header. Offset 48 815 # is the first byte of the checksum for the first frame in the WAL. 816 # The following three lines replaces the contents of that byte with 817 # a different value. 818 set newval FF 819 if {$newval == [hexio_read test2.db-wal 48 1]} { set newval 00 } 820 hexio_write test2.db-wal 48 $newval 821} {1} 822do_test wal2-7.1.3 { 823 sqlite3 db2 test2.db 824 execsql { PRAGMA wal_checkpoint } db2 825 execsql { SELECT * FROM sqlite_master } db2 826} {} 827db close 828db2 close 829forcedelete test.db test.db-wal test.db-journal 830do_test wal2-8.1.2 { 831 sqlite3 db test.db 832 execsql { 833 PRAGMA auto_vacuum=OFF; 834 PRAGMA page_size = 1024; 835 PRAGMA journal_mode = WAL; 836 CREATE TABLE t1(x); 837 INSERT INTO t1 VALUES(zeroblob(8188*1020)); 838 CREATE TABLE t2(y); 839 PRAGMA wal_checkpoint; 840 } 841 execsql { 842 SELECT rootpage>=8192 FROM sqlite_master WHERE tbl_name = 't2'; 843 } 844} {1} 845do_test wal2-8.1.3 { 846 execsql { 847 PRAGMA cache_size = 10; 848 CREATE TABLE t3(z); 849 BEGIN; 850 INSERT INTO t3 VALUES(randomblob(900)); 851 INSERT INTO t3 SELECT randomblob(900) FROM t3; 852 INSERT INTO t2 VALUES('hello'); 853 INSERT INTO t3 SELECT randomblob(900) FROM t3; 854 INSERT INTO t3 SELECT randomblob(900) FROM t3; 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 ROLLBACK; 860 } 861 execsql { 862 INSERT INTO t2 VALUES('goodbye'); 863 INSERT INTO t3 SELECT randomblob(900) FROM t3; 864 INSERT INTO t3 SELECT randomblob(900) FROM t3; 865 } 866} {} 867do_test wal2-8.1.4 { 868 sqlite3 db2 test.db 869 execsql { SELECT * FROM t2 } 870} {goodbye} 871db2 close 872db close 873 874#------------------------------------------------------------------------- 875# Test that even if the checksums for both are valid, if the two copies 876# of the wal-index header in the wal-index do not match, the client 877# runs (or at least tries to run) database recovery. 878# 879# 880proc get_name {method args} { set ::filename [lindex $args 0] ; tvfs filter {} } 881testvfs tvfs 882tvfs script get_name 883tvfs filter xShmOpen 884 885forcedelete test.db test.db-wal test.db-journal 886do_test wal2-9.1 { 887 sqlite3 db test.db -vfs tvfs 888 execsql { 889 PRAGMA journal_mode = WAL; 890 CREATE TABLE x(y); 891 INSERT INTO x VALUES('Barton'); 892 INSERT INTO x VALUES('Deakin'); 893 } 894 895 # Set $wih(1) to the contents of the wal-index header after 896 # the frames associated with the first two rows in table 'x' have 897 # been inserted. Then insert one more row and set $wih(2) 898 # to the new value of the wal-index header. 899 # 900 # If the $wih(1) is written into the wal-index before running 901 # a read operation, the client will see only the first two rows. If 902 # $wih(2) is written into the wal-index, the client will see 903 # three rows. If an invalid header is written into the wal-index, then 904 # the client will run recovery and see three rows. 905 # 906 set wih(1) [set_tvfs_hdr $::filename] 907 execsql { INSERT INTO x VALUES('Watson') } 908 set wih(2) [set_tvfs_hdr $::filename] 909 910 sqlite3 db2 test.db -vfs tvfs 911 execsql { SELECT * FROM x } db2 912} {Barton Deakin Watson} 913 914foreach {tn hdr1 hdr2 res} [list \ 915 3 $wih(1) $wih(1) {Barton Deakin} \ 916 4 $wih(1) $wih(2) {Barton Deakin Watson} \ 917 5 $wih(2) $wih(1) {Barton Deakin Watson} \ 918 6 $wih(2) $wih(2) {Barton Deakin Watson} \ 919 7 $wih(1) $wih(1) {Barton Deakin} \ 920 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} 921] { 922 do_test wal2-9.$tn { 923 set_tvfs_hdr $::filename $hdr1 $hdr2 924 execsql { SELECT * FROM x } db2 925 } $res 926} 927 928db2 close 929db close 930 931#------------------------------------------------------------------------- 932# This block of tests - wal2-10.* - focus on the libraries response to 933# new versions of the wal or wal-index formats. 934# 935# wal2-10.1.*: Test that the library refuses to "recover" a new WAL 936# format. 937# 938# wal2-10.2.*: Test that the library refuses to read or write a database 939# if the wal-index version is newer than it understands. 940# 941# At time of writing, the only versions of the wal and wal-index formats 942# that exist are versions 3007000 (corresponding to SQLite version 3.7.0, 943# the first version of SQLite to feature wal mode). 944# 945do_test wal2-10.1.1 { 946 faultsim_delete_and_reopen 947 execsql { 948 PRAGMA journal_mode = WAL; 949 CREATE TABLE t1(a, b); 950 PRAGMA wal_checkpoint; 951 INSERT INTO t1 VALUES(1, 2); 952 INSERT INTO t1 VALUES(3, 4); 953 } 954 faultsim_save_and_close 955} {} 956do_test wal2-10.1.2 { 957 faultsim_restore_and_reopen 958 execsql { SELECT * FROM t1 } 959} {1 2 3 4} 960do_test wal2-10.1.3 { 961 faultsim_restore_and_reopen 962 set hdr [wal_set_walhdr test.db-wal] 963 lindex $hdr 1 964} {3007000} 965do_test wal2-10.1.4 { 966 lset hdr 1 3007001 967 wal_set_walhdr test.db-wal $hdr 968 catchsql { SELECT * FROM t1 } 969} {1 {unable to open database file}} 970 971testvfs tvfs -default 1 972do_test wal2-10.2.1 { 973 faultsim_restore_and_reopen 974 execsql { SELECT * FROM t1 } 975} {1 2 3 4} 976do_test wal2-10.2.2 { 977 set hdr [set_tvfs_hdr $::filename] 978 lindex $hdr 0 979} {3007000} 980do_test wal2-10.2.3 { 981 lset hdr 0 3007001 982 wal_fix_walindex_cksum hdr 983 set_tvfs_hdr $::filename $hdr 984 catchsql { SELECT * FROM t1 } 985} {1 {unable to open database file}} 986db close 987tvfs delete 988 989#------------------------------------------------------------------------- 990# This block of tests - wal2-11.* - tests that it is not possible to put 991# the library into an infinite loop by presenting it with a corrupt 992# hash table (one that appears to contain a single chain of infinite 993# length). 994# 995# wal2-11.1.*: While reading the hash-table. 996# 997# wal2-11.2.*: While writing the hash-table. 998# 999testvfs tvfs -default 1 1000do_test wal2-11.0 { 1001 faultsim_delete_and_reopen 1002 execsql { 1003 PRAGMA journal_mode = WAL; 1004 CREATE TABLE t1(a, b, c); 1005 INSERT INTO t1 VALUES(1, 2, 3); 1006 INSERT INTO t1 VALUES(4, 5, 6); 1007 INSERT INTO t1 VALUES(7, 8, 9); 1008 SELECT * FROM t1; 1009 } 1010} {wal 1 2 3 4 5 6 7 8 9} 1011 1012do_test wal2-11.1.1 { 1013 sqlite3 db2 test.db 1014 execsql { SELECT name FROM sqlite_master } db2 1015} {t1} 1016 1017if {$::tcl_version>=8.5} { 1018 # Set all zeroed slots in the first hash table to invalid values. 1019 # 1020 set blob [string range [tvfs shm $::filename] 0 16383] 1021 set I [string range [tvfs shm $::filename] 16384 end] 1022 binary scan $I t* L 1023 set I [list] 1024 foreach p $L { 1025 lappend I [expr $p ? $p : 400] 1026 } 1027 append blob [binary format t* $I] 1028 tvfs shm $::filename $blob 1029 do_test wal2-11.2 { 1030 catchsql { INSERT INTO t1 VALUES(10, 11, 12) } 1031 } {1 {database disk image is malformed}} 1032 1033 # Fill up the hash table on the first page of shared memory with 0x55 bytes. 1034 # 1035 set blob [string range [tvfs shm $::filename] 0 16383] 1036 append blob [string repeat [binary format c 55] 16384] 1037 tvfs shm $::filename $blob 1038 do_test wal2-11.3 { 1039 catchsql { SELECT * FROM t1 } db2 1040 } {1 {database disk image is malformed}} 1041} 1042 1043db close 1044db2 close 1045tvfs delete 1046 1047#------------------------------------------------------------------------- 1048# If a connection is required to create a WAL or SHM file, it creates 1049# the new files with the same file-system permissions as the database 1050# file itself. Test this. 1051# 1052if {$::tcl_platform(platform) == "unix"} { 1053 faultsim_delete_and_reopen 1054 # Changed on 2012-02-13: umask is deliberately ignored for -wal files. 1055 #set umask [exec /bin/sh -c umask] 1056 set umask 0 1057 1058 1059 do_test wal2-12.1 { 1060 sqlite3 db test.db 1061 execsql { 1062 CREATE TABLE tx(y, z); 1063 PRAGMA journal_mode = WAL; 1064 } 1065 db close 1066 list [file exists test.db-wal] [file exists test.db-shm] 1067 } {0 0} 1068 1069 foreach {tn permissions} { 1070 1 00644 1071 2 00666 1072 3 00600 1073 4 00755 1074 } { 1075 set effective [format %.5o [expr $permissions & ~$umask]] 1076 do_test wal2-12.2.$tn.1 { 1077 file attributes test.db -permissions $permissions 1078 file attributes test.db -permissions 1079 } $permissions 1080 do_test wal2-12.2.$tn.2 { 1081 list [file exists test.db-wal] [file exists test.db-shm] 1082 } {0 0} 1083 do_test wal2-12.2.$tn.3 { 1084 sqlite3 db test.db 1085 execsql { INSERT INTO tx DEFAULT VALUES } 1086 list [file exists test.db-wal] [file exists test.db-shm] 1087 } {1 1} 1088 do_test wal2-12.2.$tn.4 { 1089 list [file attr test.db-wal -perm] [file attr test.db-shm -perm] 1090 } [list $effective $effective] 1091 do_test wal2-12.2.$tn.5 { 1092 db close 1093 list [file exists test.db-wal] [file exists test.db-shm] 1094 } {0 0} 1095 } 1096} 1097 1098#------------------------------------------------------------------------- 1099# Test the libraries response to discovering that one or more of the 1100# database, wal or shm files cannot be opened, or can only be opened 1101# read-only. 1102# 1103if {$::tcl_platform(platform) == "unix"} { 1104 proc perm {} { 1105 set L [list] 1106 foreach f {test.db test.db-wal test.db-shm} { 1107 if {[file exists $f]} { 1108 lappend L [file attr $f -perm] 1109 } else { 1110 lappend L {} 1111 } 1112 } 1113 set L 1114 } 1115 1116 faultsim_delete_and_reopen 1117 execsql { 1118 PRAGMA journal_mode = WAL; 1119 CREATE TABLE t1(a, b); 1120 PRAGMA wal_checkpoint; 1121 INSERT INTO t1 VALUES('3.14', '2.72'); 1122 } 1123 do_test wal2-13.1.1 { 1124 list [file exists test.db-shm] [file exists test.db-wal] 1125 } {1 1} 1126 faultsim_save_and_close 1127 1128 foreach {tn db_perm wal_perm shm_perm can_open can_read can_write} { 1129 2 00644 00644 00644 1 1 1 1130 3 00644 00400 00644 1 1 0 1131 4 00644 00644 00400 1 0 0 1132 5 00400 00644 00644 1 1 0 1133 1134 7 00644 00000 00644 1 0 0 1135 8 00644 00644 00000 1 0 0 1136 9 00000 00644 00644 0 0 0 1137 } { 1138 faultsim_restore 1139 do_test wal2-13.$tn.1 { 1140 file attr test.db -perm $db_perm 1141 file attr test.db-wal -perm $wal_perm 1142 file attr test.db-shm -perm $shm_perm 1143 1144 set L [file attr test.db -perm] 1145 lappend L [file attr test.db-wal -perm] 1146 lappend L [file attr test.db-shm -perm] 1147 } [list $db_perm $wal_perm $shm_perm] 1148 1149 # If $can_open is true, then it should be possible to open a database 1150 # handle. Otherwise, if $can_open is 0, attempting to open the db 1151 # handle throws an "unable to open database file" exception. 1152 # 1153 set r(1) {0 ok} 1154 set r(0) {1 {unable to open database file}} 1155 do_test wal2-13.$tn.2 { 1156 list [catch {sqlite3 db test.db ; set {} ok} msg] $msg 1157 } $r($can_open) 1158 1159 if {$can_open} { 1160 1161 # If $can_read is true, then the client should be able to read from 1162 # the database file. If $can_read is false, attempting to read should 1163 # throw the "unable to open database file" exception. 1164 # 1165 set a(0) {1 {unable to open database file}} 1166 set a(1) {0 {3.14 2.72}} 1167 do_test wal2-13.$tn.3 { 1168 catchsql { SELECT * FROM t1 } 1169 } $a($can_read) 1170 1171 # Now try to write to the db file. If the client can read but not 1172 # write, then it should throw the familiar "unable to open db file" 1173 # exception. If it can read but not write, the exception should 1174 # be "attempt to write a read only database". 1175 # 1176 # If the client can read and write, the operation should succeed. 1177 # 1178 set b(0,0) {1 {unable to open database file}} 1179 set b(1,0) {1 {attempt to write a readonly database}} 1180 set b(1,1) {0 {}} 1181 do_test wal2-13.$tn.4 { 1182 catchsql { INSERT INTO t1 DEFAULT VALUES } 1183 } $b($can_read,$can_write) 1184 } 1185 catch { db close } 1186 } 1187} 1188 1189#------------------------------------------------------------------------- 1190# Test that "PRAGMA checkpoint_fullsync" appears to be working. 1191# 1192foreach {tn sql reslist} { 1193 1 { } {10 0 4 0 6 0} 1194 2 { PRAGMA checkpoint_fullfsync = 1 } {10 4 4 2 6 2} 1195 3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0} 1196} { 1197 faultsim_delete_and_reopen 1198 1199 execsql {PRAGMA auto_vacuum = 0} 1200 execsql $sql 1201 do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 } {} 1202 do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal} 1203 1204 set sqlite_sync_count 0 1205 set sqlite_fullsync_count 0 1206 1207 do_execsql_test wal2-14.$tn.2 { 1208 PRAGMA wal_autocheckpoint = 10; 1209 CREATE TABLE t1(a, b); -- 2 wal syncs 1210 INSERT INTO t1 VALUES(1, 2); -- 2 wal sync 1211 PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync 1212 BEGIN; 1213 INSERT INTO t1 VALUES(3, 4); 1214 INSERT INTO t1 VALUES(5, 6); 1215 COMMIT; -- 2 wal sync 1216 PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync 1217 } {10 0 3 3 0 1 1} 1218 1219 do_test wal2-14.$tn.3 { 1220 cond_incr_sync_count 1 1221 list $sqlite_sync_count $sqlite_fullsync_count 1222 } [lrange $reslist 0 1] 1223 1224 set sqlite_sync_count 0 1225 set sqlite_fullsync_count 0 1226 1227 do_test wal2-14.$tn.4 { 1228 execsql { INSERT INTO t1 VALUES(7, zeroblob(12*4096)) } 1229 list $sqlite_sync_count $sqlite_fullsync_count 1230 } [lrange $reslist 2 3] 1231 1232 set sqlite_sync_count 0 1233 set sqlite_fullsync_count 0 1234 1235 do_test wal2-14.$tn.5 { 1236 execsql { PRAGMA wal_autocheckpoint = 1000 } 1237 execsql { INSERT INTO t1 VALUES(9, 10) } 1238 execsql { INSERT INTO t1 VALUES(11, 12) } 1239 execsql { INSERT INTO t1 VALUES(13, 14) } 1240 db close 1241 list $sqlite_sync_count $sqlite_fullsync_count 1242 } [lrange $reslist 4 5] 1243} 1244 1245catch { db close } 1246 1247# PRAGMA checkpoint_fullsync 1248# PRAGMA fullfsync 1249# PRAGMA synchronous 1250# 1251foreach {tn settings restart_sync commit_sync ckpt_sync} { 1252 1 {0 0 off} {0 0} {0 0} {0 0} 1253 2 {0 0 normal} {1 0} {0 0} {2 0} 1254 3 {0 0 full} {2 0} {1 0} {2 0} 1255 1256 4 {0 1 off} {0 0} {0 0} {0 0} 1257 5 {0 1 normal} {0 1} {0 0} {0 2} 1258 6 {0 1 full} {0 2} {0 1} {0 2} 1259 1260 7 {1 0 off} {0 0} {0 0} {0 0} 1261 8 {1 0 normal} {1 0} {0 0} {0 2} 1262 9 {1 0 full} {2 0} {1 0} {0 2} 1263 1264 10 {1 1 off} {0 0} {0 0} {0 0} 1265 11 {1 1 normal} {0 1} {0 0} {0 2} 1266 12 {1 1 full} {0 2} {0 1} {0 2} 1267} { 1268 forcedelete test.db 1269 1270 testvfs tvfs -default 1 1271 tvfs filter xSync 1272 tvfs script xSyncCb 1273 proc xSyncCb {method file fileid flags} { 1274 incr ::sync($flags) 1275 } 1276 1277 sqlite3 db test.db 1278 do_execsql_test 15.$tn.1 " 1279 PRAGMA page_size = 4096; 1280 CREATE TABLE t1(x); 1281 PRAGMA wal_autocheckpoint = OFF; 1282 PRAGMA journal_mode = WAL; 1283 PRAGMA checkpoint_fullfsync = [lindex $settings 0]; 1284 PRAGMA fullfsync = [lindex $settings 1]; 1285 PRAGMA synchronous = [lindex $settings 2]; 1286 " {0 wal} 1287 1288 do_test 15.$tn.2 { 1289 set sync(normal) 0 1290 set sync(full) 0 1291 execsql { INSERT INTO t1 VALUES('abc') } 1292 list $::sync(normal) $::sync(full) 1293 } $restart_sync 1294 1295 do_test 15.$tn.3 { 1296 set sync(normal) 0 1297 set sync(full) 0 1298 execsql { INSERT INTO t1 VALUES('abc') } 1299 list $::sync(normal) $::sync(full) 1300 } $commit_sync 1301 1302 do_test 15.$tn.4 { 1303 set sync(normal) 0 1304 set sync(full) 0 1305 execsql { INSERT INTO t1 VALUES('def') } 1306 list $::sync(normal) $::sync(full) 1307 } $commit_sync 1308 1309 do_test 15.$tn.5 { 1310 set sync(normal) 0 1311 set sync(full) 0 1312 execsql { PRAGMA wal_checkpoint } 1313 list $::sync(normal) $::sync(full) 1314 } $ckpt_sync 1315 1316 db close 1317 tvfs delete 1318} 1319 1320 1321 1322finish_test 1323