summaryrefslogtreecommitdiffstats
path: root/test/bestindex8.test
diff options
context:
space:
mode:
Diffstat (limited to 'test/bestindex8.test')
-rw-r--r--test/bestindex8.test463
1 files changed, 463 insertions, 0 deletions
diff --git a/test/bestindex8.test b/test/bestindex8.test
new file mode 100644
index 0000000..e95c3c6
--- /dev/null
+++ b/test/bestindex8.test
@@ -0,0 +1,463 @@
+# 2020-01-29
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix bestindex8
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+register_tcl_module db
+
+proc vtab_command {src method args} {
+ switch -- $method {
+ xConnect {
+ return "CREATE TABLE xxx(a, b)"
+ }
+
+ xBestIndex {
+ set hdl [lindex $args 0]
+ set clist [$hdl constraints]
+ set orderby [$hdl orderby]
+ lappend ::lBestIndexDistinct [$hdl distinct]
+
+ #puts "ORDERBY: $orderby"
+ set iCons 0
+ set ret [list]
+ foreach cons $clist {
+ catch { array unset C }
+ array set C $cons
+ if {$C(usable)} {
+ lappend ret use $iCons
+ }
+ incr iCons
+ }
+ if {$orderby=="{column 0 desc 0} {column 1 desc 0}"
+ || $orderby=="{column 0 desc 0}"
+ } {
+ lappend ret orderby 1
+ lappend ret idxnum 1
+ set ::lOrderByConsumed 1
+ }
+ return $ret
+ }
+
+ xFilter {
+ set idxnum [lindex $args 0]
+ if {$idxnum} {
+ return [list sql "SELECT rowid, a, b FROM $src order by 2, 3"]
+ }
+ return [list sql "SELECT rowid, a, b FROM $src"]
+ }
+
+ }
+
+ return {}
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a, b);
+ INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
+ INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
+ CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+
+ CREATE TABLE t0(c0);
+ INSERT INTO t0(c0) VALUES (1), (0);
+}
+
+foreach {tn sql bDistinct idxinsert bConsumed res} {
+ 1 "SELECT a, b FROM vt1" 0 0 0 {a b c d a b c d}
+ 2 "SELECT DISTINCT a, b FROM vt1" 2 1 1 {a b c d}
+ 3 "SELECT DISTINCT a FROM vt1" 2 1 1 {a c}
+ 4 "SELECT DISTINCT b FROM vt1" 2 1 0 {b d}
+ 5 "SELECT DISTINCT b FROM vt1 ORDER BY a" 0 1 1 {b d}
+ 6 "SELECT DISTINCT t0.c0 FROM vt1, t0 ORDER BY vt1.a" 0 1 1 {1 0}
+ 7 "SELECT DISTINCT a, b FROM vt1 ORDER BY a, b" 3 0 1 {a b c d}
+ 8 "SELECT DISTINCT a, b FROM vt1 ORDER BY a" 0 1 1 {a b c d}
+ 9 "SELECT DISTINCT a FROM vt1 ORDER BY a, b" 0 1 1 {a c}
+
+ 10 "SELECT DISTINCT a, b FROM vt1 WHERE b='b'" 2 1 1 {a b}
+ 11 "SELECT DISTINCT a, b FROM vt1 WHERE +b='b'" 2 1 1 {a b}
+} {
+ set ::lBestIndexDistinct ""
+ set ::lOrderByConsumed 0
+ do_execsql_test 1.$tn.1 $sql $res
+ do_test 1.$tn.2 {
+ set ::lBestIndexDistinct
+ } $bDistinct
+ do_test 1.$tn.3 {
+ expr {[lsearch [execsql "explain $sql"] IdxInsert]>=0}
+ } $idxinsert
+ do_test 1.$tn.4 {
+ set ::lOrderByConsumed
+ } $bConsumed
+}
+
+#-------------------------------------------------------------------------
+reset_db
+register_tcl_module db
+
+proc vtab_command {src method args} {
+ switch -- $method {
+ xConnect {
+ return "CREATE TABLE xxx(a, b)"
+ }
+
+ xBestIndex {
+ set hdl [lindex $args 0]
+ set ret [list]
+
+ set iCons 0
+ foreach cons [$hdl constraints] {
+ array set C $cons
+ if {($C(op)=="limit" || $C(op)=="offset") && $C(usable)} {
+ lappend ret use $iCons
+ }
+ incr iCons
+ }
+
+ return $ret
+ }
+
+ xFilter {
+ lappend ::lFilterArgs [lindex $args 2]
+ return [list sql "SELECT rowid, a, b FROM $src"]
+ }
+
+ }
+
+ return {}
+}
+
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a, b);
+ CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+}
+
+do_test 2.1 {
+ set ::lFilterArgs [list]
+ execsql { SELECT * FROM vt1 LIMIT 10 }
+ set ::lFilterArgs
+} {10}
+
+do_test 2.2 {
+ set ::lFilterArgs [list]
+ execsql { SELECT * FROM vt1 LIMIT 5 OFFSET 50 }
+ set ::lFilterArgs
+} {{5 50}}
+
+do_test 2.3 {
+ set ::lFilterArgs [list]
+ execsql { SELECT * FROM vt1 ORDER BY a, b LIMIT 1 OFFSET 1 }
+ set ::lFilterArgs
+} {{1 1}}
+
+do_test 2.4 {
+ set ::lFilterArgs [list]
+ execsql { SELECT * FROM vt1 ORDER BY a, +b LIMIT 1 OFFSET 1 }
+ set ::lFilterArgs
+} {{}}
+
+#-------------------------------------------------------------------------
+reset_db
+register_tcl_module db
+
+proc vtab_command {src method args} {
+ switch -- $method {
+ xConnect {
+ return "CREATE TABLE xxx(a, b)"
+ }
+
+ xBestIndex {
+ set hdl [lindex $args 0]
+ set lCons [$hdl constraints]
+
+ set ret [list]
+ for {set i 0} {$i < [llength $lCons]} {incr i} {
+ array set C [lindex $lCons $i]
+ if {$C(usable)} {
+ lappend ret use $i
+ $hdl in $i 1
+ }
+ }
+ return $ret
+ }
+
+ xFilter {
+ set lArg [lindex $args 2]
+ lappend ::lFilterArg {*}$lArg
+ return [list sql "SELECT rowid, a, b FROM $src"]
+ }
+
+ }
+
+ return {}
+}
+
+do_execsql_test 3.0 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a, b);
+ CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+}
+
+foreach {tn sql lfa} {
+ 1 "SELECT * FROM vt1 WHERE b IN (10, 20, 30)" {{10 20 30}}
+ 2 "SELECT * FROM vt1 WHERE b IN ('abc', 'def')" {{abc def}}
+ 3 "SELECT * FROM vt1 WHERE a IS NULL AND b IN ('abc', 'def')" {{} {abc def}}
+ 4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b IN ('abc', 'def')"
+ {{1 2 3} {abc def}}
+
+ 5 "SELECT * FROM vt1
+ WHERE a IN (SELECT 1 UNION SELECT 2) AND b IN ('abc', 'def')"
+ {{1 2} {abc def}}
+
+ 6 "SELECT * FROM vt1
+ WHERE b IN ('abc', 'def') AND a IN (SELECT 1 UNION SELECT 2)"
+ {{abc def} {1 2}}
+} {
+ do_test 3.$tn {
+ set ::lFilterArg [list]
+ execsql $sql
+ set ::lFilterArg
+ } $lfa
+}
+
+#explain_i { SELECT * FROM vt1 WHERE b IN (10, 20, 30) }
+
+#-------------------------------------------------------------------------
+reset_db
+register_tcl_module db
+
+proc vtab_command {src method args} {
+ switch -- $method {
+ xConnect {
+ return "CREATE TABLE xxx(a, b, c)"
+ }
+
+ xBestIndex {
+ set hdl [lindex $args 0]
+ set lCons [$hdl constraints]
+
+ set ret [list]
+ for {set i 0} {$i < [llength $lCons]} {incr i} {
+ lappend ::lBestIndexRhs [$hdl rhs_value $i -]
+ }
+ return $ret
+ }
+
+ xFilter {
+ return [list sql "SELECT rowid, a, b, c FROM $src"]
+ }
+
+ }
+
+ return {}
+}
+
+do_execsql_test 4.0 {
+ CREATE TABLE t1(a, b, c);
+ CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+}
+
+foreach {tn sql lbir} {
+ 1 "SELECT * FROM vt1 WHERE b = 10" {10}
+ 2 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30" {abc 30}
+ 3 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30+2" {abc -}
+ 4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b < 30+2" {- -}
+ 5 "SELECT * FROM vt1 WHERE a IS 111 AND b < 30+2" {111 -}
+} {
+ do_test 4.$tn {
+ set ::lBestIndexRhs [list]
+ execsql $sql
+ set ::lBestIndexRhs
+ } $lbir
+}
+
+#-------------------------------------------------------------------------
+reset_db
+db cache size 0
+register_tcl_module db
+
+set ::vtab_handle_in 1
+proc vtab_command {src method args} {
+ switch -- $method {
+ xConnect {
+ return "CREATE TABLE xxx(a, b, c)"
+ }
+
+ xBestIndex {
+ set lCols [list a b c]
+
+ set hdl [lindex $args 0]
+ set lCons [$hdl constraints]
+ set lOrder [$hdl order]
+
+ set L ""
+ set O ""
+ set W [list]
+ set a 0
+ for {set i 0} {$i < [llength $lCons]} {incr i} {
+ array set C [lindex $lCons $i]
+ if {$C(usable)} {
+ if { $C(op)=="eq" } {
+ set bIn 0
+ if {$::vtab_handle_in} { set bIn [$hdl in $i 1] }
+ if {$bIn} {
+ lappend W "[lindex $lCols $C(column)] IN (%I$a%)"
+ } else {
+ lappend W "[lindex $lCols $C(column)] = %$a%"
+ }
+ lappend ret omit $i
+ }
+ if { $C(op)=="limit" } { set L " LIMIT %$a%" ; lappend ret use $i }
+ if { $C(op)=="offset" } { set O " OFFSET %$a%" ; lappend ret use $i }
+ incr a
+ }
+ }
+
+ set order ""
+ set selectlist "rowid, a, b, c"
+ if {[llength $lOrder]} {
+ array set sl [list]
+ set lO [list]
+ foreach s $lOrder {
+ array set C $s
+ set ad ""
+ if {$C(desc)} { set ad " DESC" }
+ lappend lO "[lindex $lCols $C(column)]$ad"
+ set sl($C(column)) 1
+ }
+ if {[$hdl distinct]==2} {
+ set selectlist "DISTINCT 0"
+ foreach i {0 1 2} {
+ if {[info exists sl($i)]} {
+ append selectlist ", [lindex $lCols $i]"
+ } else {
+ append selectlist ", 0"
+ }
+ }
+ } else {
+ set order " ORDER BY [join $lO ,]"
+ }
+ }
+
+ set where ""
+ if {[llength $W]} { set where " WHERE [join $W { AND }]" }
+ set sql "SELECT $selectlist FROM $src$where$order$L$O"
+
+ lappend ret idxStr $sql
+ return $ret
+ }
+
+ xFilter {
+ foreach {idxnum idxstr lArg} $args {}
+ set ii 0
+ set sql $idxstr
+ foreach a $lArg {
+ set sql [string map [list %$ii% $a] $sql]
+ set sql [string map [list %I$ii% [join $a ,]] $sql]
+ incr ii
+ }
+ lappend ::lFilterSql $sql
+
+ if {[regexp {OFFSET (.*)$} $sql -> off]} {
+ set real_sql "
+ WITH c(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM c WHERE i<$off )
+ SELECT 0,0,0,0 FROM c
+ UNION ALL SELECT * FROM (
+ $sql
+ )
+ "
+ } else {
+ set real_sql $sql
+ }
+
+ return [list sql $real_sql]
+ }
+
+ }
+
+ return {}
+}
+
+do_execsql_test 5.0 {
+ CREATE TABLE t1(a, b, c);
+ CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 VALUES(2, 3, 4);
+ INSERT INTO t1 VALUES(3, 4, 5);
+ INSERT INTO t1 VALUES(1, 5, 6);
+ INSERT INTO t1 VALUES(2, 6, 7);
+ INSERT INTO t1 VALUES(3, 7, 8);
+ INSERT INTO t1 VALUES(1, 8, 9);
+ INSERT INTO t1 VALUES(2, 9, 0);
+}
+
+proc do_vtab_test {tn sql vtsql {res {}}} {
+ set ::lFilterSql [list]
+ uplevel [list do_execsql_test $tn.1 $sql $res]
+ uplevel [list do_test $tn.2 {set ::lFilterSql} [list {*}$vtsql]]
+}
+
+do_vtab_test 5.1.1 {
+ SELECT DISTINCT a FROM vt1
+} {
+ {SELECT DISTINCT 0, a, 0, 0 FROM t1}
+} {1 2 3}
+
+do_vtab_test 5.1.2 {
+ SELECT DISTINCT a FROM vt1 ORDER BY a
+} {
+ {SELECT rowid, a, b, c FROM t1 ORDER BY a}
+} {1 2 3}
+
+do_vtab_test 5.1.3 {
+ SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
+} {
+ {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c IN (4,5,6,7,8)}
+} {2 3 1}
+
+set ::vtab_handle_in 0
+do_vtab_test 5.1.4 {
+ SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
+} {
+ {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 4}
+ {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 5}
+ {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 6}
+ {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 7}
+ {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 8}
+} {2 3 1}
+
+set ::vtab_handle_in 1
+do_vtab_test 5.1.5a {
+ SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
+} {
+ {SELECT rowid, a, b, c FROM t1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2}
+} {1 5 6 2 6 7}
+
+set ::vtab_handle_in 0
+do_vtab_test 5.1.5b {
+ SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
+} {
+ {SELECT rowid, a, b, c FROM t1 WHERE c = 4}
+ {SELECT rowid, a, b, c FROM t1 WHERE c = 5}
+ {SELECT rowid, a, b, c FROM t1 WHERE c = 6}
+ {SELECT rowid, a, b, c FROM t1 WHERE c = 7}
+} {1 5 6 2 6 7}
+set ::vtab_handle_in 1
+
+finish_test