# 2006 June 10 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is creating and dropping virtual tables. # # $Id: vtab1.test,v 1.28 2006/06/21 16:02:44 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !vtab||!schema_pragmas { finish_test return } #---------------------------------------------------------------------- # Organization of tests in this file: # # vtab1-1.*: Error conditions and other issues surrounding creation/connection # of a virtual module. # vtab1-2.*: Test sqlite3_declare_vtab() and the xConnect/xDisconnect methods. # vtab1-3.*: Table scans and WHERE clauses. # vtab1-4.*: Table scans and ORDER BY clauses. # vtab1-5.*: Test queries that include joins. This brings the # sqlite3_index_info.estimatedCost variable into play. # vtab1-6.*: Test UPDATE/INSERT/DELETE on vtables. # vtab1-7.*: Test sqlite3_last_insert_rowid(). # # This file uses the "echo" module (see src/test8.c). Refer to comments # in that file for the special behaviour of the Tcl $echo_module variable. # # TODO: # * How to test the sqlite3_index_constraint_usage.omit field? # * vtab1-5.* # #---------------------------------------------------------------------- # Test cases vtab1.1.* # # We cannot create a virtual table if the module has not been registered. # do_test vtab1-1.1 { catchsql { CREATE VIRTUAL TABLE t1 USING echo; } } {1 {no such module: echo}} do_test vtab1-1.2 { execsql { SELECT name FROM sqlite_master ORDER BY 1 } } {} # Register the module register_echo_module [sqlite3_connection_pointer db] # Once a module has been registered, virtual tables using that module # may be created. However if a module xCreate() fails to call # sqlite3_declare_vtab() an error will be raised and the table not created. # # The "echo" module does not invoke sqlite3_declare_vtab() if it is # passed zero arguments. # do_test vtab1-1.3 { catchsql { CREATE VIRTUAL TABLE t1 USING echo; } } {1 {vtable constructor did not declare schema: t1}} do_test vtab1-1.4 { execsql { SELECT name FROM sqlite_master ORDER BY 1 } } {} # The "echo" module xCreate method returns an error and does not create # the virtual table if it is passed an argument that does not correspond # to an existing real table in the same database. # do_test vtab1-1.5 { catchsql { CREATE VIRTUAL TABLE t1 USING echo(no_such_table); } } {1 {vtable constructor failed: t1}} do_test vtab1-1.6 { execsql { SELECT name FROM sqlite_master ORDER BY 1 } } {} # Test to make sure nothing goes wrong and no memory is leaked if we # select an illegal table-name (i.e a reserved name or the name of a # table that already exists). # do_test vtab1-1.7 { catchsql { CREATE VIRTUAL TABLE sqlite_master USING echo; } } {1 {object name reserved for internal use: sqlite_master}} do_test vtab1-1.8 { catchsql { CREATE TABLE treal(a, b, c); CREATE VIRTUAL TABLE treal USING echo(treal); } } {1 {table treal already exists}} do_test vtab1-1.9 { execsql { DROP TABLE treal; SELECT name FROM sqlite_master ORDER BY 1 } } {} do_test vtab1-1.10 { execsql { CREATE TABLE treal(a, b, c); CREATE VIRTUAL TABLE techo USING echo(treal); } db close sqlite3 db test.db catchsql { SELECT * FROM techo; } } {1 {no such module: echo}} do_test vtab1-1.11 { catchsql { INSERT INTO techo VALUES(1, 2, 3); } } {1 {no such module: echo}} do_test vtab1-1.12 { catchsql { UPDATE techo SET a = 10; } } {1 {no such module: echo}} do_test vtab1-1.13 { catchsql { DELETE FROM techo; } } {1 {no such module: echo}} do_test vtab1-1.14 { catchsql { PRAGMA table_info(techo) } } {1 {no such module: echo}} do_test vtab1-1.15 { catchsql { DROP TABLE techo; } } {1 {no such module: echo}} register_echo_module [sqlite3_connection_pointer db] do_test vtab1-1.X { execsql { DROP TABLE techo; DROP TABLE treal; SELECT sql FROM sqlite_master; } } {} #---------------------------------------------------------------------- # Test cases vtab1.2.* # # At this point, the database is completely empty. The echo module # has already been registered. # If a single argument is passed to the echo module during table # creation, it is assumed to be the name of a table in the same # database. The echo module attempts to set the schema of the # new virtual table to be the same as the existing database table. # do_test vtab1-2.1 { execsql { CREATE TABLE template(a, b, c); } execsql { PRAGMA table_info(template); } } [list \ 0 a {} 0 {} 0 \ 1 b {} 0 {} 0 \ 2 c {} 0 {} 0 \ ] do_test vtab1-2.2 { execsql { CREATE VIRTUAL TABLE t1 USING echo(template); } execsql { PRAGMA table_info(t1); } } [list \ 0 a {} 0 {} 0 \ 1 b {} 0 {} 0 \ 2 c {} 0 {} 0 \ ] # Test that the database can be unloaded. This should invoke the xDisconnect() # callback for the successfully create virtual table (t1). # do_test vtab1-2.3 { set echo_module [list] db close set echo_module } [list xDisconnect] # Re-open the database. This should not cause any virtual methods to # be called. The invocation of xConnect() is delayed until the virtual # table schema is first required by the compiler. # do_test vtab1-2.4 { set echo_module [list] sqlite3 db test.db db cache size 0 set echo_module } {} # Try to query the virtual table schema. This should fail, as the # echo module has not been registered with this database connection. # do_test vtab1.2.6 { breakpoint catchsql { PRAGMA table_info(t1); } } {1 {no such module: echo}} # Register the module register_echo_module [sqlite3_connection_pointer db] # Try to query the virtual table schema again. This time it should # invoke the xConnect method and succeed. # do_test vtab1.2.7 { execsql { PRAGMA table_info(t1); } } [list \ 0 a {} 0 {} 0 \ 1 b {} 0 {} 0 \ 2 c {} 0 {} 0 \ ] do_test vtab1.2.8 { set echo_module } {xConnect echo main t1 template} # Drop table t1. This should cause the xDestroy (but not xDisconnect) method # to be invoked. do_test vtab1-2.5 { set echo_module "" execsql { DROP TABLE t1; } set echo_module } {xDestroy} do_test vtab1-2.6 { execsql { PRAGMA table_info(t1); } } {} do_test vtab1-2.7 { execsql { SELECT sql FROM sqlite_master; } } [list {CREATE TABLE template(a, b, c)}] # Clean up other test artifacts: do_test vtab1-2.8 { execsql { DROP TABLE template; SELECT sql FROM sqlite_master; } } [list] #---------------------------------------------------------------------- # Test case vtab1-3 test table scans and the echo module's # xBestIndex/xFilter handling of WHERE conditions. do_test vtab1-3.1 { set echo_module "" execsql { CREATE TABLE treal(a INTEGER, b INTEGER, c); CREATE INDEX treal_idx ON treal(b); CREATE VIRTUAL TABLE t1 USING echo(treal); } set echo_module } [list xCreate echo main t1 treal \ xSync echo(treal) \ xCommit echo(treal) \ ] # Test that a SELECT on t1 doesn't crash. No rows are returned # because the underlying real table is currently empty. # do_test vtab1-3.2 { execsql { SELECT a, b, c FROM t1; } } {} # Put some data into the table treal. Then try a few simple SELECT # statements on t1. # do_test vtab1-3.3 { execsql { INSERT INTO treal VALUES(1, 2, 3); INSERT INTO treal VALUES(4, 5, 6); SELECT * FROM t1; } } {1 2 3 4 5 6} do_test vtab1-3.4 { execsql { SELECT a FROM t1; } } {1 4} do_test vtab1-3.5 { execsql { SELECT rowid FROM t1; } } {1 2} do_test vtab1-3.6 { set echo_module "" execsql { SELECT * FROM t1; } } {1 2 3 4 5 6} do_test vtab1-3.7 { execsql { SELECT rowid, * FROM t1; } } {1 1 2 3 2 4 5 6} do_test vtab1-3.8 { execsql { SELECT a AS d, b AS e, c AS f FROM t1; } } {1 2 3 4 5 6} # Execute some SELECT statements with WHERE clauses on the t1 table. # Then check the echo_module variable (written to by the module methods # in test8.c) to make sure the xBestIndex() and xFilter() methods were # called correctly. # do_test vtab1-3.8 { set echo_module "" execsql { SELECT * FROM t1; } set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal'} \ xFilter {SELECT rowid, * FROM 'treal'} ] do_test vtab1-3.9 { set echo_module "" execsql { SELECT * FROM t1 WHERE b = 5; } } {4 5 6} do_test vtab1-3.10 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b = ?} \ xFilter {SELECT rowid, * FROM 'treal' WHERE b = ?} 5 ] do_test vtab1-3.10 { set echo_module "" execsql { SELECT * FROM t1 WHERE b >= 5 AND b <= 10; } } {4 5 6} do_test vtab1-3.11 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} \ xFilter {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} 5 10 ] do_test vtab1-3.12 { set echo_module "" execsql { SELECT * FROM t1 WHERE b BETWEEN 2 AND 10; } } {1 2 3 4 5 6} do_test vtab1-3.13 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} \ xFilter {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} 2 10 ] # Add a function for the MATCH operator. Everything always matches! #proc test_match {lhs rhs} { # lappend ::echo_module MATCH $lhs $rhs # return 1 #} #db function match test_match set echo_module "" do_test vtab1-3.12 { set echo_module "" catchsql { SELECT * FROM t1 WHERE a MATCH 'string'; } } {1 {MATCH is not implemented}} do_test vtab1-3.13 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal'} \ xFilter {SELECT rowid, * FROM 'treal'}] do_test vtab1-3.14 { set echo_module "" btree_breakpoint execsql { SELECT * FROM t1 WHERE b MATCH 'string'; } } {} do_test vtab1-3.15 { set echo_module } [list xBestIndex \ {SELECT rowid, * FROM 'treal' WHERE b LIKE (SELECT '%'||?||'%')} \ xFilter \ {SELECT rowid, * FROM 'treal' WHERE b LIKE (SELECT '%'||?||'%')} \ string ] #---------------------------------------------------------------------- # Test case vtab1-3 test table scans and the echo module's # xBestIndex/xFilter handling of ORDER BY clauses. # This procedure executes the SQL. Then it checks to see if the OP_Sort # opcode was executed. If an OP_Sort did occur, then "sort" is appended # to the result. If no OP_Sort happened, then "nosort" is appended. # # This procedure is used to check to make sure sorting is or is not # occurring as expected. # proc cksort {sql} { set ::sqlite_sort_count 0 set data [execsql $sql] if {$::sqlite_sort_count} {set x sort} {set x nosort} lappend data $x return $data } do_test vtab1-4.1 { set echo_module "" cksort { SELECT b FROM t1 ORDER BY b; } } {2 5 nosort} do_test vtab1-4.2 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal' ORDER BY b ASC} \ xFilter {SELECT rowid, * FROM 'treal' ORDER BY b ASC} ] do_test vtab1-4.3 { set echo_module "" cksort { SELECT b FROM t1 ORDER BY b DESC; } } {5 2 nosort} do_test vtab1-4.4 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal' ORDER BY b DESC} \ xFilter {SELECT rowid, * FROM 'treal' ORDER BY b DESC} ] do_test vtab1-4.3 { set echo_module "" cksort { SELECT b FROM t1 ORDER BY b||''; } } {2 5 sort} do_test vtab1-4.4 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal'} \ xFilter {SELECT rowid, * FROM 'treal'} ] execsql { DROP TABLE t1; DROP TABLE treal; } #---------------------------------------------------------------------- # Test cases vtab1-5 test SELECT queries that include joins on virtual # tables. proc filter {log} { set out [list] for {set ii 0} {$ii < [llength $log]} {incr ii} { if {[lindex $log $ii] eq "xFilter"} { lappend out xFilter lappend out [lindex $log [expr $ii+1]] } } return $out } do_test vtab1-5-1 { execsql { CREATE TABLE t1(a, b, c); CREATE TABLE t2(d, e, f); INSERT INTO t1 VALUES(1, 'red', 'green'); INSERT INTO t1 VALUES(2, 'blue', 'black'); INSERT INTO t2 VALUES(1, 'spades', 'clubs'); INSERT INTO t2 VALUES(2, 'hearts', 'diamonds'); CREATE VIRTUAL TABLE et1 USING echo(t1); CREATE VIRTUAL TABLE et2 USING echo(t2); } } {} do_test vtab1-5-2 { set echo_module "" execsql { SELECT * FROM et1, et2; } } [list \ 1 red green 1 spades clubs \ 1 red green 2 hearts diamonds \ 2 blue black 1 spades clubs \ 2 blue black 2 hearts diamonds \ ] do_test vtab1-5-3 { filter $echo_module } [list \ xFilter {SELECT rowid, * FROM 't1'} \ xFilter {SELECT rowid, * FROM 't2'} \ xFilter {SELECT rowid, * FROM 't2'} \ ] do_test vtab1-5-4 { set echo_module "" execsql { SELECT * FROM et1, et2 WHERE et2.d = 2; } } [list \ 1 red green 2 hearts diamonds \ 2 blue black 2 hearts diamonds \ ] do_test vtab1-5-5 { filter $echo_module } [list \ xFilter {SELECT rowid, * FROM 't1'} \ xFilter {SELECT rowid, * FROM 't2'} \ xFilter {SELECT rowid, * FROM 't2'} \ ] do_test vtab1-5-6 { execsql { CREATE INDEX i1 ON t2(d); } db close sqlite3 db test.db register_echo_module [sqlite3_connection_pointer db] set echo_module "" execsql { SELECT * FROM et1, et2 WHERE et2.d = 2; } } [list \ 1 red green 2 hearts diamonds \ 2 blue black 2 hearts diamonds \ ] do_test vtab1-5-7 { filter $echo_module } [list \ xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \ xFilter {SELECT rowid, * FROM 't1'} \ ] execsql { DROP TABLE t1; DROP TABLE t2; DROP TABLE et1; DROP TABLE et2; } #---------------------------------------------------------------------- # Test cases vtab1-6 test INSERT, UPDATE and DELETE operations # on virtual tables. do_test vtab1-6-1 { execsql { SELECT sql FROM sqlite_master } } {} do_test vtab1-6-2 { execsql { CREATE TABLE treal(a PRIMARY KEY, b, c); CREATE VIRTUAL TABLE techo USING echo(treal); SELECT name FROM sqlite_master WHERE type = 'table'; } } {treal techo} do_test vtab1-6-3 { execsql { INSERT INTO techo VALUES(1, 2, 3); SELECT * FROM techo; } } {1 2 3} do_test vtab1-6-4 { execsql { UPDATE techo SET a = 5; SELECT * FROM techo; } } {5 2 3} do_test vtab1-6-5 { execsql { UPDATE techo set a = a||b||c; SELECT * FROM techo; } } {523 2 3} do_test vtab1-6-6 { execsql { UPDATE techo set rowid = 10; SELECT rowid FROM techo; } } {10} do_test vtab1-6-7 { execsql { DELETE FROM techo; SELECT * FROM techo; } } {} file delete -force test2.db file delete -force test2.db-journal sqlite3 db2 test2.db execsql { CREATE TABLE techo(a PRIMARY KEY, b, c); } db2 proc check_echo_table {tn} { set ::data1 [execsql {SELECT rowid, * FROM techo}] set ::data2 [execsql {SELECT rowid, * FROM techo} db2] do_test $tn { string equal $::data1 $::data2 } 1 } set tn 0 foreach stmt [list \ {INSERT INTO techo VALUES('abc', 'def', 'ghi')} \ {INSERT INTO techo SELECT a||'.'||rowid, b, c FROM techo} \ {INSERT INTO techo SELECT a||'x'||rowid, b, c FROM techo} \ {INSERT INTO techo SELECT a||'y'||rowid, b, c FROM techo} \ {DELETE FROM techo WHERE (oid % 3) = 0} \ {UPDATE techo set rowid = 100 WHERE rowid = 1} \ {INSERT INTO techo(a, b) VALUES('hello', 'world')} \ {DELETE FROM techo} \ ] { execsql $stmt execsql $stmt db2 check_echo_table vtab1-6.8.[incr tn] } db2 close #---------------------------------------------------------------------- # Test cases vtab1-7 tests that the value returned by # sqlite3_last_insert_rowid() is set correctly when rows are inserted # into virtual tables. do_test vtab1.7-1 { execsql { CREATE TABLE real_abc(a PRIMARY KEY, b, c); CREATE VIRTUAL TABLE echo_abc USING echo(real_abc); } } {} do_test vtab1.7-2 { execsql { INSERT INTO echo_abc VALUES(1, 2, 3); SELECT last_insert_rowid(); } } {1} do_test vtab1.7-3 { execsql { INSERT INTO echo_abc(rowid) VALUES(31427); SELECT last_insert_rowid(); } } {31427} do_test vtab1.7-4 { execsql { INSERT INTO echo_abc SELECT a||'.v2', b, c FROM echo_abc; SELECT last_insert_rowid(); } } {31429} do_test vtab1.7-5 { execsql { SELECT rowid, a, b, c FROM echo_abc } } [list 1 1 2 3 \ 31427 {} {} {} \ 31428 1.v2 2 3 \ 31429 {} {} {} \ ] # Now test that DELETE and UPDATE operations do not modify the value. do_test vtab1.7-6 { execsql { UPDATE echo_abc SET c = 5 WHERE b = 2; SELECT last_insert_rowid(); } } {31429} do_test vtab1.7-7 { execsql { UPDATE echo_abc SET rowid = 5 WHERE rowid = 1; SELECT last_insert_rowid(); } } {31429} do_test vtab1.7-8 { execsql { DELETE FROM echo_abc WHERE b = 2; SELECT last_insert_rowid(); } } {31429} do_test vtab1.7-9 { execsql { SELECT rowid, a, b, c FROM echo_abc } } [list 31427 {} {} {} \ 31429 {} {} {} \ ] do_test vtab1.7-10 { execsql { DELETE FROM echo_abc WHERE b = 2; SELECT last_insert_rowid(); } } {31429} do_test vtab1.7-11 { execsql { SELECT rowid, a, b, c FROM real_abc } } [list 31427 {} {} {} \ 31429 {} {} {} \ ] do_test vtab1.7-12 { execsql { DELETE FROM echo_abc; SELECT last_insert_rowid(); } } {31429} do_test vtab1.7-13 { execsql { SELECT rowid, a, b, c FROM real_abc } } {} do_test vtab1.8-1 { set echo_module "" execsql { ATTACH 'test2.db' AS aux; CREATE VIRTUAL TABLE aux.e2 USING echo(real_abc); } set echo_module } [list xCreate echo aux e2 real_abc \ xSync echo(real_abc) \ xCommit echo(real_abc) \ ] finish_test