| OLD | NEW | 
 | (Empty) | 
|    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.16 2008/01/16 17:46:38 drh 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 |  | 
| OLD | NEW |