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