| # 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 |