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