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