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