Home | History | Annotate | Download | only in test
      1 # 2004 Feb 8
      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 script is the sqlite_interrupt() API.
     13 #
     14 # $Id: interrupt.test,v 1.16 2008/01/16 17:46:38 drh Exp $
     15 
     16 
     17 set testdir [file dirname $argv0]
     18 source $testdir/tester.tcl
     19 set DB [sqlite3_connection_pointer db]
     20 
     21 # This routine attempts to execute the sql in $sql.  It triggers an
     22 # interrupt at progressively later and later points during the processing
     23 # and checks to make sure SQLITE_INTERRUPT is returned.  Eventually,
     24 # the routine completes successfully.
     25 #
     26 proc interrupt_test {testid sql result {initcnt 0}} {
     27   set orig_sum [cksum]
     28   set i $initcnt
     29   while 1 {
     30     incr i
     31     set ::sqlite_interrupt_count $i
     32     do_test $testid.$i.1 [format {
     33       set ::r [catchsql %s]
     34       set ::code [db errorcode]
     35       expr {$::code==0 || $::code==9}
     36     } [list $sql]] 1
     37     if {$::code==9} {
     38       do_test $testid.$i.2 {
     39         cksum
     40       } $orig_sum
     41     } else {
     42       do_test $testid.$i.99 {
     43         set ::r
     44       } [list 0 $result]
     45       break
     46     }
     47   }
     48   set ::sqlite_interrupt_count 0
     49 }
     50 
     51 do_test interrupt-1.1 {
     52   execsql {
     53     CREATE TABLE t1(a,b);
     54     SELECT name FROM sqlite_master;
     55   }
     56 } {t1}
     57 interrupt_test interrupt-1.2 {DROP TABLE t1} {}
     58 do_test interrupt-1.3 {
     59   execsql {
     60     SELECT name FROM sqlite_master;
     61   }
     62 } {}
     63 integrity_check interrupt-1.4
     64 
     65 do_test interrrupt-2.1 {
     66   execsql {
     67     BEGIN;
     68     CREATE TABLE t1(a,b);
     69     INSERT INTO t1 VALUES(1,randstr(300,400));
     70     INSERT INTO t1 SELECT a+1, randstr(300,400) FROM t1;
     71     INSERT INTO t1 SELECT a+2, a || '-' || b FROM t1;
     72     INSERT INTO t1 SELECT a+4, a || '-' || b FROM t1;
     73     INSERT INTO t1 SELECT a+8, a || '-' || b FROM t1;
     74     INSERT INTO t1 SELECT a+16, a || '-' || b FROM t1;
     75     INSERT INTO t1 SELECT a+32, a || '-' || b FROM t1;
     76     COMMIT;
     77     UPDATE t1 SET b=substr(b,-5,5);
     78     SELECT count(*) from t1;
     79   }
     80 } 64
     81 set origsize [file size test.db]
     82 set cksum [db eval {SELECT md5sum(a || b) FROM t1}]
     83 ifcapable {vacuum} {
     84   interrupt_test interrupt-2.2 {VACUUM} {} 100
     85 }
     86 do_test interrupt-2.3 {
     87   execsql {
     88     SELECT md5sum(a || b) FROM t1;
     89   }
     90 } $cksum
     91 ifcapable {vacuum && !default_autovacuum} {
     92   do_test interrupt-2.4 {
     93     expr {$::origsize>[file size test.db]}
     94   } 1
     95 }
     96 ifcapable {explain} {
     97   do_test interrupt-2.5 {
     98     set sql {EXPLAIN SELECT max(a,b), a, b FROM t1}
     99     execsql $sql
    100     set rc [catch {db eval $sql {sqlite3_interrupt $DB}} msg]
    101     lappend rc $msg
    102   } {1 interrupted}
    103 }
    104 integrity_check interrupt-2.6
    105 
    106 # Ticket #594.  If an interrupt occurs in the middle of a transaction
    107 # and that transaction is later rolled back, the internal schema tables do
    108 # not reset.
    109 #
    110 # UPDATE: Interrupting a DML statement in the middle of a transaction now
    111 # causes the transaction to roll back. Leaving the transaction open after
    112 # an SQL statement was interrupted halfway through risks database corruption.
    113 #
    114 ifcapable tempdb {
    115   for {set i 1} {$i<50} {incr i 5} {
    116     do_test interrupt-3.$i.1 {
    117       execsql {
    118         BEGIN;
    119         CREATE TEMP TABLE t2(x,y);
    120         SELECT name FROM sqlite_temp_master;
    121       }
    122     } {t2}
    123     do_test interrupt-3.$i.2 {
    124       set ::sqlite_interrupt_count $::i
    125       catchsql {
    126         INSERT INTO t2 SELECT * FROM t1;
    127       }
    128     } {1 interrupted}
    129     do_test interrupt-3.$i.3 {
    130       execsql {
    131         SELECT name FROM sqlite_temp_master;
    132       }
    133     } {}
    134     do_test interrupt-3.$i.4 {
    135       catchsql {
    136         ROLLBACK
    137       }
    138     } {1 {cannot rollback - no transaction is active}}
    139     do_test interrupt-3.$i.5 {
    140       catchsql {SELECT name FROM sqlite_temp_master};
    141       execsql {
    142         SELECT name FROM sqlite_temp_master;
    143       }
    144     } {}
    145   }
    146 }
    147 
    148 # There are reports of a memory leak if an interrupt occurs during
    149 # the beginning of a complex query - before the first callback.  We
    150 # will try to reproduce it here:
    151 #
    152 execsql {
    153   CREATE TABLE t2(a,b,c);
    154   INSERT INTO t2 SELECT round(a/10), randstr(50,80), randstr(50,60) FROM t1;
    155 }
    156 set sql {
    157   SELECT max(min(b,c)), min(max(b,c)), a FROM t2 GROUP BY a ORDER BY a;
    158 }
    159 set sqlite_interrupt_count 1000000
    160 execsql $sql
    161 set max_count [expr {1000000-$sqlite_interrupt_count}]
    162 for {set i 1} {$i<$max_count-5} {incr i 1} {
    163   do_test interrupt-4.$i.1 {
    164     set ::sqlite_interrupt_count $::i
    165     catchsql $sql
    166   } {1 interrupted}
    167 }
    168 
    169 # Interrupt during parsing
    170 #
    171 do_test interrupt-5.1 {
    172   proc fake_interrupt {args} {
    173     db collate fake_collation no-op
    174     sqlite3_interrupt db
    175     return SQLITE_OK
    176   }
    177   db collation_needed fake_interrupt
    178   catchsql {
    179     CREATE INDEX fake ON fake1(a COLLATE fake_collation, b, c DESC);
    180   }
    181 } {1 interrupt}
    182 
    183 finish_test
    184