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.3 2007/10/09 08:29:33 danielk1977 Exp $ 16 17set testdir [file dirname $argv0] 18source $testdir/tester.tcl 19 20ifcapable !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# 43register_echo_module [sqlite3_connection_pointer db] 44trace add variable ::echo_module write echo_module_trace 45 46# This Tcl proc is invoked whenever the ::echo_module variable is written. 47# 48proc 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. 65do_test vtab7-1.1 { 66 execsql { 67 CREATE TABLE abc(a, b, c); 68 CREATE VIRTUAL TABLE abc2 USING echo(abc); 69 } 70} {} 71 72do_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. 82do_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} 92do_test vtab7-2.3 { 93 execsql { 94 INSERT INTO abc2 VALUES(4, 5, 6); 95 SELECT * FROM log; 96 } 97} {xSync xSync} 98do_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. 106do_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. 117do_test vtab7-2.6 { 118 set ::callbacks(xSync,abc) { 119 execsql { DROP TABLE newtab } 120 } 121 execsql { 122 INSERT INTO abc2 VALUES(1, 2, 3); 123 SELECT name FROM sqlite_master ORDER BY name; 124 } 125} {abc abc2 log} 126 127# Write to an attached database from xSync(). 128ifcapable attach { 129 do_test vtab7-3.1 { 130 file delete -force test2.db 131 file delete -force test2.db-journal 132 execsql { 133 ATTACH 'test2.db' AS db2; 134 CREATE TABLE db2.stuff(description, shape, color); 135 } 136 set ::callbacks(xSync,abc) { 137 execsql { INSERT INTO db2.stuff VALUES('abc', 'square', 'green'); } 138 } 139 execsql { 140 INSERT INTO abc2 VALUES(1, 2, 3); 141 SELECT * from stuff; 142 } 143 } {abc square green} 144} 145 146# UPDATE: The next test passes, but leaks memory. So leave it out. 147# 148# The following tests test that writing to the database from within 149# the xCommit callback causes a misuse error. 150# do_test vtab7-4.1 { 151# unset -nocomplain ::callbacks(xSync,abc) 152# set ::callbacks(xCommit,abc) { 153# execsql { INSERT INTO log VALUES('hello') } 154# } 155# catchsql { 156# INSERT INTO abc2 VALUES(1, 2, 3); 157# } 158# } {1 {library routine called out of sequence}} 159 160# These tests, vtab7-4.*, test that an SQLITE_LOCKED error is returned 161# if an attempt to write to a virtual module table or create a new 162# virtual table from within an xSync() callback. 163do_test vtab7-4.1 { 164 execsql { 165 CREATE TABLE def(d, e, f); 166 CREATE VIRTUAL TABLE def2 USING echo(def); 167 } 168 set ::callbacks(xSync,abc) { 169 set ::error [catchsql { INSERT INTO def2 VALUES(1, 2, 3) }] 170 } 171 execsql { 172 INSERT INTO abc2 VALUES(1, 2, 3); 173 } 174 set ::error 175} {1 {database table is locked}} 176do_test vtab7-4.2 { 177 set ::callbacks(xSync,abc) { 178 set ::error [catchsql { CREATE VIRTUAL TABLE def3 USING echo(def) }] 179 } 180 execsql { 181 INSERT INTO abc2 VALUES(1, 2, 3); 182 } 183 set ::error 184} {1 {database table is locked}} 185 186do_test vtab7-4.3 { 187 set ::callbacks(xSync,abc) { 188 set ::error [catchsql { DROP TABLE def2 }] 189 } 190 execsql { 191 INSERT INTO abc2 VALUES(1, 2, 3); 192 SELECT name FROM sqlite_master ORDER BY name; 193 } 194 set ::error 195} {1 {database table is locked}} 196 197trace remove variable ::echo_module write echo_module_trace 198unset -nocomplain ::callbacks 199 200finish_test 201 202