4c3ab545a9
FossilOrigin-Name: 90e5c8226a695e838e8c1703a9b8598e654d216799e8806c4d1a1f20c28c6486
464 lines
11 KiB
Plaintext
464 lines
11 KiB
Plaintext
# 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
|
|
} {{50 5}}
|
|
|
|
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
|