xref: /sqlite-3.40.0/test/interrupt.test (revision a408adc5)
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.14 2007/05/15 16:51:37 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} {
184    db collate fake_collation no-op
185    sqlite3_interrupt db
186    return SQLITE_OK
187  }
188  db collation_needed fake_interrupt
189  catchsql {
190    CREATE INDEX fake ON fake1(a COLLATE fake_collation, b, c DESC);
191  }
192} {1 interrupt}
193
194finish_test
195