| # 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 |