diff options
Diffstat (limited to 'test/interrupt.test')
-rw-r--r-- | test/interrupt.test | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/test/interrupt.test b/test/interrupt.test new file mode 100644 index 0000000..23d9869 --- /dev/null +++ b/test/interrupt.test @@ -0,0 +1,199 @@ +# 2004 Feb 8 +# +# 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 implements regression tests for SQLite library. The +# focus of this script is the sqlite_interrupt() API. +# +# $Id: interrupt.test,v 1.16 2008/01/16 17:46:38 drh Exp $ + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set DB [sqlite3_connection_pointer db] + +# This routine attempts to execute the sql in $sql. It triggers an +# interrupt at progressively later and later points during the processing +# and checks to make sure SQLITE_INTERRUPT is returned. Eventually, +# the routine completes successfully. +# +proc interrupt_test {testid sql result {initcnt 0}} { + set orig_sum [cksum] + set i $initcnt + while 1 { + incr i + set ::sqlite_interrupt_count $i + do_test $testid.$i.1 [format { + set ::r [catchsql %s] + set ::code [db errorcode] + expr {$::code==0 || $::code==9} + } [list $sql]] 1 + if {$::code==9} { + do_test $testid.$i.2 { + cksum + } $orig_sum + } else { + do_test $testid.$i.99 { + set ::r + } [list 0 $result] + break + } + } + set ::sqlite_interrupt_count 0 +} + +do_test interrupt-1.1 { + execsql { + CREATE TABLE t1(a,b); + SELECT name FROM sqlite_master; + } +} {t1} +interrupt_test interrupt-1.2 {DROP TABLE t1} {} +do_test interrupt-1.3 { + execsql { + SELECT name FROM sqlite_master; + } +} {} +integrity_check interrupt-1.4 + +do_test interrrupt-2.1 { + execsql { + BEGIN; + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,randstr(300,400)); + INSERT INTO t1 SELECT a+1, randstr(300,400) FROM t1; + INSERT INTO t1 SELECT a+2, a || '-' || b FROM t1; + INSERT INTO t1 SELECT a+4, a || '-' || b FROM t1; + INSERT INTO t1 SELECT a+8, a || '-' || b FROM t1; + INSERT INTO t1 SELECT a+16, a || '-' || b FROM t1; + INSERT INTO t1 SELECT a+32, a || '-' || b FROM t1; + COMMIT; + UPDATE t1 SET b=substr(b,-5,5); + SELECT count(*) from t1; + } +} 64 +set origsize [file size test.db] +set cksum [db eval {SELECT md5sum(a || b) FROM t1}] +ifcapable {vacuum} { + interrupt_test interrupt-2.2 {VACUUM} {} 100 +} +do_test interrupt-2.3 { + execsql { + SELECT md5sum(a || b) FROM t1; + } +} $cksum +ifcapable {vacuum && !default_autovacuum} { + do_test interrupt-2.4 { + expr {$::origsize>[file size test.db]} + } 1 +} +ifcapable {explain} { + do_test interrupt-2.5.1 { + sqlite3_is_interrupted $DB + } {0} + do_test interrupt-2.5.2 { + unset -nocomplain ::interrupt_count + set ::interrupt_count 0 + set sql {EXPLAIN SELECT max(a,b), a, b FROM t1} + execsql $sql + set rc [catch {db eval $sql { + sqlite3_interrupt $DB; + incr ::interrupt_count [sqlite3_is_interrupted $DB]; + }} msg] + lappend rc $msg + } {1 interrupted} + do_test interrupt-2.5.3 { + set ::interrupt_count + } {1} +} +integrity_check interrupt-2.6 +do_test interrupt-2.7 { + sqlite3_is_interrupted $DB +} {0} + +# Ticket #594. If an interrupt occurs in the middle of a transaction +# and that transaction is later rolled back, the internal schema tables do +# not reset. +# +# UPDATE: Interrupting a DML statement in the middle of a transaction now +# causes the transaction to roll back. Leaving the transaction open after +# an SQL statement was interrupted halfway through risks database corruption. +# +ifcapable tempdb { + for {set i 1} {$i<50} {incr i 5} { + do_test interrupt-3.$i.1 { + execsql { + BEGIN; + CREATE TEMP TABLE t2(x,y); + SELECT name FROM sqlite_temp_master; + } + } {t2} + do_test interrupt-3.$i.2 { + set ::sqlite_interrupt_count $::i + catchsql { + INSERT INTO t2 SELECT * FROM t1; + } + } {1 interrupted} + do_test interrupt-3.$i.3 { + execsql { + SELECT name FROM temp.sqlite_master; + } + } {} + do_test interrupt-3.$i.4 { + catchsql { + ROLLBACK + } + } {1 {cannot rollback - no transaction is active}} + do_test interrupt-3.$i.5 { + catchsql {SELECT name FROM sqlite_temp_master}; + execsql { + SELECT name FROM temp.sqlite_master; + } + } {} + } +} + +# There are reports of a memory leak if an interrupt occurs during +# the beginning of a complex query - before the first callback. We +# will try to reproduce it here: +# +execsql { + CREATE TABLE t2(a,b,c); + INSERT INTO t2 SELECT round(a/10), randstr(50,80), randstr(50,60) FROM t1; +} +set sql { + SELECT max(min(b,c)), min(max(b,c)), a FROM t2 GROUP BY a ORDER BY a; +} +set sqlite_interrupt_count 1000000 +execsql $sql +set max_count [expr {1000000-$sqlite_interrupt_count}] +for {set i 1} {$i<$max_count-5} {incr i 1} { + do_test interrupt-4.$i.1 { + set ::sqlite_interrupt_count $::i + catchsql $sql + } {1 interrupted} +} + +if {0} { # This doesn't work anymore since the collation factor is + # no longer called during schema parsing. +# Interrupt during parsing +# +do_test interrupt-5.1 { + proc fake_interrupt {args} { + db collate fake_collation no-op + sqlite3_interrupt db + return SQLITE_OK + } + db collation_needed fake_interrupt + catchsql { + CREATE INDEX fake ON fake1(a COLLATE fake_collation, b, c DESC); + } +} {1 interrupt} +} +finish_test |