Prevent deep recursions on nested COLLATE operators.
[sqlite.git] / test / bestindex1.test
blob5857e7f002c5f8f6535d6eea6b18aba9759f8094
1 # 2016-03-01
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
11
14 set testdir [file dirname $argv0]
15 source $testdir/tester.tcl
16 set testprefix bestindex1
18 ifcapable !vtab {
19   finish_test
20   return
23 register_tcl_module db
25 proc vtab_command {method args} {
26   switch -- $method {
27     xConnect {
28       return "CREATE TABLE t1(a, b, c)"
29     }
31     xBestIndex {
32       set clist [lindex $args 0]
33       if {[llength $clist]!=1} { error "unexpected constraint list" }
34       catch { array unset C }
35       array set C [lindex $clist 0]
36       if {$C(usable)} {
37         return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!"
38       } else {
39         return "cost 1000000 rows 0 idxnum 0 idxstr scan..."
40       }
41     }
43   }
45   return {}
48 do_execsql_test 1.0 {
49   CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
50 } {}
52 do_eqp_test 1.1 {
53   SELECT * FROM x1 WHERE a = 'abc'
54 } {
55   0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
58 do_eqp_test 1.2 {
59   SELECT * FROM x1 WHERE a IN ('abc', 'def');
60 } {
61   0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
64 #-------------------------------------------------------------------------
66 reset_db
67 register_tcl_module db
69 # Parameter $mode may be one of:
71 #   "omit" - Implement filtering. Set the omit flag.
72 #   "use"  - Implement filtering. Use the constraint, but do not set omit.
73 #   "use2" - Do not implement filtering. Use the constraint anyway.
75 #   
76 proc t1_vtab {mode method args} {
77   switch -- $method {
78     xConnect {
79       return "CREATE TABLE t1(a, b)"
80     }
82     xBestIndex {
83       set SQL_FILTER {SELECT * FROM t1x WHERE a='%1%'}
84       set SQL_SCAN   {SELECT * FROM t1x}
86       set clist [lindex $args 0]
87       set idx 0
88       for {set idx 0} {$idx < [llength $clist]} {incr idx} {
89         array unset C
90         array set C [lindex $clist $idx]
91         if {$C(column)==0 && $C(op)=="eq" && $C(usable)} {
92           switch -- $mode {
93             "omit" {
94               return [list omit $idx rows 10 cost 10 idxstr $SQL_FILTER]
95             }
96             "use" {
97               return [list use $idx rows 10 cost 10 idxstr $SQL_FILTER]
98             }
99             "use2" {
100               return [list use $idx rows 10 cost 10 idxstr $SQL_SCAN]
101             }
102             default {
103               error "Bad mode - $mode"
104             }
105           }
106         }
107       }
109       return [list idxstr {SELECT * FROM t1x}]
110     }
112     xFilter {
113       set map [list %1% [lindex $args 2 0]]
114       set sql [string map $map [lindex $args 1]]
115       return [list sql $sql]
116     }
117   }
119   return {}
122 do_execsql_test 2.1 {
123   CREATE TABLE t1x(i INTEGER PRIMARY KEY, a, b);
124   INSERT INTO t1x VALUES(1, 'one', 1);
125   INSERT INTO t1x VALUES(2, 'two', 2);
126   INSERT INTO t1x VALUES(3, 'three', 3);
127   INSERT INTO t1x VALUES(4, 'four', 4);
130 foreach {tn mode} {
131   1 use 2 omit 3 use2
132 } {
133   do_execsql_test 2.2.$mode.1 "
134     DROP TABLE IF EXISTS t1;
135     CREATE VIRTUAL TABLE t1 USING tcl(t1_vtab $mode);
136   "
138   do_execsql_test 2.2.$mode.2 {SELECT * FROM t1} {one 1 two 2 three 3 four 4}
139   do_execsql_test 2.2.$mode.3 {SELECT rowid FROM t1} {1 2 3 4}
140   do_execsql_test 2.2.$mode.4 {SELECT rowid FROM t1 WHERE a='two'} {2} 
142   do_execsql_test 2.2.$mode.5 {
143     SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid
144   } {1 4} 
146   set plan(use) {
147     0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'}
148     0 0 0 {USE TEMP B-TREE FOR ORDER BY}
149   }
150   set plan(omit) {
151     0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'}
152     0 0 0 {USE TEMP B-TREE FOR ORDER BY}
153   }
154   set plan(use2) {
155     0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x}
156     0 0 0 {USE TEMP B-TREE FOR ORDER BY}
157   }
159   do_eqp_test 2.2.$mode.6 { 
160     SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid
161   } $plan($mode)
164 # 2016-04-09.
165 # Demonstrate a register overwrite problem when using two virtual
166 # tables where the outer loop uses the IN operator.
168 set G(collist) [list PrimaryKey flagA columnA]
169 set G(cols) [join $G(collist) ,]
170 set G(nulls) "NULL"
172 proc vtab_command {method args} {
173   global G
175   switch -- $method {
176     xConnect {
177       return "CREATE TABLE t1($G(cols))"
178     }
180     xBestIndex {
181       set clist [lindex $args 0]
182       #puts $clist
183       set W [list]
184       set U [list]
186       set i 0
187       for {set idx 0} {$idx < [llength $clist]} {incr idx} {
188         array set c [lindex $clist $idx]
189         if {$c(op)=="eq" && $c(usable)} {
190           lappend W "[lindex $G(collist) $c(column)] = %$i%"
191           lappend U use $idx
192           incr i
193         }
194       }
196       if {$W==""} {
197         set sql "SELECT rowid, * FROM t1"
198       } else {
199         set sql "SELECT rowid, * FROM t1 WHERE [join $W { AND }]"
200       }
202       return [concat [list idxstr $sql] $U]
203     }
205     xFilter {
206       foreach {idxnum idxstr vals} $args {}
208       set map [list]
209       for {set i 0} {$i < [llength $vals]} {incr i} {
210         lappend map "%$i%" 
211         set v [lindex $vals $i]
212         if {[string is integer $v]} { 
213           lappend map $v 
214         } else {
215           lappend map "'$v'"
216         }
217       }
218       set sql [string map $map $idxstr]
220       #puts "SQL: $sql"
221       return [list sql $sql]
222     }
223   }
225   return {}
228 db close
229 forcedelete test.db
230 sqlite3 db test.db
231 register_tcl_module db
233 do_execsql_test 3.1 "
234   CREATE TABLE t1($G(cols));
235   INSERT INTO t1 VALUES(1, 0, 'ValueA');
236   INSERT INTO t1 VALUES(2, 0, 'ValueA');
237   INSERT INTO t1 VALUES(3, 0, 'ValueB');
238   INSERT INTO t1 VALUES(4, 0, 'ValueB');
241 do_execsql_test 3.2 {
242   CREATE VIRTUAL TABLE VirtualTableA USING tcl(vtab_command);
243   CREATE VIRTUAL TABLE VirtualTableB USING tcl(vtab_command);
246 do_execsql_test 3.3 { SELECT primarykey FROM VirtualTableA } {1 2 3 4}
248 do_execsql_test 3.4 {
249   SELECT * FROM 
250   VirtualTableA a CROSS JOIN VirtualTableB b ON b.PrimaryKey=a.PrimaryKey
251   WHERE a.ColumnA IN ('ValueA', 'ValueB') AND a.FlagA=0
252 } {
253   1 0 ValueA 1 0 ValueA
254   2 0 ValueA 2 0 ValueA
255   3 0 ValueB 3 0 ValueB
256   4 0 ValueB 4 0 ValueB
259 do_execsql_test 3.5 {
260   SELECT * FROM 
261   VirtualTableA a CROSS JOIN VirtualTableB b ON b.PrimaryKey=a.PrimaryKey
262   WHERE a.FlagA=0 AND a.ColumnA IN ('ValueA', 'ValueB') 
263 } {
264   1 0 ValueA 1 0 ValueA
265   2 0 ValueA 2 0 ValueA
266   3 0 ValueB 3 0 ValueB
267   4 0 ValueB 4 0 ValueB
271 finish_test