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