Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix a fairly obscure problem causing the planner to sometimes choose sub-optimal plans for a query with a single virtual table in the FROM clause, and at least one IN(...) constraint in the WHERE clause. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
f5752517f590b37bfc0267650f580032 |
User & Date: | dan 2019-03-29 13:17:50.351 |
Context
2019-03-29
| ||
15:21 | Remove the unused P5 flag from the OP_Rewind opcode. (check-in: c2edbf3b8c user: drh tags: trunk) | |
13:17 | Fix a fairly obscure problem causing the planner to sometimes choose sub-optimal plans for a query with a single virtual table in the FROM clause, and at least one IN(...) constraint in the WHERE clause. (check-in: f5752517f5 user: dan tags: trunk) | |
11:48 | Fix typos in the comments of the sessions extension, one of which affects the generated documentation. No code changes. (check-in: 040d5d515b user: drh tags: trunk) | |
Changes
Changes to src/where.c.
︙ | ︙ | |||
3331 3332 3333 3334 3335 3336 3337 | /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); /* If the call to xBestIndex() with all terms enabled produced a plan | | | | | | | 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 | /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); /* If the call to xBestIndex() with all terms enabled produced a plan ** that does not require any source tables (IOW: a plan with mBest==0) ** and does not use an IN(...) operator, then there is no point in making ** any further calls to xBestIndex() since they will all return the same ** result (if the xBestIndex() implementation is sane). */ if( rc==SQLITE_OK && ((mBest = (pNew->prereq & ~mPrereq))!=0 || bIn) ){ int seenZero = 0; /* True if a plan with no prereqs seen */ int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */ Bitmask mPrev = 0; Bitmask mBestNoIn = 0; /* If the plan produced by the earlier call uses an IN(...) term, call ** xBestIndex again, this time with IN(...) terms disabled. */ |
︙ | ︙ |
Changes to test/bestindex1.test.
︙ | ︙ | |||
261 262 263 264 265 266 267 268 269 270 | WHERE a.FlagA=0 AND a.ColumnA IN ('ValueA', 'ValueB') } { 1 0 ValueA 1 0 ValueA 2 0 ValueA 2 0 ValueA 3 0 ValueB 3 0 ValueB 4 0 ValueB 4 0 ValueB } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | WHERE a.FlagA=0 AND a.ColumnA IN ('ValueA', 'ValueB') } { 1 0 ValueA 1 0 ValueA 2 0 ValueA 2 0 ValueA 3 0 ValueB 3 0 ValueB 4 0 ValueB 4 0 ValueB } #------------------------------------------------------------------------- # If there is an IN(..) condition in the WHERE clause of a query on a # virtual table, the xBestIndex method is first invoked with the IN(...) # represented by a "usable" SQLITE_INDEX_CONSTRAINT_EQ constraint. If # the virtual table elects to use the IN(...) constraint, then the # xBestIndex method is invoked again, this time with the IN(...) marked # as "not usable". Depending on the relative costs of the two plans as # defined by the virtual table implementation, and the cardinality of the # IN(...) operator, SQLite chooses the most efficient plan. # # At one point the second invocation of xBestIndex() was only being made # for join queries. The following tests check that this problem has been # fixed. # proc vtab_command {method args} { switch -- $method { xConnect { return "CREATE TABLE t1(a, b, c, d)" } xBestIndex { set clist [lindex $args 0] lappend ::bestindex_calls $clist set ret "cost 1000000 idxnum 555" for {set i 0} {$i < [llength $clist]} {incr i} { array set C [lindex $clist $i] if {$C(usable)} { lappend ret use $i } } return $ret } } return {} } do_execsql_test 4.0 { CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); } {} do_test 4.1 { set ::bestindex_calls [list] execsql { SELECT * FROM x1 WHERE a=? AND b BETWEEN ? AND ? AND c IN (1, 2, 3, 4); } set ::bestindex_calls } [list \ [list {op eq column 0 usable 1} \ {op eq column 2 usable 1} \ {op ge column 1 usable 1} \ {op le column 1 usable 1} \ ] \ [list {op eq column 0 usable 1} \ {op eq column 2 usable 0} \ {op ge column 1 usable 1} \ {op le column 1 usable 1} ] ] finish_test |