Home | History | Annotate | Download | only in test
      1 # 2004 June 30
      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 verifying that a rollback in one statement
     13 # caused by an ON CONFLICT ROLLBACK clause aborts any other pending
     14 # statements.
     15 #
     16 # $Id: rollback.test,v 1.11 2009/06/26 07:12:07 danielk1977 Exp $
     17 
     18 set testdir [file dirname $argv0]
     19 source $testdir/tester.tcl
     20 
     21 set DB [sqlite3_connection_pointer db]
     22 
     23 do_test rollback-1.1 {
     24   execsql {
     25     CREATE TABLE t1(a);
     26     INSERT INTO t1 VALUES(1);
     27     INSERT INTO t1 VALUES(2);
     28     INSERT INTO t1 VALUES(3);
     29     INSERT INTO t1 VALUES(4);
     30     SELECT * FROM t1;
     31   }
     32 } {1 2 3 4}
     33 
     34 ifcapable conflict {
     35   do_test rollback-1.2 {
     36     execsql {
     37       CREATE TABLE t3(a unique on conflict rollback);
     38       INSERT INTO t3 SELECT a FROM t1;
     39       BEGIN;
     40       INSERT INTO t1 SELECT * FROM t1;
     41     }
     42   } {}
     43 }
     44 do_test rollback-1.3 {
     45   set STMT [sqlite3_prepare $DB "SELECT a FROM t1" -1 TAIL]
     46   sqlite3_step $STMT
     47 } {SQLITE_ROW}
     48 
     49 ifcapable conflict {
     50   # This causes a ROLLBACK, which deletes the table out from underneath the
     51   # SELECT statement.
     52   #
     53   do_test rollback-1.4 {
     54     catchsql {
     55       INSERT INTO t3 SELECT a FROM t1;
     56     }
     57   } {1 {column a is not unique}}
     58   
     59   # Try to continue with the SELECT statement
     60   #
     61   do_test rollback-1.5 {
     62     sqlite3_step $STMT
     63   } {SQLITE_ERROR}
     64 
     65   # Restart the SELECT statement
     66   #
     67   do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_ABORT}
     68 } else {
     69   do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_OK}
     70 }
     71 
     72 do_test rollback-1.7 {
     73   sqlite3_step $STMT
     74 } {SQLITE_ROW}
     75 do_test rollback-1.8 {
     76   sqlite3_step $STMT
     77 } {SQLITE_ROW}
     78 do_test rollback-1.9 {
     79   sqlite3_finalize $STMT
     80 } {SQLITE_OK}
     81 
     82 if {$tcl_platform(platform) == "unix" 
     83  && [permutation] ne "onefile"
     84  && [permutation] ne "inmemory_journal"
     85 } {
     86   do_test rollback-2.1 {
     87     execsql {
     88       BEGIN;
     89       INSERT INTO t3 VALUES('hello world');
     90     }
     91     file copy -force test.db testA.db
     92     file copy -force test.db-journal testA.db-journal
     93     execsql {
     94       COMMIT;
     95     }
     96   } {}
     97 
     98   # At this point files testA.db and testA.db-journal are present in the
     99   # file system. This block adds a master-journal file pointer to the
    100   # end of testA.db-journal. The master-journal file does not exist.
    101   # 
    102   set mj [file normalize testA.db-mj-123]
    103   binary scan $mj c* a
    104   set cksum 0
    105   foreach i $a { incr cksum $i }
    106   set mj_pgno [expr $sqlite_pending_byte / 1024]
    107   set zAppend [binary format Ia*IIa8 $mj_pgno $mj [string length $mj] $cksum \
    108     "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
    109   ]
    110   set iOffset [expr (([file size testA.db-journal] + 511)/512)*512]
    111   set fd [open testA.db-journal a+]
    112   fconfigure $fd -encoding binary -translation binary
    113   seek $fd $iOffset
    114   puts -nonewline $fd $zAppend
    115 
    116   # Also, fix the first journal-header in the journal-file. Because the
    117   # journal file has not yet been synced, the 8-byte magic string at the
    118   # start of the first journal-header has not been written by SQLite.
    119   # So write it now.
    120   seek $fd 0
    121   puts -nonewline $fd "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
    122   close $fd
    123 
    124   # Open a handle on testA.db and use it to query the database. At one
    125   # point the first query would attempt a hot rollback, attempt to open
    126   # the master-journal file and return SQLITE_CANTOPEN when it could not
    127   # be opened. This is incorrect, it should simply delete the journal
    128   # file and proceed with the query.
    129   # 
    130   do_test rollback-2.2 {
    131     sqlite3 db2 testA.db
    132     execsql {
    133       SELECT distinct tbl_name FROM sqlite_master;
    134     } db2
    135   } {t1 t3}
    136   if {[lsearch {exclusive persistent_journal no_journal} [permutation]]<0} {
    137     do_test rollback-2.3 {
    138       file exists testA.db-journal
    139     } 0
    140   }
    141   do_test rollback-2.4 {
    142     execsql {
    143       SELECT distinct tbl_name FROM sqlite_master;
    144     } db2
    145   } {t1 t3}
    146 
    147   db2 close
    148 }
    149 
    150 finish_test
    151