1 2#pragma ident "%Z%%M% %I% %E% SMI" 3 4# 2004 Feb 8 5# 6# The author disclaims copyright to this source code. In place of 7# a legal notice, here is a blessing: 8# 9# May you do good and not evil. 10# May you find forgiveness for yourself and forgive others. 11# May you share freely, never taking more than you give. 12# 13#*********************************************************************** 14# This file implements regression tests for SQLite library. The 15# focus of this script is the sqlite_interrupt() API. 16# 17# $Id: interrupt.test,v 1.4.2.1 2004/05/10 20:27:42 drh Exp $ 18 19 20set testdir [file dirname $argv0] 21source $testdir/tester.tcl 22 23# Compute a checksum on the entire database. 24# 25proc cksum {{db db}} { 26 set txt [$db eval {SELECT name, type, sql FROM sqlite_master}]\n 27 foreach tbl [$db eval {SELECT name FROM sqlite_master WHERE type='table'}] { 28 append txt [$db eval "SELECT * FROM $tbl"]\n 29 } 30 foreach prag {default_synchronous default_cache_size} { 31 append txt $prag-[$db eval "PRAGMA $prag"]\n 32 } 33 set cksum [string length $txt]-[md5 $txt] 34 # puts $cksum-[file size test.db] 35 return $cksum 36} 37 38# This routine attempts to execute the sql in $sql. It triggers an 39# interrupt a progressively later and later points during the processing 40# and checks to make sure SQLITE_INTERRUPT is returned. Eventually, 41# the routine completes successfully. 42# 43proc interrupt_test {testid sql result {initcnt 0} {maxcnt 1000000}} { 44 set orig_sum [cksum] 45 set i $initcnt 46 global sqlite_interrupt_count 47 while {$i<$maxcnt} { 48 incr i 49 set sqlite_interrupt_count $i 50 do_test $testid.$i.1 [format { 51 set ::r [catchsql %s] 52 set ::code [db errorcode] 53 expr {$::code==0 || $::code==9} 54 } [list $sql]] 1 55 if {$::code==9} { 56 do_test $testid.$i.2 { 57 cksum 58 } $orig_sum 59 } elseif {$sqlite_interrupt_count>0} { 60 do_test $testid.$i.99 { 61 set ::r 62 } [list 0 $result] 63 break 64 } 65 } 66 set sqlite_interrupt_count 0 67} 68 69do_test interrupt-1.1 { 70 execsql { 71 CREATE TABLE t1(a,b); 72 SELECT name FROM sqlite_master; 73 } 74} {t1} 75interrupt_test interrupt-1.2 {DROP TABLE t1} {} 1 14 76do_test interrupt-1.3 { 77 execsql { 78 SELECT name FROM sqlite_master; 79 } 80} {} 81integrity_check interrupt-1.4 82 83do_test interrrupt-2.1 { 84 execsql { 85 BEGIN; 86 CREATE TABLE t1(a,b); 87 INSERT INTO t1 VALUES(1,randstr(300,400)); 88 INSERT INTO t1 SELECT a+1, randstr(300,400) FROM t1; 89 INSERT INTO t1 SELECT a+2, a || '-' || b FROM t1; 90 INSERT INTO t1 SELECT a+4, a || '-' || b FROM t1; 91 INSERT INTO t1 SELECT a+8, a || '-' || b FROM t1; 92 INSERT INTO t1 SELECT a+16, a || '-' || b FROM t1; 93 INSERT INTO t1 SELECT a+32, a || '-' || b FROM t1; 94 COMMIT; 95 UPDATE t1 SET b=substr(b,-5,5); 96 SELECT count(*) from t1; 97 } 98} 64 99set origsize [file size test.db] 100set cksum [db eval {SELECT md5sum(a || b) FROM t1}] 101interrupt_test interrupt-2.2 {VACUUM} {} 100 102do_test interrupt-2.3 { 103 execsql { 104 SELECT md5sum(a || b) FROM t1; 105 } 106} $cksum 107do_test interrupt-2.4 { 108 expr {$::origsize>[file size test.db]} 109} 1 110integrity_check interrupt-2.5 111 112# Ticket #594. If an interrupt occurs in the middle of a transaction 113# and that transaction is later rolled back, the internal schema tables do 114# not reset. 115# 116for {set i 1} {$i<50} {incr i 5} { 117 do_test interrupt-3.$i.1 { 118 execsql { 119 BEGIN; 120 CREATE TEMP TABLE t2(x,y); 121 SELECT name FROM sqlite_temp_master; 122 } 123 } {t2} 124 do_test interrupt-3.$i.2 { 125 set ::sqlite_interrupt_count $::i 126 catchsql { 127 INSERT INTO t2 SELECT * FROM t1; 128 } 129 } {1 interrupted} 130 do_test interrupt-3.$i.3 { 131 execsql { 132 SELECT name FROM sqlite_temp_master; 133 } 134 } {t2} 135 do_test interrupt-3.$i.4 { 136 catchsql { 137 ROLLBACK 138 } 139 } {0 {}} 140 do_test interrupt-3.$i.5 { 141 catchsql {SELECT name FROM sqlite_temp_master}; 142 execsql { 143 SELECT name FROM sqlite_temp_master; 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# 152execsql { 153 CREATE TABLE t2(a,b,c); 154 INSERT INTO t2 SELECT round(a/10), randstr(50,80), randstr(50,60) FROM t1; 155} 156set sql { 157 SELECT max(min(b,c)), min(max(b,c)), a FROM t2 GROUP BY a ORDER BY a; 158} 159set sqlite_interrupt_count 1000000 160execsql $sql 161set max_count [expr {1000000-$sqlite_interrupt_count}] 162for {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 170finish_test 171