1# 2016 November 11 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# Test the virtual table interface. In particular the xBestIndex 12# method. 13# 14 15set testdir [file dirname $argv0] 16source $testdir/tester.tcl 17set testprefix bestindex4 18 19ifcapable !vtab { 20 finish_test 21 return 22} 23 24#------------------------------------------------------------------------- 25# Virtual table callback for a virtual table named $tbl. 26# 27# The table created is: 28# 29# "CREATE TABLE t1 (id, host, class)" 30# 31# The virtual table supports == operators on a subset of its columns. The 32# exact subset depends on the value of bitmask paramater $param. 33# 34# 0x01 - == on "id" supported 35# 0x02 - == on "host" supported 36# 0x04 - == on "class" supported 37# 38# $param also supports the following bits: 39# 40# 0x08 - ignore the "usable" flag (malfunction) 41# 42# 43# 44proc vtab_cmd {param method args} { 45 switch -- $method { 46 xConnect { 47 return "CREATE TABLE t1(id TEXT, host TEXT, class TEXT)" 48 } 49 50 xBestIndex { 51 foreach {clist orderby mask} $args {} 52 53 set ret [list] 54 55 set use use 56 57 58 for {set i 0} {$i < [llength $clist]} {incr i} { 59 array unset C 60 array set C [lindex $clist $i] 61 if { ($C(usable) || ($param & 0x08)) 62 && $C(op)=="eq" && ($param & 1<<$C(column)) 63 } { 64 lappend ret $use $i 65 break 66 } 67 } 68 69 set score 1000000 70 if {$ret!=""} { 71 set score [expr $score / [llength $ret]] 72 } 73 lappend ret cost $score rows $score 74 75 return $ret 76 } 77 78 xFilter { 79 } 80 } 81 return "" 82} 83 84register_tcl_module db 85 86for {set param1 0} {$param1<16} {incr param1} { 87 for {set param2 0} {$param2<16} {incr param2} { 88 reset_db 89 register_tcl_module db 90 do_execsql_test 1.$param1.$param2.1 " 91 CREATE VIRTUAL TABLE t1 USING tcl('vtab_cmd $param1'); 92 CREATE VIRTUAL TABLE t2 USING tcl('vtab_cmd $param2'); 93 " 94 95 foreach {tn sql} { 96 2 "select t1.id as ID from t1, t2 where t1.id=t2.host and t2.class='xx'" 97 3 { 98 select t1.id as ID from t1, t2 where t2.class ='xx' and t2.id = t1.host 99 } 100 4 { 101 select t1.id as ID from t1, t2 where t1.host = t2.id and t2. class ='xx' 102 } 103 } { 104 105 if {($param1 & 0x08)==0 && ($param2 & 0x08)==0} { 106 107 do_execsql_test 1.$param1.$param2.$tn.a $sql {} 108 109 } else { 110 do_test 1.$param1.$param2.$tn.b { 111 catchsql $sql 112 set {} {} 113 } {} 114 } 115 } 116 117 } 118} 119 120#------------------------------------------------------------------------- 121# Test that a parameter passed to a table-valued function cannot be 122# used to drive an index. i.e. that in the following: 123# 124# SELECT * FROM tbl, vtab(tbl.x); 125# 126# The implicit constraint "tbl.x = vtab.hidden" is not optimized using 127# an index on tbl.x. 128# 129reset_db 130register_tcl_module db 131proc vtab_command {method args} { 132 switch -- $method { 133 xConnect { 134 return "CREATE TABLE t1(a, b, c, d HIDDEN)" 135 } 136 137 xBestIndex { 138 set clist [lindex $args 0] 139 if {[llength $clist]!=1} { error "unexpected constraint list" } 140 catch { array unset C } 141 array set C [lindex $clist 0] 142 if {$C(usable)} { 143 return [list omit 0 idxnum 555 rows 10 cost 100] 144 } 145 return [list cost 100000000] 146 } 147 148 } 149 150 return {} 151} 152 153do_execsql_test 2.0 { 154 CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); 155 CREATE TABLE t1 (x INT PRIMARY KEY); 156} {} 157 158do_eqp_test 2.1 { 159 SELECT * FROM t1, x1 WHERE x1.d=t1.x; 160} { 161 QUERY PLAN 162 |--SCAN TABLE x1 VIRTUAL TABLE INDEX 0: 163 `--SEARCH TABLE t1 USING COVERING INDEX sqlite_autoindex_t1_1 (x=?) 164} 165 166do_eqp_test 2.2 { 167 SELECT * FROM t1, x1(t1.x) 168} { 169 QUERY PLAN 170 |--SCAN TABLE t1 171 `--SCAN TABLE x1 VIRTUAL TABLE INDEX 555: 172} 173 174 175finish_test 176