xref: /sqlite-3.40.0/test/bestindex1.test (revision 8349c11d)
1# 2016-03-01
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#
12#
13
14set testdir [file dirname $argv0]
15source $testdir/tester.tcl
16set testprefix bestindex1
17
18ifcapable !vtab {
19  finish_test
20  return
21}
22
23register_tcl_module db
24
25proc vtab_command {method args} {
26  switch -- $method {
27    xConnect {
28      return "CREATE TABLE t1(a, b, c)"
29    }
30
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    }
42
43  }
44
45  return {}
46}
47
48do_execsql_test 1.0 {
49  CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
50} {}
51
52do_eqp_test 1.1 {
53  SELECT * FROM x1 WHERE a = 'abc'
54} {
55  0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
56}
57
58do_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!}
62}
63
64#-------------------------------------------------------------------------
65#
66reset_db
67register_tcl_module db
68
69# Parameter $mode may be one of:
70#
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.
74#
75#
76proc t1_vtab {mode method args} {
77  switch -- $method {
78    xConnect {
79      return "CREATE TABLE t1(a, b)"
80    }
81
82    xBestIndex {
83      set SQL_FILTER {SELECT * FROM t1x WHERE a='%1%'}
84      set SQL_SCAN   {SELECT * FROM t1x}
85
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      }
108
109      return [list idxstr {SELECT * FROM t1x}]
110    }
111
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  }
118
119  return {}
120}
121
122do_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);
128}
129
130foreach {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  "
137
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}
141
142  do_execsql_test 2.2.$mode.5 {
143    SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid
144  } {1 4}
145
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  }
158
159  do_eqp_test 2.2.$mode.6 {
160    SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid
161  } $plan($mode)
162}
163
164# 2016-04-09.
165# Demonstrate a register overwrite problem when using two virtual
166# tables where the outer loop uses the IN operator.
167#
168set G(collist) [list PrimaryKey flagA columnA]
169set G(cols) [join $G(collist) ,]
170set G(nulls) "NULL"
171
172proc vtab_command {method args} {
173  global G
174
175  switch -- $method {
176    xConnect {
177      return "CREATE TABLE t1($G(cols))"
178    }
179
180    xBestIndex {
181      set clist [lindex $args 0]
182      #puts $clist
183      set W [list]
184      set U [list]
185
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      }
195
196      if {$W==""} {
197        set sql "SELECT rowid, * FROM t1"
198      } else {
199        set sql "SELECT rowid, * FROM t1 WHERE [join $W { AND }]"
200      }
201
202      return [concat [list idxstr $sql] $U]
203    }
204
205    xFilter {
206      foreach {idxnum idxstr vals} $args {}
207
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]
219
220      #puts "SQL: $sql"
221      return [list sql $sql]
222    }
223  }
224
225  return {}
226}
227
228db close
229forcedelete test.db
230sqlite3 db test.db
231register_tcl_module db
232
233do_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');
239"
240
241do_execsql_test 3.2 {
242  CREATE VIRTUAL TABLE VirtualTableA USING tcl(vtab_command);
243  CREATE VIRTUAL TABLE VirtualTableB USING tcl(vtab_command);
244}
245
246do_execsql_test 3.3 { SELECT primarykey FROM VirtualTableA } {1 2 3 4}
247
248do_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
257}
258
259do_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
268}
269
270
271finish_test
272