Home | History | Annotate | Download | only in test
      1 # 2006 July 25
      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 focus
     12 # of this test is reading and writing to the database from within a
     13 # virtual table xSync() callback.
     14 #
     15 # $Id: vtab7.test,v 1.4 2007/12/04 16:54:53 drh Exp $
     16 
     17 set testdir [file dirname $argv0]
     18 source $testdir/tester.tcl
     19 
     20 ifcapable !vtab {
     21   finish_test
     22   return
     23 }
     24 
     25 # Register the echo module. Code inside the echo module appends elements
     26 # to the global tcl list variable ::echo_module whenever SQLite invokes
     27 # certain module callbacks. This includes the xSync(), xCommit() and 
     28 # xRollback() callbacks. For each of these callback, two elements are
     29 # appended to ::echo_module, as follows:
     30 #
     31 #     Module method        Elements appended to ::echo_module
     32 #     -------------------------------------------------------
     33 #     xSync()              xSync echo($tablename)
     34 #     xCommit()            xCommit echo($tablename)
     35 #     xRollback()          xRollback echo($tablename)
     36 #     -------------------------------------------------------
     37 #
     38 # In each case, $tablename is replaced by the name of the real table (not
     39 # the echo table). By setting up a tcl trace on the ::echo_module variable,
     40 # code in this file arranges for a Tcl script to be executed from within
     41 # the echo module xSync() callback.
     42 #
     43 register_echo_module [sqlite3_connection_pointer db]
     44 trace add variable ::echo_module write echo_module_trace
     45 
     46 # This Tcl proc is invoked whenever the ::echo_module variable is written.
     47 #
     48 proc echo_module_trace {args} {
     49   # Filter out writes to ::echo_module that are not xSync, xCommit or 
     50   # xRollback callbacks.
     51   if {[llength $::echo_module] < 2} return
     52   set x [lindex $::echo_module end-1]
     53   if {$x ne "xSync" && $x ne "xCommit" && $x ne "xRollback"} return
     54 
     55   regexp {^echo.(.*).$} [lindex $::echo_module end] dummy tablename
     56   # puts "Ladies and gentlemen, an $x on $tablename!"
     57 
     58   if {[info exists ::callbacks($x,$tablename)]} {
     59     eval $::callbacks($x,$tablename)
     60   }
     61 }
     62 
     63 # The following tests, vtab7-1.*, test that the trace callback on 
     64 # ::echo_module is providing the expected tcl callbacks.
     65 do_test vtab7-1.1 {
     66   execsql {
     67     CREATE TABLE abc(a, b, c);
     68     CREATE VIRTUAL TABLE abc2 USING echo(abc);
     69   }
     70 } {}
     71 
     72 do_test vtab7-1.2 {
     73   set ::callbacks(xSync,abc) {incr ::counter}
     74   set ::counter 0
     75   execsql {
     76     INSERT INTO abc2 VALUES(1, 2, 3);
     77   }
     78   set ::counter
     79 } {1}
     80 
     81 # Write to an existing database table from within an xSync callback.
     82 do_test vtab7-2.1 {
     83   set ::callbacks(xSync,abc) {
     84     execsql {INSERT INTO log VALUES('xSync');}
     85   }
     86   execsql {
     87     CREATE TABLE log(msg);
     88     INSERT INTO abc2 VALUES(4, 5, 6);
     89     SELECT * FROM log;
     90   }
     91 } {xSync}
     92 do_test vtab7-2.3 {
     93   execsql {
     94     INSERT INTO abc2 VALUES(4, 5, 6);
     95     SELECT * FROM log;
     96   }
     97 } {xSync xSync}
     98 do_test vtab7-2.4 {
     99   execsql {
    100     INSERT INTO abc2 VALUES(4, 5, 6);
    101     SELECT * FROM log;
    102   }
    103 } {xSync xSync xSync}
    104 
    105 # Create a database table from within xSync callback.
    106 do_test vtab7-2.5 {
    107   set ::callbacks(xSync,abc) {
    108     execsql { CREATE TABLE newtab(d, e, f); }
    109   }
    110   execsql {
    111     INSERT INTO abc2 VALUES(1, 2, 3);
    112     SELECT name FROM sqlite_master ORDER BY name;
    113   }
    114 } {abc abc2 log newtab}
    115 
    116 # Drop a database table from within xSync callback.
    117 # This is not allowed.  Tables cannot be dropped while
    118 # any other statement is active.
    119 #
    120 do_test vtab7-2.6 {
    121   set ::callbacks(xSync,abc) {
    122     set ::rc [catchsql { DROP TABLE newtab }]
    123   }
    124   execsql {
    125     INSERT INTO abc2 VALUES(1, 2, 3);
    126     SELECT name FROM sqlite_master ORDER BY name;
    127   }
    128 } {abc abc2 log newtab}
    129 do_test vtab7-2.6.1 {
    130   set ::rc
    131 } {1 {database table is locked}}
    132 execsql {DROP TABLE newtab}
    133 
    134 # Write to an attached database from xSync().
    135 ifcapable attach {
    136   do_test vtab7-3.1 {
    137     file delete -force test2.db
    138     file delete -force test2.db-journal
    139     execsql {
    140       ATTACH 'test2.db' AS db2;
    141       CREATE TABLE db2.stuff(description, shape, color);
    142     }
    143     set ::callbacks(xSync,abc) {
    144       execsql { INSERT INTO db2.stuff VALUES('abc', 'square', 'green'); }
    145     }
    146     execsql {
    147       INSERT INTO abc2 VALUES(1, 2, 3);
    148       SELECT * from stuff;
    149     }
    150   } {abc square green}
    151 }
    152 
    153 # UPDATE: The next test passes, but leaks memory. So leave it out.
    154 #
    155 # The following tests test that writing to the database from within
    156 # the xCommit callback causes a misuse error.
    157 # do_test vtab7-4.1 {
    158 #   unset -nocomplain ::callbacks(xSync,abc)
    159 #   set ::callbacks(xCommit,abc) {
    160 #     execsql { INSERT INTO log VALUES('hello') }
    161 #   }
    162 #   catchsql {
    163 #     INSERT INTO abc2 VALUES(1, 2, 3);
    164 #   }
    165 # } {1 {library routine called out of sequence}}
    166 
    167 # These tests, vtab7-4.*, test that an SQLITE_LOCKED error is returned
    168 # if an attempt to write to a virtual module table or create a new 
    169 # virtual table from within an xSync() callback.
    170 do_test vtab7-4.1 {
    171   execsql {
    172     CREATE TABLE def(d, e, f);
    173     CREATE VIRTUAL TABLE def2 USING echo(def);
    174   }
    175   set ::callbacks(xSync,abc) {
    176     set ::error [catchsql { INSERT INTO def2 VALUES(1, 2, 3) }]
    177   }
    178   execsql {
    179     INSERT INTO abc2 VALUES(1, 2, 3);
    180   }
    181   set ::error
    182 } {1 {database table is locked}}
    183 do_test vtab7-4.2 {
    184   set ::callbacks(xSync,abc) {
    185     set ::error [catchsql { CREATE VIRTUAL TABLE def3 USING echo(def) }]
    186   }
    187   execsql {
    188     INSERT INTO abc2 VALUES(1, 2, 3);
    189   }
    190   set ::error
    191 } {1 {database table is locked}}
    192 
    193 do_test vtab7-4.3 {
    194   set ::callbacks(xSync,abc) {
    195     set ::error [catchsql { DROP TABLE def2 }]
    196   }
    197   execsql {
    198     INSERT INTO abc2 VALUES(1, 2, 3);
    199     SELECT name FROM sqlite_master ORDER BY name;
    200   }
    201   set ::error
    202 } {1 {database table is locked}}
    203 
    204 trace remove variable ::echo_module write echo_module_trace
    205 unset -nocomplain ::callbacks
    206 
    207 finish_test
    208