Avoid passing constraints that are unusable due to LEFT or CROSS joins to virtual table xBestIndex() methods.
FossilOrigin-Name: 7b446771cadedafbe8924ad0658adc2597816dc7
This commit is contained in:
@@ -269,5 +269,88 @@ ifcapable rtree {
|
||||
db close
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that queries featuring LEFT or CROSS JOINS are handled correctly.
|
||||
# Handled correctly in this case means:
|
||||
#
|
||||
# * Terms with prereqs that appear to the left of a LEFT JOIN against
|
||||
# the virtual table are always available to xBestIndex.
|
||||
#
|
||||
# * Terms with prereqs that appear to the right of a LEFT JOIN against
|
||||
# the virtual table are never available to xBestIndex.
|
||||
#
|
||||
# And the same behaviour for CROSS joins.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE xdir(x1);
|
||||
CREATE TABLE ydir(y1);
|
||||
CREATE VIRTUAL TABLE rt USING rtree_i32(id, xmin, xmax, ymin, ymax);
|
||||
|
||||
INSERT INTO xdir VALUES(5);
|
||||
INSERT INTO ydir VALUES(10);
|
||||
|
||||
INSERT INTO rt VALUES(1, 2, 7, 12, 14); -- Not a hit
|
||||
INSERT INTO rt VALUES(2, 2, 7, 8, 12); -- A hit!
|
||||
INSERT INTO rt VALUES(3, 7, 11, 8, 12); -- Not a hit!
|
||||
INSERT INTO rt VALUES(4, 5, 5, 10, 10); -- A hit!
|
||||
|
||||
}
|
||||
|
||||
proc do_eqp_execsql_test {tn sql res} {
|
||||
set query "EXPLAIN QUERY PLAN $sql ; $sql "
|
||||
uplevel [list do_execsql_test $tn $query $res]
|
||||
}
|
||||
|
||||
do_eqp_execsql_test 7.1 {
|
||||
SELECT id FROM xdir, rt, ydir
|
||||
ON (y1 BETWEEN ymin AND ymax)
|
||||
WHERE (x1 BETWEEN xmin AND xmax);
|
||||
} {
|
||||
0 0 0 {SCAN TABLE xdir}
|
||||
0 1 2 {SCAN TABLE ydir}
|
||||
0 2 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B2D3B0D1}
|
||||
2 4
|
||||
}
|
||||
|
||||
do_eqp_execsql_test 7.2 {
|
||||
SELECT * FROM xdir, rt LEFT JOIN ydir
|
||||
ON (y1 BETWEEN ymin AND ymax)
|
||||
WHERE (x1 BETWEEN xmin AND xmax);
|
||||
} {
|
||||
0 0 0 {SCAN TABLE xdir}
|
||||
0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
|
||||
0 2 2 {SCAN TABLE ydir}
|
||||
|
||||
5 1 2 7 12 14 {}
|
||||
5 2 2 7 8 12 10
|
||||
5 4 5 5 10 10 10
|
||||
}
|
||||
|
||||
do_eqp_execsql_test 7.3 {
|
||||
SELECT id FROM xdir, rt CROSS JOIN ydir
|
||||
ON (y1 BETWEEN ymin AND ymax)
|
||||
WHERE (x1 BETWEEN xmin AND xmax);
|
||||
} {
|
||||
0 0 0 {SCAN TABLE xdir}
|
||||
0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
|
||||
0 2 2 {SCAN TABLE ydir}
|
||||
2 4
|
||||
}
|
||||
|
||||
do_eqp_execsql_test 7.4 {
|
||||
SELECT id FROM rt, xdir CROSS JOIN ydir
|
||||
ON (y1 BETWEEN ymin AND ymax)
|
||||
WHERE (x1 BETWEEN xmin AND xmax);
|
||||
} {
|
||||
0 0 1 {SCAN TABLE xdir}
|
||||
0 1 0 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
|
||||
0 2 2 {SCAN TABLE ydir}
|
||||
2 4
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
C Add\sthe\s--output=$file\sand\s--verbose=(0|1|file)\soptions\sto\stester.tcl.
|
||||
D 2015-06-09T15:58:28.618
|
||||
C Avoid\spassing\sconstraints\sthat\sare\sunusable\sdue\sto\sLEFT\sor\sCROSS\sjoins\sto\svirtual\stable\sxBestIndex()\smethods.
|
||||
D 2015-06-10T14:27:40.648
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 580e006530fab67ccd34926ce2eda66d326af60f
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@@ -156,7 +156,7 @@ F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a
|
||||
F ext/rtree/rtree9.test b5eb13849545dfd271a54ff16784cb00d8792aea
|
||||
F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
|
||||
F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
|
||||
F ext/rtree/rtreeC.test df158dcc81f1a43ce7eef361af03c48ec91f1e06
|
||||
F ext/rtree/rtreeC.test 90aaaffe2fd4f0dcd12289cad5515f6d41f45ffd
|
||||
F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca
|
||||
F ext/rtree/rtreeE.test 45a147a64a76306172819562309681d8e90f94bb
|
||||
F ext/rtree/rtreeF.test 66deb9fd1611c7ca2e374adba63debdc2dbb12b4
|
||||
@@ -327,7 +327,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
|
||||
F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
|
||||
F src/where.c a328fcc3342044992644b6a11bf301593b8dafb4
|
||||
F src/where.c 95c0fbfff7aef890b5da7293f6d85cd9a5f99af8
|
||||
F src/whereInt.h 5f87e3c4b0551747d119730dfebddd3c54f04047
|
||||
F src/wherecode.c 0669481cabaf5caf934b6bb825df15bc57f60d40
|
||||
F src/whereexpr.c 9ce1c9cfedbf80c93c7d899497025ec8256ce652
|
||||
@@ -709,7 +709,7 @@ F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
|
||||
F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
|
||||
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
|
||||
F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
|
||||
F test/join.test 52d4d49f86d0cf46926672878c4eaf0da399104a
|
||||
F test/join.test f9d4a28dec81c6e9dc21b73518e024d73b5ebf57
|
||||
F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
|
||||
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
|
||||
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
|
||||
@@ -1285,7 +1285,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P e62aed01f1a6dbc12d6e21386c1671eb640b8d49
|
||||
R f55cc3f41ca69cff9ff874d367e120de
|
||||
P f7b2c70362f10ee0347c1d2318918ffefa53243d a34cd71c8aafaece04a2332a127e9b8e99c1403c
|
||||
R 83db81ff2fb6c9dcf1dbb41c52657424
|
||||
U dan
|
||||
Z 528ec808c863ae4b55820f5f3962de4a
|
||||
Z 293899bbe176179f6b2abcd42b5d0831
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
f7b2c70362f10ee0347c1d2318918ffefa53243d
|
||||
7b446771cadedafbe8924ad0658adc2597816dc7
|
||||
+54
-14
@@ -757,6 +757,7 @@ end_auto_index_create:
|
||||
static sqlite3_index_info *allocateIndexInfo(
|
||||
Parse *pParse,
|
||||
WhereClause *pWC,
|
||||
Bitmask mUnusable, /* Ignore terms with these prereqs */
|
||||
struct SrcList_item *pSrc,
|
||||
ExprList *pOrderBy
|
||||
){
|
||||
@@ -773,6 +774,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
** to this virtual table */
|
||||
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
if( pTerm->leftCursor != pSrc->iCursor ) continue;
|
||||
if( pTerm->prereqRight & mUnusable ) continue;
|
||||
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
|
||||
testcase( pTerm->eOperator & WO_IN );
|
||||
testcase( pTerm->eOperator & WO_ISNULL );
|
||||
@@ -827,6 +829,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
u8 op;
|
||||
if( pTerm->leftCursor != pSrc->iCursor ) continue;
|
||||
if( pTerm->prereqRight & mUnusable ) continue;
|
||||
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
|
||||
testcase( pTerm->eOperator & WO_IN );
|
||||
testcase( pTerm->eOperator & WO_IS );
|
||||
@@ -2666,10 +2669,32 @@ static int whereLoopAddBtree(
|
||||
/*
|
||||
** Add all WhereLoop objects for a table of the join identified by
|
||||
** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
|
||||
**
|
||||
** If there are no LEFT or CROSS JOIN joins in the query, both mExtra and
|
||||
** mUnusable are set to 0. Otherwise, mExtra is a mask of all FROM clause
|
||||
** entries that occur before the virtual table in the FROM clause and are
|
||||
** separated from it by at least one LEFT or CROSS JOIN. Similarly, the
|
||||
** mUnusable mask contains all FROM clause entries that occur after the
|
||||
** virtual table and are separated from it by at least one LEFT or
|
||||
** CROSS JOIN.
|
||||
**
|
||||
** For example, if the query were:
|
||||
**
|
||||
** ... FROM t1, t2 LEFT JOIN t3, t4, vt CROSS JOIN t5, t6;
|
||||
**
|
||||
** then mExtra corresponds to (t1, t2) and mUnusable to (t5, t6).
|
||||
**
|
||||
** All the tables in mExtra must be scanned before the current virtual
|
||||
** table. So any terms for which all prerequisites are satisfied by
|
||||
** mExtra may be specified as "usable" in all calls to xBestIndex.
|
||||
** Conversely, all tables in mUnusable must be scanned after the current
|
||||
** virtual table, so any terms for which the prerequisites overlap with
|
||||
** mUnusable should always be configured as "not-usable" for xBestIndex.
|
||||
*/
|
||||
static int whereLoopAddVirtual(
|
||||
WhereLoopBuilder *pBuilder, /* WHERE clause information */
|
||||
Bitmask mExtra
|
||||
Bitmask mExtra, /* Tables that must be scanned before this one */
|
||||
Bitmask mUnusable /* Tables that must be scanned after this one */
|
||||
){
|
||||
WhereInfo *pWInfo; /* WHERE analysis context */
|
||||
Parse *pParse; /* The parsing context */
|
||||
@@ -2690,6 +2715,7 @@ static int whereLoopAddVirtual(
|
||||
WhereLoop *pNew;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( (mExtra & mUnusable)==0 );
|
||||
pWInfo = pBuilder->pWInfo;
|
||||
pParse = pWInfo->pParse;
|
||||
db = pParse->db;
|
||||
@@ -2698,7 +2724,7 @@ static int whereLoopAddVirtual(
|
||||
pSrc = &pWInfo->pTabList->a[pNew->iTab];
|
||||
pTab = pSrc->pTab;
|
||||
assert( IsVirtual(pTab) );
|
||||
pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pBuilder->pOrderBy);
|
||||
pIdxInfo = allocateIndexInfo(pParse, pWC, mUnusable, pSrc,pBuilder->pOrderBy);
|
||||
if( pIdxInfo==0 ) return SQLITE_NOMEM;
|
||||
pNew->prereq = 0;
|
||||
pNew->rSetup = 0;
|
||||
@@ -2728,7 +2754,7 @@ static int whereLoopAddVirtual(
|
||||
if( (pTerm->eOperator & WO_IN)!=0 ){
|
||||
seenIn = 1;
|
||||
}
|
||||
if( pTerm->prereqRight!=0 ){
|
||||
if( (pTerm->prereqRight & ~mExtra)!=0 ){
|
||||
seenVar = 1;
|
||||
}else if( (pTerm->eOperator & WO_IN)==0 ){
|
||||
pIdxCons->usable = 1;
|
||||
@@ -2736,7 +2762,7 @@ static int whereLoopAddVirtual(
|
||||
break;
|
||||
case 1: /* Constants with IN operators */
|
||||
assert( seenIn );
|
||||
pIdxCons->usable = (pTerm->prereqRight==0);
|
||||
pIdxCons->usable = (pTerm->prereqRight & ~mExtra)==0;
|
||||
break;
|
||||
case 2: /* Variables without IN */
|
||||
assert( seenVar );
|
||||
@@ -2835,7 +2861,11 @@ whereLoopAddVtab_exit:
|
||||
** Add WhereLoop entries to handle OR terms. This works for either
|
||||
** btrees or virtual tables.
|
||||
*/
|
||||
static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
static int whereLoopAddOr(
|
||||
WhereLoopBuilder *pBuilder,
|
||||
Bitmask mExtra,
|
||||
Bitmask mUnusable
|
||||
){
|
||||
WhereInfo *pWInfo = pBuilder->pWInfo;
|
||||
WhereClause *pWC;
|
||||
WhereLoop *pNew;
|
||||
@@ -2894,14 +2924,14 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pItem->pTab) ){
|
||||
rc = whereLoopAddVirtual(&sSubBuild, mExtra);
|
||||
rc = whereLoopAddVirtual(&sSubBuild, mExtra, mUnusable);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
rc = whereLoopAddBtree(&sSubBuild, mExtra);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = whereLoopAddOr(&sSubBuild, mExtra);
|
||||
rc = whereLoopAddOr(&sSubBuild, mExtra, mUnusable);
|
||||
}
|
||||
assert( rc==SQLITE_OK || sCur.n==0 );
|
||||
if( sCur.n==0 ){
|
||||
@@ -2963,33 +2993,43 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
|
||||
int iTab;
|
||||
SrcList *pTabList = pWInfo->pTabList;
|
||||
struct SrcList_item *pItem;
|
||||
struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel];
|
||||
sqlite3 *db = pWInfo->pParse->db;
|
||||
int nTabList = pWInfo->nLevel;
|
||||
int rc = SQLITE_OK;
|
||||
u8 priorJoinType = 0;
|
||||
WhereLoop *pNew;
|
||||
u8 priorJointype = 0;
|
||||
|
||||
/* Loop over the tables in the join, from left to right */
|
||||
pNew = pBuilder->pNew;
|
||||
whereLoopInit(pNew);
|
||||
for(iTab=0, pItem=pTabList->a; iTab<nTabList; iTab++, pItem++){
|
||||
for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){
|
||||
Bitmask mUnusable = 0;
|
||||
pNew->iTab = iTab;
|
||||
pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
|
||||
if( ((pItem->jointype|priorJoinType) & (JT_LEFT|JT_CROSS))!=0 ){
|
||||
if( ((pItem->jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){
|
||||
/* This condition is true when pItem is the FROM clause term on the
|
||||
** right-hand-side of a LEFT or CROSS JOIN. */
|
||||
mExtra = mPrior;
|
||||
}
|
||||
priorJoinType = pItem->jointype;
|
||||
priorJointype = pItem->jointype;
|
||||
if( IsVirtual(pItem->pTab) ){
|
||||
rc = whereLoopAddVirtual(pBuilder, mExtra);
|
||||
struct SrcList_item *p;
|
||||
for(p=&pItem[1]; p<pEnd; p++){
|
||||
if( mUnusable || (p->jointype & (JT_LEFT|JT_CROSS)) ){
|
||||
mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
|
||||
}
|
||||
}
|
||||
rc = whereLoopAddVirtual(pBuilder, mExtra, mUnusable);
|
||||
}else{
|
||||
rc = whereLoopAddBtree(pBuilder, mExtra);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = whereLoopAddOr(pBuilder, mExtra);
|
||||
rc = whereLoopAddOr(pBuilder, mExtra, mUnusable);
|
||||
}
|
||||
mPrior |= pNew->maskSelf;
|
||||
if( rc || db->mallocFailed ) break;
|
||||
}
|
||||
|
||||
whereLoopClear(db, pNew);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -687,4 +687,30 @@ ifcapable pragma&&compileoption_diags {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test a problem with reordering tables following a LEFT JOIN.
|
||||
#
|
||||
do_execsql_test join-13.0 {
|
||||
CREATE TABLE aa(a);
|
||||
CREATE TABLE bb(b);
|
||||
CREATE TABLE cc(c);
|
||||
|
||||
INSERT INTO aa VALUES(45);
|
||||
INSERT INTO cc VALUES(45);
|
||||
INSERT INTO cc VALUES(45);
|
||||
}
|
||||
|
||||
do_execsql_test join-13.1 {
|
||||
SELECT * FROM aa LEFT JOIN bb, cc WHERE cc.c=aa.a;
|
||||
} {45 {} 45 45 {} 45}
|
||||
|
||||
# In the following, the order of [cc] and [bb] must not be exchanged, even
|
||||
# though this would be helpful if the query used an inner join.
|
||||
do_execsql_test join-13.2 {
|
||||
CREATE INDEX ccc ON cc(c);
|
||||
SELECT * FROM aa LEFT JOIN bb, cc WHERE cc.c=aa.a;
|
||||
} {45 {} 45 45 {} 45}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
Reference in New Issue
Block a user