CoolFace
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
bestindex1.test345 linesDownload Raw Back to test
1# 2016-03-012#3# The author disclaims copyright to this source code.  In place of4# 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#13 14set testdir [file dirname $argv0]15source $testdir/tester.tcl16set testprefix bestindex117 18ifcapable !vtab {19  finish_test20  return21}22 23register_tcl_module db24 25proc vtab_command {method args} {26  switch -- $method {27    xConnect {28      return "CREATE TABLE t1(a, b, c)"29    }30 31    xBestIndex {32      set hdl [lindex $args 0]33      set clist [$hdl constraints]34      set orderby [$hdl orderby]35      36      if {[llength $clist]!=1} { error "unexpected constraint list" }37      catch { array unset C }38      array set C [lindex $clist 0]39      if {$C(usable)} {40        return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!"41      } else {42        return "cost 1000000 rows 0 idxnum 0 idxstr scan..."43      }44    }45 46  }47 48  return {}49}50 51do_execsql_test 1.0 {52  CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);53} {}54 55do_eqp_test 1.1 {56  SELECT * FROM x1 WHERE a = 'abc'57} {SCAN x1 VIRTUAL TABLE INDEX 555:eq!}58 59do_eqp_test 1.2 {60  SELECT * FROM x1 WHERE a IN ('abc', 'def');61} {SCAN x1 VIRTUAL TABLE INDEX 555:eq!}62 63#-------------------------------------------------------------------------64#65reset_db66register_tcl_module db67 68# Parameter $mode may be one of:69#70#   "omit" - Implement filtering. Set the omit flag.71#   "use"  - Implement filtering. Use the constraint, but do not set omit.72#   "use2" - Do not implement filtering. Use the constraint anyway.73#74#   75proc t1_vtab {mode method args} {76  switch -- $method {77    xConnect {78      return "CREATE TABLE t1(a, b)"79    }80 81    xBestIndex {82      set hdl [lindex $args 0]83      set clist [$hdl constraints]84      set orderby [$hdl orderby]85 86      set SQL_FILTER {SELECT * FROM t1x WHERE a='%1%'}87      set SQL_SCAN   {SELECT * FROM t1x}88 89      set idx 090      for {set idx 0} {$idx < [llength $clist]} {incr idx} {91        array unset C92        array set C [lindex $clist $idx]93        if {$C(column)==0 && $C(op)=="eq" && $C(usable)} {94          switch -- $mode {95            "omit" {96              return [list omit $idx rows 10 cost 10 idxstr $SQL_FILTER]97            }98            "use" {99              return [list use $idx rows 10 cost 10 idxstr $SQL_FILTER]100            }101            "use2" {102              return [list use $idx rows 10 cost 10 idxstr $SQL_SCAN]103            }104            default {105              error "Bad mode - $mode"106            }107          }108        }109      }110 111      return [list idxstr {SELECT * FROM t1x}]112    }113 114    xFilter {115      set map [list %1% [lindex $args 2 0]]116      set sql [string map $map [lindex $args 1]]117      return [list sql $sql]118    }119  }120 121  return {}122}123 124do_execsql_test 2.1 {125  CREATE TABLE t1x(i INTEGER PRIMARY KEY, a, b);126  INSERT INTO t1x VALUES(1, 'one', 1);127  INSERT INTO t1x VALUES(2, 'two', 2);128  INSERT INTO t1x VALUES(3, 'three', 3);129  INSERT INTO t1x VALUES(4, 'four', 4);130}131 132foreach {tn mode} {133  1 use 2 omit 3 use2134} {135  do_execsql_test 2.2.$mode.1 "136    DROP TABLE IF EXISTS t1;137    CREATE VIRTUAL TABLE t1 USING tcl(t1_vtab $mode);138  "139 140  do_execsql_test 2.2.$mode.2 {SELECT * FROM t1} {one 1 two 2 three 3 four 4}141  do_execsql_test 2.2.$mode.3 {SELECT rowid FROM t1} {1 2 3 4}142  do_execsql_test 2.2.$mode.4 {SELECT rowid FROM t1 WHERE a='two'} {2} 143 144  do_execsql_test 2.2.$mode.5 {145    SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid146  } {1 4} 147 148  set plan(use) {149    QUERY PLAN150    |--SCAN t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'151    `--USE TEMP B-TREE FOR ORDER BY152  }153  set plan(omit) {154    QUERY PLAN155    |--SCAN t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'156    `--USE TEMP B-TREE FOR ORDER BY157  }158  set plan(use2) {159    QUERY PLAN160    |--SCAN t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x161    `--USE TEMP B-TREE FOR ORDER BY162  }163 164  do_eqp_test 2.2.$mode.6 { 165    SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid166  } [string map {"\n  " "\n"} $plan($mode)]167}168 169# 2016-04-09.170# Demonstrate a register overwrite problem when using two virtual171# tables where the outer loop uses the IN operator.172#173set G(collist) [list PrimaryKey flagA columnA]174set G(cols) [join $G(collist) ,]175set G(nulls) "NULL"176 177proc vtab_command {method args} {178  global G179 180  switch -- $method {181    xConnect {182      return "CREATE TABLE t1($G(cols))"183    }184 185    xBestIndex {186      set hdl [lindex $args 0]187      set clist [$hdl constraints]188      set orderby [$hdl orderby]189      190      #puts $clist191      set W [list]192      set U [list]193 194      set i 0195      for {set idx 0} {$idx < [llength $clist]} {incr idx} {196        array set c [lindex $clist $idx]197        if {$c(op)=="eq" && $c(usable)} {198          lappend W "[lindex $G(collist) $c(column)] = %$i%"199          lappend U use $idx200          incr i201        }202      }203 204      if {$W==""} {205        set sql "SELECT rowid, * FROM t1"206      } else {207        set sql "SELECT rowid, * FROM t1 WHERE [join $W { AND }]"208      }209 210      return [concat [list idxstr $sql] $U]211    }212 213    xFilter {214      foreach {idxnum idxstr vals} $args {}215 216      set map [list]217      for {set i 0} {$i < [llength $vals]} {incr i} {218        lappend map "%$i%" 219        set v [lindex $vals $i]220        if {[string is integer $v]} { 221          lappend map $v 222        } else {223          lappend map "'$v'"224        }225      }226      set sql [string map $map $idxstr]227 228      #puts "SQL: $sql"229      return [list sql $sql]230    }231  }232 233  return {}234}235 236db close237forcedelete test.db238sqlite3 db test.db239register_tcl_module db240 241do_execsql_test 3.1 "242  CREATE TABLE t1($G(cols));243  INSERT INTO t1 VALUES(1, 0, 'ValueA');244  INSERT INTO t1 VALUES(2, 0, 'ValueA');245  INSERT INTO t1 VALUES(3, 0, 'ValueB');246  INSERT INTO t1 VALUES(4, 0, 'ValueB');247"248 249do_execsql_test 3.2 {250  CREATE VIRTUAL TABLE VirtualTableA USING tcl(vtab_command);251  CREATE VIRTUAL TABLE VirtualTableB USING tcl(vtab_command);252}253 254do_execsql_test 3.3 { SELECT primarykey FROM VirtualTableA } {1 2 3 4}255 256do_execsql_test 3.4 {257  SELECT * FROM 258  VirtualTableA a CROSS JOIN VirtualTableB b ON b.PrimaryKey=a.PrimaryKey259  WHERE a.ColumnA IN ('ValueA', 'ValueB') AND a.FlagA=0260} {261  1 0 ValueA 1 0 ValueA262  2 0 ValueA 2 0 ValueA263  3 0 ValueB 3 0 ValueB264  4 0 ValueB 4 0 ValueB265}266 267do_execsql_test 3.5 {268  SELECT * FROM 269  VirtualTableA a CROSS JOIN VirtualTableB b ON b.PrimaryKey=a.PrimaryKey270  WHERE a.FlagA=0 AND a.ColumnA IN ('ValueA', 'ValueB') 271} {272  1 0 ValueA 1 0 ValueA273  2 0 ValueA 2 0 ValueA274  3 0 ValueB 3 0 ValueB275  4 0 ValueB 4 0 ValueB276}277 278#-------------------------------------------------------------------------279# If there is an IN(..) condition in the WHERE clause of a query on a280# virtual table, the xBestIndex method is first invoked with the IN(...)281# represented by a "usable" SQLITE_INDEX_CONSTRAINT_EQ constraint. If282# the virtual table elects to use the IN(...) constraint, then the 283# xBestIndex method is invoked again, this time with the IN(...) marked284# as "not usable". Depending on the relative costs of the two plans as285# defined by the virtual table implementation, and the cardinality of the286# IN(...) operator, SQLite chooses the most efficient plan. 287#288# At one point the second invocation of xBestIndex() was only being made289# for join queries. The following tests check that this problem has been290# fixed.291#292proc vtab_command {method args} {293  switch -- $method {294    xConnect {295      return "CREATE TABLE t1(a, b, c, d)"296    }297 298    xBestIndex {299      set hdl [lindex $args 0]300      set clist [$hdl constraints]301      set orderby [$hdl orderby]302      303      lappend ::bestindex_calls $clist304      set ret "cost 1000000 idxnum 555"305      for {set i 0} {$i < [llength $clist]} {incr i} {306        array set C [lindex $clist $i]307        if {$C(usable)} { 308          lappend ret use $i 309        }310      }311      return $ret312    }313  }314  return {}315}316 317do_execsql_test 4.0 {318  CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);319} {}320 321do_test 4.1 {322  set ::bestindex_calls [list]323  execsql {324    SELECT * FROM x1 WHERE a=? AND b BETWEEN ? AND ? AND c IN (1, 2, 3, 4);325  }326  set ::bestindex_calls327} [list \328    [list {op eq column 0 usable 1} \329          {op eq column 2 usable 1} \330          {op ge column 1 usable 1} \331          {op le column 1 usable 1} \332    ] \333    [list {op eq column 0 usable 1} \334          {op eq column 2 usable 0} \335          {op ge column 1 usable 1} \336          {op le column 1 usable 1}337    ]338]339 340do_catchsql_test 5.0 {341  SELECT * FROM tcl('abc');342} {1 {wrong number of arguments}}343 344finish_test345