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