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