xref: /sqlite-3.40.0/test/bestindex8.test (revision 247d0946)
1# 2020-01-29
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
13set testdir [file dirname $argv0]
14source $testdir/tester.tcl
15set testprefix bestindex8
16
17ifcapable !vtab {
18  finish_test
19  return
20}
21
22register_tcl_module db
23
24proc vtab_command {src method args} {
25  switch -- $method {
26    xConnect {
27      return "CREATE TABLE xxx(a, b)"
28    }
29
30    xBestIndex {
31      set hdl [lindex $args 0]
32      set clist [$hdl constraints]
33      set orderby [$hdl orderby]
34      lappend ::lBestIndexDistinct [$hdl distinct]
35
36      #puts "ORDERBY: $orderby"
37      set iCons 0
38      set ret [list]
39      foreach cons $clist {
40        catch { array unset C }
41        array set C $cons
42        if {$C(usable)} {
43          lappend ret use $iCons
44        }
45        incr iCons
46      }
47      if {$orderby=="{column 0 desc 0} {column 1 desc 0}"
48       || $orderby=="{column 0 desc 0}"
49      } {
50        lappend ret orderby 1
51        lappend ret idxnum 1
52        set ::lOrderByConsumed 1
53      }
54      return $ret
55    }
56
57    xFilter {
58      set idxnum [lindex $args 0]
59      if {$idxnum} {
60        return [list sql "SELECT rowid, a, b FROM $src order by 2, 3"]
61      }
62      return [list sql "SELECT rowid, a, b FROM $src"]
63    }
64
65  }
66
67  return {}
68}
69
70do_execsql_test 1.0 {
71  CREATE TABLE t1(a, b);
72  CREATE INDEX i1 ON t1(a, b);
73  INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
74  INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
75  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
76
77  CREATE TABLE t0(c0);
78  INSERT INTO t0(c0) VALUES (1), (0);
79}
80
81foreach {tn sql bDistinct idxinsert bConsumed res} {
82  1 "SELECT a, b FROM vt1"                              0 0 0 {a b c d a b c d}
83  2 "SELECT DISTINCT a, b FROM vt1"                     2 1 1 {a b c d}
84  3 "SELECT DISTINCT a FROM vt1"                        2 1 1 {a c}
85  4 "SELECT DISTINCT b FROM vt1"                        2 1 0 {b d}
86  5 "SELECT DISTINCT b FROM vt1 ORDER BY a"             0 1 1 {b d}
87  6 "SELECT DISTINCT t0.c0 FROM vt1, t0 ORDER BY vt1.a" 0 1 1 {1 0}
88  7 "SELECT DISTINCT a, b FROM vt1 ORDER BY a, b"       3 0 1 {a b c d}
89  8 "SELECT DISTINCT a, b FROM vt1 ORDER BY a"          0 1 1 {a b c d}
90  9 "SELECT DISTINCT a FROM vt1 ORDER BY a, b"          0 1 1 {a c}
91
92 10 "SELECT DISTINCT a, b FROM vt1 WHERE b='b'"         2 1 1 {a b}
93 11 "SELECT DISTINCT a, b FROM vt1 WHERE +b='b'"        2 1 1 {a b}
94} {
95  set ::lBestIndexDistinct ""
96  set ::lOrderByConsumed 0
97  do_execsql_test 1.$tn.1 $sql $res
98  do_test 1.$tn.2 {
99    set ::lBestIndexDistinct
100  } $bDistinct
101  do_test 1.$tn.3 {
102    expr {[lsearch [execsql "explain $sql"] IdxInsert]>=0}
103  } $idxinsert
104  do_test 1.$tn.4 {
105    set ::lOrderByConsumed
106  } $bConsumed
107}
108
109#-------------------------------------------------------------------------
110reset_db
111register_tcl_module db
112
113proc vtab_command {src method args} {
114  switch -- $method {
115    xConnect {
116      return "CREATE TABLE xxx(a, b)"
117    }
118
119    xBestIndex {
120      set hdl [lindex $args 0]
121      set ret [list]
122
123      set iCons 0
124      foreach cons [$hdl constraints] {
125        array set C $cons
126        if {($C(op)=="limit" || $C(op)=="offset") && $C(usable)} {
127          lappend ret use $iCons
128        }
129        incr iCons
130      }
131
132      return $ret
133    }
134
135    xFilter {
136      lappend ::lFilterArgs [lindex $args 2]
137      return [list sql "SELECT rowid, a, b FROM $src"]
138    }
139
140  }
141
142  return {}
143}
144
145do_execsql_test 2.0 {
146  CREATE TABLE t1(a, b);
147  CREATE INDEX i1 ON t1(a, b);
148  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
149}
150
151do_test 2.1 {
152  set ::lFilterArgs [list]
153  execsql { SELECT * FROM vt1 LIMIT 10 }
154  set ::lFilterArgs
155} {10}
156
157do_test 2.2 {
158  set ::lFilterArgs [list]
159  execsql { SELECT * FROM vt1 LIMIT 5 OFFSET 50 }
160  set ::lFilterArgs
161} {{5 50}}
162
163do_test 2.3 {
164  set ::lFilterArgs [list]
165  execsql { SELECT * FROM vt1 ORDER BY a, b LIMIT 1 OFFSET 1 }
166  set ::lFilterArgs
167} {{1 1}}
168
169do_test 2.4 {
170  set ::lFilterArgs [list]
171  execsql { SELECT * FROM vt1 ORDER BY a, +b LIMIT 1 OFFSET 1 }
172  set ::lFilterArgs
173} {{}}
174
175#-------------------------------------------------------------------------
176reset_db
177register_tcl_module db
178
179proc vtab_command {src method args} {
180  switch -- $method {
181    xConnect {
182      return "CREATE TABLE xxx(a, b)"
183    }
184
185    xBestIndex {
186      set hdl [lindex $args 0]
187      set lCons [$hdl constraints]
188
189      set ret [list]
190      for {set i 0} {$i < [llength $lCons]} {incr i} {
191        array set C [lindex $lCons $i]
192        if {$C(usable)} {
193          lappend ret use $i
194          $hdl in $i 1
195        }
196      }
197      return $ret
198    }
199
200    xFilter {
201      set lArg [lindex $args 2]
202      lappend ::lFilterArg {*}$lArg
203      return [list sql "SELECT rowid, a, b FROM $src"]
204    }
205
206  }
207
208  return {}
209}
210
211do_execsql_test 3.0 {
212  CREATE TABLE t1(a, b);
213  CREATE INDEX i1 ON t1(a, b);
214  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
215}
216
217foreach {tn sql lfa} {
218  1 "SELECT * FROM vt1 WHERE b IN (10, 20, 30)" {{10 20 30}}
219  2 "SELECT * FROM vt1 WHERE b IN ('abc', 'def')" {{abc def}}
220  3 "SELECT * FROM vt1 WHERE a IS NULL AND b IN ('abc', 'def')" {{} {abc def}}
221  4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b IN ('abc', 'def')"
222     {{1 2 3} {abc def}}
223
224  5 "SELECT * FROM vt1
225     WHERE a IN (SELECT 1 UNION SELECT 2) AND b IN ('abc', 'def')"
226     {{1 2} {abc def}}
227
228  6 "SELECT * FROM vt1
229     WHERE b IN ('abc', 'def') AND a IN (SELECT 1 UNION SELECT 2)"
230     {{abc def} {1 2}}
231} {
232  do_test 3.$tn {
233    set ::lFilterArg [list]
234    execsql $sql
235    set ::lFilterArg
236  } $lfa
237}
238
239#explain_i { SELECT * FROM vt1 WHERE b IN (10, 20, 30) }
240
241#-------------------------------------------------------------------------
242reset_db
243register_tcl_module db
244
245proc vtab_command {src method args} {
246  switch -- $method {
247    xConnect {
248      return "CREATE TABLE xxx(a, b, c)"
249    }
250
251    xBestIndex {
252      set hdl [lindex $args 0]
253      set lCons [$hdl constraints]
254
255      set ret [list]
256      for {set i 0} {$i < [llength $lCons]} {incr i} {
257        lappend ::lBestIndexRhs [$hdl rhs_value $i -]
258      }
259      return $ret
260    }
261
262    xFilter {
263      return [list sql "SELECT rowid, a, b, c FROM $src"]
264    }
265
266  }
267
268  return {}
269}
270
271do_execsql_test 4.0 {
272  CREATE TABLE t1(a, b, c);
273  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
274}
275
276foreach {tn sql lbir} {
277  1 "SELECT * FROM vt1 WHERE b = 10" {10}
278  2 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30" {abc 30}
279  3 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30+2" {abc -}
280  4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b < 30+2" {- -}
281  5 "SELECT * FROM vt1 WHERE a IS 111 AND b < 30+2" {111 -}
282} {
283  do_test 4.$tn {
284    set ::lBestIndexRhs [list]
285    execsql $sql
286    set ::lBestIndexRhs
287  } $lbir
288}
289
290#-------------------------------------------------------------------------
291reset_db
292db cache size 0
293register_tcl_module db
294
295set ::vtab_handle_in 1
296proc vtab_command {src method args} {
297  switch -- $method {
298    xConnect {
299      return "CREATE TABLE xxx(a, b, c)"
300    }
301
302    xBestIndex {
303      set lCols [list a b c]
304
305      set hdl [lindex $args 0]
306      set lCons [$hdl constraints]
307      set lOrder [$hdl order]
308
309      set L ""
310      set O ""
311      set W [list]
312      set a 0
313      for {set i 0} {$i < [llength $lCons]} {incr i} {
314        array set C [lindex $lCons $i]
315        if {$C(usable)} {
316          if { $C(op)=="eq" } {
317            set bIn 0
318            if {$::vtab_handle_in} { set bIn [$hdl in $i 1] }
319            if {$bIn} {
320              lappend W "[lindex $lCols $C(column)] IN (%I$a%)"
321            } else {
322              lappend W "[lindex $lCols $C(column)] = %$a%"
323            }
324            lappend ret omit $i
325          }
326          if { $C(op)=="limit"  } { set L " LIMIT %$a%"  ; lappend ret use $i }
327          if { $C(op)=="offset" } { set O " OFFSET %$a%" ; lappend ret use $i }
328          incr a
329        }
330      }
331
332      set order ""
333      set selectlist "rowid, a, b, c"
334      if {[llength $lOrder]} {
335        array set sl [list]
336        set lO [list]
337        foreach s $lOrder {
338          array set C $s
339          set ad ""
340          if {$C(desc)} { set ad " DESC" }
341          lappend lO "[lindex $lCols $C(column)]$ad"
342          set sl($C(column)) 1
343        }
344        if {[$hdl distinct]==2} {
345          set selectlist "DISTINCT 0"
346          foreach i {0 1 2} {
347            if {[info exists sl($i)]} {
348              append selectlist ", [lindex $lCols $i]"
349            } else {
350              append selectlist ", 0"
351            }
352          }
353        } else {
354          set order " ORDER BY [join $lO ,]"
355        }
356      }
357
358      set where ""
359      if {[llength $W]} { set where " WHERE [join $W { AND }]" }
360      set sql "SELECT $selectlist FROM $src$where$order$L$O"
361
362      lappend ret idxStr $sql
363      return $ret
364    }
365
366    xFilter {
367      foreach {idxnum idxstr lArg} $args {}
368      set ii 0
369      set sql $idxstr
370      foreach a $lArg {
371        set sql [string map [list %$ii% $a] $sql]
372        set sql [string map [list %I$ii% [join $a ,]] $sql]
373        incr ii
374      }
375      lappend ::lFilterSql $sql
376
377      if {[regexp {OFFSET (.*)$} $sql -> off]} {
378        set real_sql "
379          WITH c(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM c WHERE i<$off )
380          SELECT 0,0,0,0 FROM c
381          UNION ALL SELECT * FROM (
382            $sql
383          )
384        "
385      } else {
386        set real_sql $sql
387      }
388
389      return [list sql $real_sql]
390    }
391
392  }
393
394  return {}
395}
396
397do_execsql_test 5.0 {
398  CREATE TABLE t1(a, b, c);
399  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
400  INSERT INTO t1 VALUES(1, 2, 3);
401  INSERT INTO t1 VALUES(2, 3, 4);
402  INSERT INTO t1 VALUES(3, 4, 5);
403  INSERT INTO t1 VALUES(1, 5, 6);
404  INSERT INTO t1 VALUES(2, 6, 7);
405  INSERT INTO t1 VALUES(3, 7, 8);
406  INSERT INTO t1 VALUES(1, 8, 9);
407  INSERT INTO t1 VALUES(2, 9, 0);
408}
409
410proc do_vtab_test {tn sql vtsql {res {}}} {
411  set ::lFilterSql [list]
412  uplevel [list do_execsql_test $tn.1 $sql $res]
413  uplevel [list do_test $tn.2 {set ::lFilterSql} [list {*}$vtsql]]
414}
415
416do_vtab_test 5.1.1 {
417  SELECT DISTINCT a FROM vt1
418} {
419  {SELECT DISTINCT 0, a, 0, 0 FROM t1}
420} {1 2 3}
421
422do_vtab_test 5.1.2 {
423  SELECT DISTINCT a FROM vt1 ORDER BY a
424} {
425  {SELECT rowid, a, b, c FROM t1 ORDER BY a}
426} {1 2 3}
427
428do_vtab_test 5.1.3 {
429  SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
430} {
431  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c IN (4,5,6,7,8)}
432} {2 3 1}
433
434set ::vtab_handle_in 0
435do_vtab_test 5.1.4 {
436  SELECT  DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
437} {
438  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 4}
439  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 5}
440  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 6}
441  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 7}
442  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 8}
443} {2 3 1}
444
445set ::vtab_handle_in 1
446do_vtab_test 5.1.5a {
447  SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
448} {
449  {SELECT rowid, a, b, c FROM t1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2}
450} {1 5 6 2 6 7}
451
452set ::vtab_handle_in 0
453do_vtab_test 5.1.5b {
454  SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
455} {
456  {SELECT rowid, a, b, c FROM t1 WHERE c = 4}
457  {SELECT rowid, a, b, c FROM t1 WHERE c = 5}
458  {SELECT rowid, a, b, c FROM t1 WHERE c = 6}
459  {SELECT rowid, a, b, c FROM t1 WHERE c = 7}
460} {1 5 6 2 6 7}
461set ::vtab_handle_in 1
462
463finish_test
464