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