1# 2010 June 15 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# 12 13set testdir [file dirname $argv0] 14source $testdir/tester.tcl 15source $testdir/lock_common.tcl 16source $testdir/malloc_common.tcl 17 18if {[permutation] == "inmemory_journal"} { 19 finish_test 20 return 21} 22 23if {$::tcl_platform(platform)=="windows"} { 24 finish_test 25 return 26} 27 28set a_string_counter 1 29proc a_string {n} { 30 global a_string_counter 31 incr a_string_counter 32 string range [string repeat "${a_string_counter}." $n] 1 $n 33} 34db func a_string a_string 35 36#------------------------------------------------------------------------- 37# Test fault-injection while rolling back a hot-journal file. 38# 39do_test pagerfault-1-pre1 { 40 execsql { 41 PRAGMA journal_mode = DELETE; 42 PRAGMA cache_size = 10; 43 CREATE TABLE t1(a UNIQUE, b UNIQUE); 44 INSERT INTO t1 VALUES(a_string(200), a_string(300)); 45 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 46 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 47 BEGIN; 48 INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; 49 INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; 50 INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; 51 INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; 52 } 53 faultsim_save_and_close 54} {} 55do_faultsim_test pagerfault-1 -prep { 56 faultsim_restore_and_reopen 57} -body { 58 execsql { SELECT count(*) FROM t1 } 59} -test { 60 faultsim_test_result {0 4} 61 faultsim_integrity_check 62 if {[db one { SELECT count(*) FROM t1 }] != 4} { 63 error "Database content appears incorrect" 64 } 65} 66 67#------------------------------------------------------------------------- 68# Test fault-injection while rolling back a hot-journal file with a 69# page-size different from the current value stored on page 1 of the 70# database file. 71# 72do_test pagerfault-2-pre1 { 73 testvfs tv -default 1 74 tv filter xSync 75 tv script xSyncCb 76 proc xSyncCb {filename args} { 77 if {[string match *journal filename]==0} faultsim_save 78 } 79 faultsim_delete_and_reopen 80 execsql { 81 PRAGMA page_size = 4096; 82 BEGIN; 83 CREATE TABLE abc(a, b, c); 84 INSERT INTO abc VALUES('o', 't', 't'); 85 INSERT INTO abc VALUES('f', 'f', 's'); 86 INSERT INTO abc SELECT * FROM abc; -- 4 87 INSERT INTO abc SELECT * FROM abc; -- 8 88 INSERT INTO abc SELECT * FROM abc; -- 16 89 INSERT INTO abc SELECT * FROM abc; -- 32 90 INSERT INTO abc SELECT * FROM abc; -- 64 91 INSERT INTO abc SELECT * FROM abc; -- 128 92 INSERT INTO abc SELECT * FROM abc; -- 256 93 COMMIT; 94 PRAGMA page_size = 1024; 95 VACUUM; 96 } 97 db close 98 tv delete 99} {} 100do_faultsim_test pagerfault-2 -prep { 101 faultsim_restore_and_reopen 102} -body { 103 execsql { SELECT * FROM abc } 104} -test { 105 set answer [split [string repeat "ottffs" 128] ""] 106 faultsim_test_result [list 0 $answer] 107 faultsim_integrity_check 108 set res [db eval { SELECT * FROM abc }] 109 if {$res != $answer} { error "Database content appears incorrect ($res)" } 110} 111 112#------------------------------------------------------------------------- 113# Test fault-injection while rolling back hot-journals that were created 114# as part of a multi-file transaction. 115# 116do_test pagerfault-3-pre1 { 117 testvfs tstvfs -default 1 118 tstvfs filter xDelete 119 tstvfs script xDeleteCallback 120 121 proc xDeleteCallback {method file args} { 122 set file [file tail $file] 123 if { [string match *mj* $file] } { faultsim_save } 124 } 125 126 faultsim_delete_and_reopen 127 db func a_string a_string 128 129 execsql { 130 ATTACH 'test.db2' AS aux; 131 PRAGMA journal_mode = DELETE; 132 PRAGMA main.cache_size = 10; 133 PRAGMA aux.cache_size = 10; 134 135 CREATE TABLE t1(a UNIQUE, b UNIQUE); 136 CREATE TABLE aux.t2(a UNIQUE, b UNIQUE); 137 INSERT INTO t1 VALUES(a_string(200), a_string(300)); 138 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 139 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 140 INSERT INTO t2 SELECT * FROM t1; 141 142 BEGIN; 143 INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; 144 INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; 145 INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; 146 INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; 147 REPLACE INTO t2 SELECT * FROM t1; 148 COMMIT; 149 } 150 151 db close 152 tstvfs delete 153} {} 154do_faultsim_test pagerfault-3 -prep { 155 faultsim_restore_and_reopen 156} -body { 157 execsql { 158 ATTACH 'test.db2' AS aux; 159 SELECT count(*) FROM t2; 160 SELECT count(*) FROM t1; 161 } 162} -test { 163 faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}} 164 faultsim_integrity_check 165 catchsql { ATTACH 'test.db2' AS aux } 166 if {[db one { SELECT count(*) FROM t1 }] != 4 167 || [db one { SELECT count(*) FROM t2 }] != 4 168 } { 169 error "Database content appears incorrect" 170 } 171} 172 173#------------------------------------------------------------------------- 174# Test fault-injection as part of a vanilla, no-transaction, INSERT 175# statement. 176# 177do_faultsim_test pagerfault-4 -prep { 178 faultsim_delete_and_reopen 179} -body { 180 execsql { 181 CREATE TABLE x(y); 182 INSERT INTO x VALUES('z'); 183 SELECT * FROM x; 184 } 185} -test { 186 faultsim_test_result {0 z} 187 faultsim_integrity_check 188} 189 190#------------------------------------------------------------------------- 191# Test fault-injection as part of a commit when using journal_mode=PERSIST. 192# Three different cases: 193# 194# pagerfault-5.1: With no journal_size_limit configured. 195# pagerfault-5.2: With a journal_size_limit configured. 196# pagerfault-5.4: Multi-file transaction. One connection has a 197# journal_size_limit of 0, the other has no limit. 198# 199do_test pagerfault-5-pre1 { 200 faultsim_delete_and_reopen 201 db func a_string a_string 202 execsql { 203 CREATE TABLE t1(a UNIQUE, b UNIQUE); 204 INSERT INTO t1 VALUES(a_string(200), a_string(300)); 205 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 206 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 207 } 208 faultsim_save_and_close 209} {} 210do_faultsim_test pagerfault-5.1 -prep { 211 faultsim_restore_and_reopen 212 db func a_string a_string 213 execsql { PRAGMA journal_mode = PERSIST } 214} -body { 215 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } 216} -test { 217 faultsim_test_result {0 {}} 218 faultsim_integrity_check 219} 220do_faultsim_test pagerfault-5.2 -prep { 221 faultsim_restore_and_reopen 222 db func a_string a_string 223 execsql { 224 PRAGMA journal_mode = PERSIST; 225 PRAGMA journal_size_limit = 2048; 226 } 227} -body { 228 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } 229} -test { 230 faultsim_test_result {0 {}} 231 faultsim_integrity_check 232} 233do_faultsim_test pagerfault-5.3 -faults oom-transient -prep { 234 faultsim_restore_and_reopen 235 db func a_string a_string 236 forcedelete test2.db test2.db-journal test2.db-wal 237 execsql { 238 PRAGMA journal_mode = PERSIST; 239 ATTACH 'test2.db' AS aux; 240 PRAGMA aux.journal_mode = PERSIST; 241 PRAGMA aux.journal_size_limit = 0; 242 } 243} -body { 244 execsql { 245 BEGIN; 246 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 247 CREATE TABLE aux.t2 AS SELECT * FROM t1; 248 COMMIT; 249 } 250} -test { 251 faultsim_test_result {0 {}} 252 253 catchsql { COMMIT } 254 catchsql { ROLLBACK } 255 256 faultsim_integrity_check 257 set res "" 258 set rc [catch { set res [db one { PRAGMA aux.integrity_check }] }] 259 if {$rc!=0 || $res != "ok"} {error "integrity-check problem:$rc $res"} 260} 261 262#------------------------------------------------------------------------- 263# Test fault-injection as part of a commit when using 264# journal_mode=TRUNCATE. 265# 266do_test pagerfault-6-pre1 { 267 faultsim_delete_and_reopen 268 db func a_string a_string 269 execsql { 270 CREATE TABLE t1(a UNIQUE, b UNIQUE); 271 INSERT INTO t1 VALUES(a_string(200), a_string(300)); 272 } 273 faultsim_save_and_close 274} {} 275 276do_faultsim_test pagerfault-6.1 -prep { 277 faultsim_restore_and_reopen 278 db func a_string a_string 279 execsql { PRAGMA journal_mode = TRUNCATE } 280} -body { 281 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } 282 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } 283} -test { 284 faultsim_test_result {0 {}} 285 faultsim_integrity_check 286} 287 288# The unix vfs xAccess() method considers a file zero bytes in size to 289# "not exist". This proc overrides that behaviour so that a zero length 290# file is considered to exist. 291# 292proc xAccess {method filename op args} { 293 if {$op != "SQLITE_ACCESS_EXISTS"} { return "" } 294 return [file exists $filename] 295} 296do_faultsim_test pagerfault-6.2 -faults cantopen-* -prep { 297 shmfault filter xAccess 298 shmfault script xAccess 299 300 faultsim_restore_and_reopen 301 db func a_string a_string 302 execsql { PRAGMA journal_mode = TRUNCATE } 303} -body { 304 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } 305 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } 306} -test { 307 faultsim_test_result {0 {}} 308 faultsim_integrity_check 309} 310 311# The following was an attempt to get a bitvec malloc to fail. Didn't work. 312# 313# do_test pagerfault-6-pre1 { 314# faultsim_delete_and_reopen 315# execsql { 316# CREATE TABLE t1(x, y, UNIQUE(x, y)); 317# INSERT INTO t1 VALUES(1, randomblob(1501)); 318# INSERT INTO t1 VALUES(2, randomblob(1502)); 319# INSERT INTO t1 VALUES(3, randomblob(1503)); 320# INSERT INTO t1 VALUES(4, randomblob(1504)); 321# INSERT INTO t1 322# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1; 323# INSERT INTO t1 324# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1; 325# INSERT INTO t1 326# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1; 327# INSERT INTO t1 328# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1; 329# } 330# faultsim_save_and_close 331# } {} 332# do_faultsim_test pagerfault-6 -prep { 333# faultsim_restore_and_reopen 334# } -body { 335# execsql { 336# BEGIN; 337# UPDATE t1 SET x=x+4 WHERE x=1; 338# SAVEPOINT one; 339# UPDATE t1 SET x=x+4 WHERE x=2; 340# SAVEPOINT three; 341# UPDATE t1 SET x=x+4 WHERE x=3; 342# SAVEPOINT four; 343# UPDATE t1 SET x=x+4 WHERE x=4; 344# RELEASE three; 345# COMMIT; 346# SELECT DISTINCT x FROM t1; 347# } 348# } -test { 349# faultsim_test_result {0 {5 6 7 8}} 350# faultsim_integrity_check 351# } 352# 353 354# This is designed to provoke a special case in the pager code: 355# 356# If an error (specifically, a FULL or IOERR error) occurs while writing a 357# dirty page to the file-system in order to free up memory, the pager enters 358# the "error state". An IO error causes SQLite to roll back the current 359# transaction (exiting the error state). A FULL error, however, may only 360# rollback the current statement. 361# 362# This block tests that nothing goes wrong if a FULL error occurs while 363# writing a dirty page out to free memory from within a statement that has 364# opened a statement transaction. 365# 366do_test pagerfault-7-pre1 { 367 faultsim_delete_and_reopen 368 execsql { 369 CREATE TABLE t2(a INTEGER PRIMARY KEY, b); 370 BEGIN; 371 INSERT INTO t2 VALUES(NULL, randomblob(1500)); 372 INSERT INTO t2 VALUES(NULL, randomblob(1500)); 373 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 4 374 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 8 375 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 16 376 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 32 377 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 64 378 COMMIT; 379 CREATE TABLE t1(a PRIMARY KEY, b); 380 INSERT INTO t1 SELECT * FROM t2; 381 DROP TABLE t2; 382 } 383 faultsim_save_and_close 384} {} 385do_faultsim_test pagerfault-7 -prep { 386 faultsim_restore_and_reopen 387 execsql { 388 PRAGMA cache_size = 10; 389 BEGIN; 390 UPDATE t1 SET b = randomblob(1500); 391 } 392} -body { 393 execsql { UPDATE t1 SET a = 65, b = randomblob(1500) WHERE (a+1)>200 } 394 execsql COMMIT 395} -test { 396 faultsim_test_result {0 {}} 397 faultsim_integrity_check 398} 399 400do_test pagerfault-8-pre1 { 401 faultsim_delete_and_reopen 402 execsql { 403 PRAGMA auto_vacuum = 1; 404 CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 405 BEGIN; 406 INSERT INTO t1 VALUES(NULL, randomblob(1500)); 407 INSERT INTO t1 VALUES(NULL, randomblob(1500)); 408 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 4 409 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 8 410 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 16 411 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 32 412 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 64 413 COMMIT; 414 } 415 faultsim_save_and_close 416 set filesize [file size test.db] 417 set {} {} 418} {} 419do_test pagerfault-8-pre2 { 420 faultsim_restore_and_reopen 421 execsql { DELETE FROM t1 WHERE a>32 } 422 expr {[file size test.db] < $filesize} 423} {1} 424do_faultsim_test pagerfault-8 -prep { 425 faultsim_restore_and_reopen 426 execsql { 427 BEGIN; 428 DELETE FROM t1 WHERE a>32; 429 } 430} -body { 431 execsql COMMIT 432} -test { 433 faultsim_test_result {0 {}} 434 faultsim_integrity_check 435} 436 437#------------------------------------------------------------------------- 438# This test case is specially designed so that during a savepoint 439# rollback, a new cache entry must be allocated (see comments surrounding 440# the call to sqlite3PagerAcquire() from within pager_playback_one_page() 441# for details). Test the effects of injecting an OOM at this point. 442# 443do_test pagerfault-9-pre1 { 444 faultsim_delete_and_reopen 445 execsql { 446 PRAGMA auto_vacuum = incremental; 447 CREATE TABLE t1(x); 448 CREATE TABLE t2(y); 449 CREATE TABLE t3(z); 450 451 INSERT INTO t1 VALUES(randomblob(900)); 452 INSERT INTO t1 VALUES(randomblob(900)); 453 DELETE FROM t1; 454 } 455 faultsim_save_and_close 456} {} 457do_faultsim_test pagerfault-9.1 -prep { 458 faultsim_restore_and_reopen 459 execsql { 460 BEGIN; 461 INSERT INTO t1 VALUES(randomblob(900)); 462 INSERT INTO t1 VALUES(randomblob(900)); 463 DROP TABLE t3; 464 DROP TABLE t2; 465 SAVEPOINT abc; 466 PRAGMA incremental_vacuum; 467 } 468} -body { 469 execsql { 470 ROLLBACK TO abc; 471 COMMIT; 472 PRAGMA freelist_count 473 } 474} -test { 475 faultsim_test_result {0 2} 476 faultsim_integrity_check 477 478 set sl [db one { SELECT COALESCE(sum(length(x)), 'null') FROM t1 }] 479 if {$sl!="null" && $sl!=1800} { 480 error "Content looks no good... ($sl)" 481 } 482} 483 484#------------------------------------------------------------------------- 485# Test fault injection with a temporary database file. 486# 487foreach v {a b} { 488 do_faultsim_test pagerfault-10$v -prep { 489 sqlite3 db "" 490 db func a_string a_string; 491 execsql { 492 PRAGMA cache_size = 10; 493 BEGIN; 494 CREATE TABLE xx(a, b, UNIQUE(a, b)); 495 INSERT INTO xx VALUES(a_string(200), a_string(200)); 496 INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx; 497 INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx; 498 INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx; 499 INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx; 500 COMMIT; 501 } 502 } -body { 503 execsql { UPDATE xx SET a = a_string(300) } 504 } -test { 505 faultsim_test_result {0 {}} 506 if {$::v == "b"} { execsql { PRAGMA journal_mode = TRUNCATE } } 507 faultsim_integrity_check 508 faultsim_integrity_check 509 } 510} 511 512#------------------------------------------------------------------------- 513# Test fault injection with transaction savepoints (savepoints created 514# when a SAVEPOINT command is executed outside of any other savepoint 515# or transaction context). 516# 517do_test pagerfault-9-pre1 { 518 faultsim_delete_and_reopen 519 db func a_string a_string; 520 execsql { 521 PRAGMA auto_vacuum = on; 522 CREATE TABLE t1(x UNIQUE); 523 CREATE TABLE t2(y UNIQUE); 524 CREATE TABLE t3(z UNIQUE); 525 BEGIN; 526 INSERT INTO t1 VALUES(a_string(202)); 527 INSERT INTO t2 VALUES(a_string(203)); 528 INSERT INTO t3 VALUES(a_string(204)); 529 INSERT INTO t1 SELECT a_string(202) FROM t1; 530 INSERT INTO t1 SELECT a_string(203) FROM t1; 531 INSERT INTO t1 SELECT a_string(204) FROM t1; 532 INSERT INTO t1 SELECT a_string(205) FROM t1; 533 INSERT INTO t2 SELECT a_string(length(x)) FROM t1; 534 INSERT INTO t3 SELECT a_string(length(x)) FROM t1; 535 COMMIT; 536 } 537 faultsim_save_and_close 538} {} 539do_faultsim_test pagerfault-11 -prep { 540 faultsim_restore_and_reopen 541 execsql { PRAGMA cache_size = 10 } 542} -body { 543 execsql { 544 SAVEPOINT trans; 545 UPDATE t2 SET y = y||'2'; 546 INSERT INTO t3 SELECT * FROM t2; 547 DELETE FROM t1; 548 ROLLBACK TO trans; 549 UPDATE t1 SET x = x||'3'; 550 INSERT INTO t2 SELECT * FROM t1; 551 DELETE FROM t3; 552 RELEASE trans; 553 } 554} -test { 555 faultsim_test_result {0 {}} 556 faultsim_integrity_check 557} 558 559 560#------------------------------------------------------------------------- 561# Test fault injection when writing to a database file that resides on 562# a file-system with a sector-size larger than the database page-size. 563# 564do_test pagerfault-12-pre1 { 565 testvfs ss_layer -default 1 566 ss_layer sectorsize 4096 567 faultsim_delete_and_reopen 568 db func a_string a_string; 569 570 execsql { 571 PRAGMA page_size = 1024; 572 PRAGMA journal_mode = PERSIST; 573 PRAGMA cache_size = 10; 574 BEGIN; 575 CREATE TABLE t1(x, y UNIQUE); 576 INSERT INTO t1 VALUES(a_string(333), a_string(444)); 577 INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1; 578 INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1; 579 INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1; 580 INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1; 581 INSERT INTO t1 SELECT a_string(44), a_string(55) FROM t1 LIMIT 13; 582 COMMIT; 583 } 584 faultsim_save_and_close 585} {} 586 587do_faultsim_test pagerfault-12a -prep { 588 faultsim_restore_and_reopen 589 execsql { PRAGMA cache_size = 10 } 590 db func a_string a_string; 591} -body { 592 execsql { 593 UPDATE t1 SET x = a_string(length(x)), y = a_string(length(y)); 594 } 595} -test { 596 faultsim_test_result {0 {}} 597 faultsim_integrity_check 598} 599 600do_test pagerfault-12-pre2 { 601 faultsim_restore_and_reopen 602 execsql { 603 CREATE TABLE t2 AS SELECT * FROM t1 LIMIT 10; 604 } 605 faultsim_save_and_close 606} {} 607do_faultsim_test pagerfault-12b -prep { 608 faultsim_restore_and_reopen 609 db func a_string a_string; 610 execsql { SELECT * FROM t1 } 611} -body { 612 set sql(1) { UPDATE t2 SET x = a_string(280) } 613 set sql(2) { UPDATE t1 SET x = a_string(280) WHERE rowid = 5 } 614 615 db eval { SELECT rowid FROM t1 LIMIT 2 } { db eval $sql($rowid) } 616 617} -test { 618 faultsim_test_result {0 {}} 619 faultsim_integrity_check 620} 621 622catch { db close } 623ss_layer delete 624 625 626#------------------------------------------------------------------------- 627# Test fault injection when SQLite opens a database where the size of the 628# database file is zero bytes but the accompanying journal file is larger 629# than that. In this scenario SQLite should delete the journal file 630# without rolling it back, even if it is in all other respects a valid 631# hot-journal file. 632# 633do_test pagerfault-13-pre1 { 634 faultsim_delete_and_reopen 635 db func a_string a_string; 636 execsql { 637 PRAGMA journal_mode = PERSIST; 638 BEGIN; 639 CREATE TABLE t1(x, y UNIQUE); 640 INSERT INTO t1 VALUES(a_string(333), a_string(444)); 641 COMMIT; 642 } 643 db close 644 forcedelete test.db 645 faultsim_save 646} {} 647do_faultsim_test pagerfault-13 -prep { 648 faultsim_restore_and_reopen 649} -body { 650 execsql { CREATE TABLE xx(a, b) } 651} -test { 652 faultsim_test_result {0 {}} 653} 654 655#--------------------------------------------------------------------------- 656# Test fault injection into a small backup operation. 657# 658do_test pagerfault-14-pre1 { 659 faultsim_delete_and_reopen 660 db func a_string a_string; 661 execsql { 662 PRAGMA journal_mode = PERSIST; 663 ATTACH 'test.db2' AS two; 664 BEGIN; 665 CREATE TABLE t1(x, y UNIQUE); 666 CREATE TABLE two.t2(x, y UNIQUE); 667 INSERT INTO t1 VALUES(a_string(333), a_string(444)); 668 INSERT INTO t2 VALUES(a_string(333), a_string(444)); 669 COMMIT; 670 } 671 faultsim_save_and_close 672} {} 673 674do_faultsim_test pagerfault-14a -prep { 675 faultsim_restore_and_reopen 676} -body { 677 if {[catch {db backup test.db2} msg]} { error [regsub {.*: } $msg {}] } 678} -test { 679 faultsim_test_result {0 {}} {1 {}} {1 {SQL logic error or missing database}} 680} 681 682# If TEMP_STORE is 2 or greater, then the database [db2] will be created 683# as an in-memory database. This test will not work in that case, as it 684# is not possible to change the page-size of an in-memory database. Even 685# using the backup API. 686# 687if {$TEMP_STORE<2} { 688 do_faultsim_test pagerfault-14b -prep { 689 catch { db2 close } 690 faultsim_restore_and_reopen 691 sqlite3 db2 "" 692 db2 eval { PRAGMA page_size = 4096; CREATE TABLE xx(a) } 693 } -body { 694 sqlite3_backup B db2 main db main 695 B step 200 696 set rc [B finish] 697 if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR} 698 if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] } 699 set {} {} 700 } -test { 701 faultsim_test_result {0 {}} {1 {sqlite3_backup_init() failed}} 702 } 703} 704 705do_faultsim_test pagerfault-14c -prep { 706 catch { db2 close } 707 faultsim_restore_and_reopen 708 sqlite3 db2 test.db2 709 db2 eval { 710 PRAGMA synchronous = off; 711 PRAGMA page_size = 4096; 712 CREATE TABLE xx(a); 713 } 714} -body { 715 sqlite3_backup B db2 main db main 716 B step 200 717 set rc [B finish] 718 if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR} 719 if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] } 720 set {} {} 721} -test { 722 faultsim_test_result {0 {}} {1 {sqlite3_backup_init() failed}} 723} 724 725do_test pagerfault-15-pre1 { 726 faultsim_delete_and_reopen 727 db func a_string a_string; 728 execsql { 729 BEGIN; 730 CREATE TABLE t1(x, y UNIQUE); 731 INSERT INTO t1 VALUES(a_string(11), a_string(22)); 732 INSERT INTO t1 VALUES(a_string(11), a_string(22)); 733 COMMIT; 734 } 735 faultsim_save_and_close 736} {} 737do_faultsim_test pagerfault-15 -prep { 738 faultsim_restore_and_reopen 739 db func a_string a_string; 740} -body { 741 db eval { SELECT * FROM t1 LIMIT 1 } { 742 execsql { 743 BEGIN; INSERT INTO t1 VALUES(a_string(333), a_string(555)); COMMIT; 744 BEGIN; INSERT INTO t1 VALUES(a_string(333), a_string(555)); COMMIT; 745 } 746 } 747} -test { 748 faultsim_test_result {0 {}} 749 faultsim_integrity_check 750} 751 752 753do_test pagerfault-16-pre1 { 754 faultsim_delete_and_reopen 755 execsql { CREATE TABLE t1(x, y UNIQUE) } 756 faultsim_save_and_close 757} {} 758do_faultsim_test pagerfault-16 -prep { 759 faultsim_restore_and_reopen 760} -body { 761 execsql { 762 PRAGMA locking_mode = exclusive; 763 PRAGMA journal_mode = wal; 764 INSERT INTO t1 VALUES(1, 2); 765 INSERT INTO t1 VALUES(3, 4); 766 PRAGMA journal_mode = delete; 767 INSERT INTO t1 VALUES(4, 5); 768 PRAGMA journal_mode = wal; 769 INSERT INTO t1 VALUES(6, 7); 770 PRAGMA journal_mode = persist; 771 INSERT INTO t1 VALUES(8, 9); 772 } 773} -test { 774 faultsim_test_result {0 {exclusive wal delete wal persist}} 775 faultsim_integrity_check 776} 777 778 779#------------------------------------------------------------------------- 780# Test fault injection while changing into and out of WAL mode. 781# 782do_test pagerfault-17-pre1 { 783 faultsim_delete_and_reopen 784 execsql { 785 CREATE TABLE t1(a PRIMARY KEY, b); 786 INSERT INTO t1 VALUES(1862, 'Botha'); 787 INSERT INTO t1 VALUES(1870, 'Smuts'); 788 INSERT INTO t1 VALUES(1866, 'Hertzog'); 789 } 790 faultsim_save_and_close 791} {} 792do_faultsim_test pagerfault-17a -prep { 793 faultsim_restore_and_reopen 794} -body { 795 execsql { 796 PRAGMA journal_mode = wal; 797 PRAGMA journal_mode = delete; 798 } 799} -test { 800 faultsim_test_result {0 {wal delete}} 801 faultsim_integrity_check 802} 803do_faultsim_test pagerfault-17b -prep { 804 faultsim_restore_and_reopen 805 execsql { PRAGMA synchronous = OFF } 806} -body { 807 execsql { 808 PRAGMA journal_mode = wal; 809 INSERT INTO t1 VALUES(22, 'Clarke'); 810 PRAGMA journal_mode = delete; 811 } 812} -test { 813 faultsim_test_result {0 {wal delete}} 814 faultsim_integrity_check 815} 816do_faultsim_test pagerfault-17c -prep { 817 faultsim_restore_and_reopen 818 execsql { 819 PRAGMA locking_mode = exclusive; 820 PRAGMA journal_mode = wal; 821 } 822} -body { 823 execsql { PRAGMA journal_mode = delete } 824} -test { 825 faultsim_test_result {0 delete} 826 faultsim_integrity_check 827} 828do_faultsim_test pagerfault-17d -prep { 829 catch { db2 close } 830 faultsim_restore_and_reopen 831 sqlite3 db2 test.db 832 execsql { PRAGMA journal_mode = delete } 833 execsql { PRAGMA journal_mode = wal } 834 execsql { INSERT INTO t1 VALUES(99, 'Bradman') } db2 835} -body { 836 execsql { PRAGMA journal_mode = delete } 837} -test { 838 faultsim_test_result {1 {database is locked}} 839 faultsim_integrity_check 840} 841do_faultsim_test pagerfault-17e -prep { 842 catch { db2 close } 843 faultsim_restore_and_reopen 844 sqlite3 db2 test.db 845 execsql { PRAGMA journal_mode = delete } 846 execsql { PRAGMA journal_mode = wal } 847 set ::chan [launch_testfixture] 848 testfixture $::chan { 849 sqlite3 db test.db 850 db eval { INSERT INTO t1 VALUES(101, 'Latham') } 851 } 852 catch { testfixture $::chan sqlite_abort } 853 catch { close $::chan } 854} -body { 855 execsql { PRAGMA journal_mode = delete } 856} -test { 857 faultsim_test_result {0 delete} 858 faultsim_integrity_check 859} 860 861#------------------------------------------------------------------------- 862# Test fault-injection when changing from journal_mode=persist to 863# journal_mode=delete (this involves deleting the journal file). 864# 865do_test pagerfault-18-pre1 { 866 faultsim_delete_and_reopen 867 execsql { 868 CREATE TABLE qq(x); 869 INSERT INTO qq VALUES('Herbert'); 870 INSERT INTO qq VALUES('Macalister'); 871 INSERT INTO qq VALUES('Mackenzie'); 872 INSERT INTO qq VALUES('Lilley'); 873 INSERT INTO qq VALUES('Palmer'); 874 } 875 faultsim_save_and_close 876} {} 877do_faultsim_test pagerfault-18 -prep { 878 faultsim_restore_and_reopen 879 execsql { 880 PRAGMA journal_mode = PERSIST; 881 INSERT INTO qq VALUES('Beatty'); 882 } 883} -body { 884 execsql { PRAGMA journal_mode = delete } 885} -test { 886 faultsim_test_result {0 delete} 887 faultsim_integrity_check 888} 889 890do_faultsim_test pagerfault-19a -prep { 891 sqlite3 db :memory: 892 db func a_string a_string 893 execsql { 894 PRAGMA auto_vacuum = FULL; 895 BEGIN; 896 CREATE TABLE t1(a, b); 897 INSERT INTO t1 VALUES(a_string(5000), a_string(6000)); 898 COMMIT; 899 } 900} -body { 901 execsql { 902 CREATE TABLE t2(a, b); 903 INSERT INTO t2 SELECT * FROM t1; 904 DELETE FROM t1; 905 } 906} -test { 907 faultsim_test_result {0 {}} 908} 909 910do_test pagerfault-19-pre1 { 911 faultsim_delete_and_reopen 912 execsql { 913 PRAGMA auto_vacuum = FULL; 914 CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); 915 CREATE TABLE t2(x); INSERT INTO t2 VALUES(2); 916 CREATE TABLE t3(x); INSERT INTO t3 VALUES(3); 917 CREATE TABLE t4(x); INSERT INTO t4 VALUES(4); 918 CREATE TABLE t5(x); INSERT INTO t5 VALUES(5); 919 CREATE TABLE t6(x); INSERT INTO t6 VALUES(6); 920 } 921 faultsim_save_and_close 922} {} 923do_faultsim_test pagerfault-19b -prep { 924 faultsim_restore_and_reopen 925} -body { 926 execsql { 927 BEGIN; 928 UPDATE t4 SET x = x+1; 929 UPDATE t6 SET x = x+1; 930 SAVEPOINT one; 931 UPDATE t3 SET x = x+1; 932 SAVEPOINT two; 933 DROP TABLE t2; 934 ROLLBACK TO one; 935 COMMIT; 936 SELECT * FROM t3; 937 SELECT * FROM t4; 938 SELECT * FROM t6; 939 } 940} -test { 941 faultsim_test_result {0 {3 5 7}} 942} 943 944#------------------------------------------------------------------------- 945# This tests fault-injection in a special case in the auto-vacuum code. 946# 947do_test pagerfault-20-pre1 { 948 faultsim_delete_and_reopen 949 execsql { 950 PRAGMA cache_size = 10; 951 PRAGMA auto_vacuum = FULL; 952 CREATE TABLE t0(a, b); 953 } 954 faultsim_save_and_close 955} {} 956do_faultsim_test pagerfault-20 -prep { 957 faultsim_restore_and_reopen 958} -body { 959 execsql { 960 BEGIN; 961 CREATE TABLE t1(a, b); 962 CREATE TABLE t2(a, b); 963 DROP TABLE t1; 964 COMMIT; 965 } 966} -test { 967 faultsim_test_result {0 {}} 968} 969 970do_test pagerfault-21-pre1 { 971 faultsim_delete_and_reopen 972 execsql { 973 PRAGMA cache_size = 10; 974 CREATE TABLE t0(a PRIMARY KEY, b); 975 INSERT INTO t0 VALUES(1, 2); 976 } 977 faultsim_save_and_close 978} {} 979do_faultsim_test pagerfault-21 -prep { 980 faultsim_restore_and_reopen 981} -body { 982 db eval { SELECT * FROM t0 LIMIT 1 } { 983 db eval { INSERT INTO t0 SELECT a+1, b FROM t0 } 984 db eval { INSERT INTO t0 SELECT a+2, b FROM t0 } 985 } 986} -test { 987 faultsim_test_result {0 {}} 988} 989 990 991#------------------------------------------------------------------------- 992# Test fault-injection and rollback when the nReserve header value 993# is non-zero. 994# 995do_test pagerfault-21-pre1 { 996 faultsim_delete_and_reopen 997 execsql { 998 PRAGMA page_size = 1024; 999 PRAGMA journal_mode = WAL; 1000 PRAGMA journal_mode = DELETE; 1001 } 1002 db close 1003 hexio_write test.db 20 10 1004 hexio_write test.db 105 03F0 1005 sqlite3 db test.db 1006 db func a_string a_string 1007 execsql { 1008 CREATE TABLE t0(a PRIMARY KEY, b UNIQUE); 1009 INSERT INTO t0 VALUES(a_string(222), a_string(333)); 1010 INSERT INTO t0 VALUES(a_string(223), a_string(334)); 1011 INSERT INTO t0 VALUES(a_string(224), a_string(335)); 1012 INSERT INTO t0 VALUES(a_string(225), a_string(336)); 1013 } 1014 faultsim_save_and_close 1015} {} 1016 1017do_faultsim_test pagerfault-21 -prep { 1018 faultsim_restore_and_reopen 1019} -body { 1020 execsql { INSERT INTO t0 SELECT a||'x', b||'x' FROM t0 } 1021} -test { 1022 faultsim_test_result {0 {}} 1023 faultsim_integrity_check 1024} 1025ifcapable crashtest { 1026 faultsim_delete_and_reopen 1027 execsql { 1028 PRAGMA page_size = 1024; 1029 PRAGMA journal_mode = WAL; 1030 PRAGMA journal_mode = DELETE; 1031 } 1032 db close 1033 hexio_write test.db 20 10 1034 hexio_write test.db 105 03F0 1035 1036 sqlite3 db test.db 1037 db func a_string a_string 1038 execsql { 1039 CREATE TABLE t0(a PRIMARY KEY, b UNIQUE); 1040 INSERT INTO t0 VALUES(a_string(222), a_string(333)); 1041 INSERT INTO t0 VALUES(a_string(223), a_string(334)); 1042 } 1043 faultsim_save_and_close 1044 1045 for {set iTest 1} {$iTest<50} {incr iTest} { 1046 do_test pagerfault-21.crash.$iTest.1 { 1047 crashsql -delay 1 -file test.db -seed $iTest { 1048 BEGIN; 1049 CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); 1050 INSERT INTO t1 SELECT a, b FROM t0; 1051 COMMIT; 1052 } 1053 } {1 {child process exited abnormally}} 1054 do_test pagerfault-22.$iTest.2 { 1055 sqlite3 db test.db 1056 execsql { PRAGMA integrity_check } 1057 } {ok} 1058 db close 1059 } 1060} 1061 1062 1063#------------------------------------------------------------------------- 1064# When a 3.7.0 client opens a write-transaction on a database file that 1065# has been appended to or truncated by a pre-370 client, it updates 1066# the db-size in the file header immediately. This test case provokes 1067# errors during that operation. 1068# 1069do_test pagerfault-22-pre1 { 1070 faultsim_delete_and_reopen 1071 db func a_string a_string 1072 execsql { 1073 PRAGMA page_size = 1024; 1074 PRAGMA auto_vacuum = 0; 1075 CREATE TABLE t1(a); 1076 CREATE INDEX i1 ON t1(a); 1077 INSERT INTO t1 VALUES(a_string(3000)); 1078 CREATE TABLE t2(a); 1079 INSERT INTO t2 VALUES(1); 1080 } 1081 db close 1082 sql36231 { INSERT INTO t1 VALUES(a_string(3000)) } 1083 faultsim_save_and_close 1084} {} 1085do_faultsim_test pagerfault-22 -prep { 1086 faultsim_restore_and_reopen 1087} -body { 1088 execsql { INSERT INTO t2 VALUES(2) } 1089 execsql { SELECT * FROM t2 } 1090} -test { 1091 faultsim_test_result {0 {1 2}} 1092 faultsim_integrity_check 1093} 1094 1095#------------------------------------------------------------------------- 1096# Provoke an OOM error during a commit of multi-file transaction. One of 1097# the databases written during the transaction is an in-memory database. 1098# This test causes rollback of the in-memory database after CommitPhaseOne() 1099# has successfully returned. i.e. the series of calls for the aborted commit 1100# is: 1101# 1102# PagerCommitPhaseOne(<in-memory-db>) -> SQLITE_OK 1103# PagerCommitPhaseOne(<file-db>) -> SQLITE_IOERR 1104# PagerRollback(<in-memory-db>) 1105# PagerRollback(<file-db>) 1106# 1107do_faultsim_test pagerfault-23 -prep { 1108 sqlite3 db :memory: 1109 foreach f [glob -nocomplain test.db*] { forcedelete $f } 1110 db eval { 1111 ATTACH 'test.db2' AS aux; 1112 CREATE TABLE t1(a, b); 1113 CREATE TABLE aux.t2(a, b); 1114 } 1115} -body { 1116 execsql { 1117 BEGIN; 1118 INSERT INTO t1 VALUES(1,2); 1119 INSERT INTO t2 VALUES(3,4); 1120 COMMIT; 1121 } 1122} -test { 1123 faultsim_test_result {0 {}} 1124 faultsim_integrity_check 1125} 1126 1127do_faultsim_test pagerfault-24 -prep { 1128 faultsim_delete_and_reopen 1129 db eval { PRAGMA temp_store = file } 1130 execsql { CREATE TABLE x(a, b) } 1131} -body { 1132 execsql { CREATE TEMP TABLE t1(a, b) } 1133} -test { 1134 faultsim_test_result {0 {}} \ 1135 {1 {unable to open a temporary database file for storing temporary tables}} 1136 set ic [db eval { PRAGMA temp.integrity_check }] 1137 if {$ic != "ok"} { error "Integrity check: $ic" } 1138} 1139 1140proc lockrows {n} { 1141 if {$n==0} { return "" } 1142 db eval { SELECT * FROM t1 WHERE oid = $n } { 1143 return [lockrows [expr {$n-1}]] 1144 } 1145} 1146 1147 1148do_test pagerfault-25-pre1 { 1149 faultsim_delete_and_reopen 1150 db func a_string a_string 1151 execsql { 1152 PRAGMA page_size = 1024; 1153 PRAGMA auto_vacuum = 0; 1154 CREATE TABLE t1(a); 1155 INSERT INTO t1 VALUES(a_string(500)); 1156 INSERT INTO t1 SELECT a_string(500) FROM t1; 1157 INSERT INTO t1 SELECT a_string(500) FROM t1; 1158 INSERT INTO t1 SELECT a_string(500) FROM t1; 1159 INSERT INTO t1 SELECT a_string(500) FROM t1; 1160 INSERT INTO t1 SELECT a_string(500) FROM t1; 1161 } 1162 faultsim_save_and_close 1163} {} 1164do_faultsim_test pagerfault-25 -prep { 1165 faultsim_restore_and_reopen 1166 db func a_string a_string 1167 set ::channel [db incrblob -readonly t1 a 1] 1168 execsql { 1169 PRAGMA cache_size = 10; 1170 BEGIN; 1171 INSERT INTO t1 VALUES(a_string(3000)); 1172 INSERT INTO t1 VALUES(a_string(3000)); 1173 } 1174} -body { 1175 lockrows 30 1176} -test { 1177 catch { lockrows 30 } 1178 catch { db eval COMMIT } 1179 close $::channel 1180 faultsim_test_result {0 {}} 1181} 1182 1183do_faultsim_test pagerfault-26 -prep { 1184 faultsim_delete_and_reopen 1185 execsql { 1186 PRAGMA page_size = 1024; 1187 PRAGMA journal_mode = truncate; 1188 PRAGMA auto_vacuum = full; 1189 PRAGMA locking_mode=exclusive; 1190 CREATE TABLE t1(a, b); 1191 INSERT INTO t1 VALUES(1, 2); 1192 PRAGMA page_size = 4096; 1193 } 1194} -body { 1195 execsql { 1196 VACUUM; 1197 } 1198} -test { 1199 faultsim_test_result {0 {}} 1200 1201 set contents [db eval {SELECT * FROM t1}] 1202 if {$contents != "1 2"} { error "Bad database contents ($contents)" } 1203 1204 set sz [file size test.db] 1205 if {$testrc!=0 && $sz!=1024*3 && $sz!=4096*3} { 1206 error "Expected file size to be 3072 or 12288 bytes - actual size $sz bytes" 1207 } 1208 if {$testrc==0 && $sz!=4096*3} { 1209 error "Expected file size to be 12288 bytes - actual size $sz bytes" 1210 } 1211} 1212 1213do_test pagerfault-27-pre { 1214 faultsim_delete_and_reopen 1215 db func a_string a_string 1216 execsql { 1217 PRAGMA page_size = 1024; 1218 CREATE TABLE t1(a, b); 1219 CREATE TABLE t2(a UNIQUE, b UNIQUE); 1220 INSERT INTO t2 VALUES( a_string(800), a_string(800) ); 1221 INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; 1222 INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; 1223 INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; 1224 INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; 1225 INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; 1226 INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; 1227 INSERT INTO t1 VALUES (a_string(20000), a_string(20000)); 1228 } 1229 faultsim_save_and_close 1230} {} 1231do_faultsim_test pagerfault-27 -faults ioerr-persistent -prep { 1232 faultsim_restore_and_reopen 1233 db func a_string a_string 1234 execsql { 1235 PRAGMA cache_size = 10; 1236 BEGIN EXCLUSIVE; 1237 } 1238 set ::channel [db incrblob t1 a 1] 1239} -body { 1240 puts $::channel [string repeat abc 6000] 1241 flush $::channel 1242} -test { 1243 catchsql { UPDATE t2 SET a = a_string(800), b = a_string(800) } 1244 catch { close $::channel } 1245 catchsql { ROLLBACK } 1246 faultsim_integrity_check 1247} 1248 1249finish_test 1250 1251