# 2007 August 23 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains tests that verify that SQLite can correctly rollback # databases after crashes when using the special IO modes triggered # by device IOCAP flags. # # $Id: crash3.test,v 1.4 2008/07/12 14:52:20 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !crashtest { finish_test return } proc do_test2 {name tcl res1 res2} { set script [subst -nocommands { do_test $name { set res1 {$res1} set res2 {$res2} set res [eval {$tcl}] if {[set res] eq [set res1] || [set res] eq [set res2]} { set res "{[set res1]} or {[set res2]}" } set res } {{$res1} or {$res2}} }] uplevel $script } # This block tests crash-recovery when the IOCAP_ATOMIC flags is set. # # Each iteration of the following loop sets up the database to contain # the following schema and data: # # CREATE TABLE abc(a, b, c); # INSERT INTO abc VALUES(1, 2, 3); # # Then execute the SQL statement, scheduling a crash for part-way through # the first sync() of either the database file or the journal file (often # the journal file is not required - meaning no crash occurs). # # After the crash (or absence of a crash), open the database and # verify that: # # * The integrity check passes, and # * The contents of table abc is either {1 2 3} or the value specified # to the right of the SQL statement below. # # The procedure is repeated 10 times for each SQL statement. Five times # with the crash scheduled for midway through the first journal sync (if # any), and five times with the crash midway through the database sync. # set tn 1 foreach {sql res2} [list \ {INSERT INTO abc VALUES(4, 5, 6)} {1 2 3 4 5 6} \ {DELETE FROM abc} {} \ {INSERT INTO abc SELECT * FROM abc} {1 2 3 1 2 3} \ {UPDATE abc SET a = 2} {2 2 3} \ {INSERT INTO abc VALUES(4, 5, randstr(1000,1000))} {n/a} \ {CREATE TABLE def(d, e, f)} {n/a} \ ] { for {set ii 0} {$ii < 10} {incr ii} { db close forcedelete test.db test.db-journal sqlite3 db test.db do_test crash3-1.$tn.1 { execsql { PRAGMA page_size = 1024; BEGIN; CREATE TABLE abc(a, b, c); INSERT INTO abc VALUES(1, 2, 3); COMMIT; } } {} db close set crashfile test.db if {($ii%2)==0} { append crashfile -journal } set rand "SELECT randstr($tn,$tn);" do_test crash3-1.$tn.2 [subst { crashsql -file $crashfile -char atomic {$rand $sql} sqlite3 db test.db execsql { PRAGMA integrity_check; } }] {ok} do_test2 crash3-1.$tn.3 { execsql { SELECT * FROM abc } } {1 2 3} $res2 incr tn } } # This block tests both the IOCAP_SEQUENTIAL and IOCAP_SAFE_APPEND flags. # db close forcedelete test.db test.db-journal sqlite3 db test.db do_test crash3-2.0 { execsql { BEGIN; CREATE TABLE abc(a PRIMARY KEY, b, c); CREATE TABLE def(d PRIMARY KEY, e, f); PRAGMA default_cache_size = 10; INSERT INTO abc VALUES(randstr(10,1000),randstr(10,1000),randstr(10,1000)); INSERT INTO abc SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc; INSERT INTO abc SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc; INSERT INTO abc SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc; INSERT INTO abc SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc; INSERT INTO abc SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc; INSERT INTO abc SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc; COMMIT; } } {} set tn 1 foreach {::crashfile ::delay ::char} { test.db 1 sequential test.db 1 safe_append test.db-journal 1 sequential test.db-journal 1 safe_append test.db-journal 2 safe_append test.db-journal 2 sequential test.db-journal 3 sequential test.db-journal 3 safe_append } { for {set ii 0} {$ii < 100} {incr ii} { set ::SQL [subst { SELECT randstr($ii,$ii+10); BEGIN; DELETE FROM abc WHERE random()%5; INSERT INTO abc SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc WHERE (random()%5)==0; DELETE FROM def WHERE random()%5; INSERT INTO def SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM def WHERE (random()%5)==0; COMMIT; }] do_test crash3-2.$tn.$ii { crashsql -file $::crashfile -delay $::delay -char $::char $::SQL db close sqlite3 db test.db execsql {PRAGMA integrity_check} } {ok} } incr tn } # The following block tests an interaction between IOCAP_ATOMIC and # IOCAP_SEQUENTIAL. At one point, if both flags were set, small # journal files that contained only a single page, but were required # for some other reason (i.e. nTrunk) were not being written to # disk. # for {set ii 0} {$ii < 10} {incr ii} { db close forcedelete test.db test.db-journal crashsql -file test.db -char {sequential atomic} { CREATE TABLE abc(a, b, c); } sqlite3 db test.db do_test crash3-3.$ii { execsql {PRAGMA integrity_check} } {ok} } finish_test