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.1.1.1 2012/02/21 17:04:16 misho Exp $
15:
16:
17: set testdir [file dirname $argv0]
18: source $testdir/tester.tcl
19: set DB [sqlite3_connection_pointer db]
20:
21: # This routine attempts to execute the sql in $sql. It triggers an
22: # interrupt at progressively later and later points during the processing
23: # and checks to make sure SQLITE_INTERRUPT is returned. Eventually,
24: # the routine completes successfully.
25: #
26: proc interrupt_test {testid sql result {initcnt 0}} {
27: set orig_sum [cksum]
28: set i $initcnt
29: while 1 {
30: incr i
31: set ::sqlite_interrupt_count $i
32: do_test $testid.$i.1 [format {
33: set ::r [catchsql %s]
34: set ::code [db errorcode]
35: expr {$::code==0 || $::code==9}
36: } [list $sql]] 1
37: if {$::code==9} {
38: do_test $testid.$i.2 {
39: cksum
40: } $orig_sum
41: } else {
42: do_test $testid.$i.99 {
43: set ::r
44: } [list 0 $result]
45: break
46: }
47: }
48: set ::sqlite_interrupt_count 0
49: }
50:
51: do_test interrupt-1.1 {
52: execsql {
53: CREATE TABLE t1(a,b);
54: SELECT name FROM sqlite_master;
55: }
56: } {t1}
57: interrupt_test interrupt-1.2 {DROP TABLE t1} {}
58: do_test interrupt-1.3 {
59: execsql {
60: SELECT name FROM sqlite_master;
61: }
62: } {}
63: integrity_check interrupt-1.4
64:
65: do_test interrrupt-2.1 {
66: execsql {
67: BEGIN;
68: CREATE TABLE t1(a,b);
69: INSERT INTO t1 VALUES(1,randstr(300,400));
70: INSERT INTO t1 SELECT a+1, randstr(300,400) FROM t1;
71: INSERT INTO t1 SELECT a+2, a || '-' || b FROM t1;
72: INSERT INTO t1 SELECT a+4, a || '-' || b FROM t1;
73: INSERT INTO t1 SELECT a+8, a || '-' || b FROM t1;
74: INSERT INTO t1 SELECT a+16, a || '-' || b FROM t1;
75: INSERT INTO t1 SELECT a+32, a || '-' || b FROM t1;
76: COMMIT;
77: UPDATE t1 SET b=substr(b,-5,5);
78: SELECT count(*) from t1;
79: }
80: } 64
81: set origsize [file size test.db]
82: set cksum [db eval {SELECT md5sum(a || b) FROM t1}]
83: ifcapable {vacuum} {
84: interrupt_test interrupt-2.2 {VACUUM} {} 100
85: }
86: do_test interrupt-2.3 {
87: execsql {
88: SELECT md5sum(a || b) FROM t1;
89: }
90: } $cksum
91: ifcapable {vacuum && !default_autovacuum} {
92: do_test interrupt-2.4 {
93: expr {$::origsize>[file size test.db]}
94: } 1
95: }
96: ifcapable {explain} {
97: do_test interrupt-2.5 {
98: set sql {EXPLAIN SELECT max(a,b), a, b FROM t1}
99: execsql $sql
100: set rc [catch {db eval $sql {sqlite3_interrupt $DB}} msg]
101: lappend rc $msg
102: } {1 interrupted}
103: }
104: integrity_check interrupt-2.6
105:
106: # Ticket #594. If an interrupt occurs in the middle of a transaction
107: # and that transaction is later rolled back, the internal schema tables do
108: # not reset.
109: #
110: # UPDATE: Interrupting a DML statement in the middle of a transaction now
111: # causes the transaction to roll back. Leaving the transaction open after
112: # an SQL statement was interrupted halfway through risks database corruption.
113: #
114: ifcapable tempdb {
115: for {set i 1} {$i<50} {incr i 5} {
116: do_test interrupt-3.$i.1 {
117: execsql {
118: BEGIN;
119: CREATE TEMP TABLE t2(x,y);
120: SELECT name FROM sqlite_temp_master;
121: }
122: } {t2}
123: do_test interrupt-3.$i.2 {
124: set ::sqlite_interrupt_count $::i
125: catchsql {
126: INSERT INTO t2 SELECT * FROM t1;
127: }
128: } {1 interrupted}
129: do_test interrupt-3.$i.3 {
130: execsql {
131: SELECT name FROM sqlite_temp_master;
132: }
133: } {}
134: do_test interrupt-3.$i.4 {
135: catchsql {
136: ROLLBACK
137: }
138: } {1 {cannot rollback - no transaction is active}}
139: do_test interrupt-3.$i.5 {
140: catchsql {SELECT name FROM sqlite_temp_master};
141: execsql {
142: SELECT name FROM sqlite_temp_master;
143: }
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: #
152: execsql {
153: CREATE TABLE t2(a,b,c);
154: INSERT INTO t2 SELECT round(a/10), randstr(50,80), randstr(50,60) FROM t1;
155: }
156: set sql {
157: SELECT max(min(b,c)), min(max(b,c)), a FROM t2 GROUP BY a ORDER BY a;
158: }
159: set sqlite_interrupt_count 1000000
160: execsql $sql
161: set max_count [expr {1000000-$sqlite_interrupt_count}]
162: for {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: # Interrupt during parsing
170: #
171: do_test interrupt-5.1 {
172: proc fake_interrupt {args} {
173: db collate fake_collation no-op
174: sqlite3_interrupt db
175: return SQLITE_OK
176: }
177: db collation_needed fake_interrupt
178: catchsql {
179: CREATE INDEX fake ON fake1(a COLLATE fake_collation, b, c DESC);
180: }
181: } {1 interrupt}
182:
183: finish_test
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>