mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-12 13:01:09 +03:00
An initial attempt to optimize VIEWs that occur as the right operand of a
LEFT JOIN. This particular check-in does not work correctly because it does not deal with the case of columns in the VIEW that return non-NULL even when all columns in the table of the VIEW are NULL because of the LEFT JOIN. FossilOrigin-Name: 1838a59c8a1c151bd6fc822b0ffef661803cf0e4704c917e74a04567b81740b9
This commit is contained in:
55
src/select.c
55
src/select.c
@@ -3154,6 +3154,7 @@ static int multiSelectOrderBy(
|
||||
typedef struct SubstContext {
|
||||
Parse *pParse; /* The parsing context */
|
||||
int iTable; /* Replace references to this table */
|
||||
int iNewTable; /* New table number, or -1 */
|
||||
ExprList *pEList; /* Replacement expressions */
|
||||
} SubstContext;
|
||||
|
||||
@@ -3179,6 +3180,9 @@ static Expr *substExpr(
|
||||
Expr *pExpr /* Expr in which substitution occurs */
|
||||
){
|
||||
if( pExpr==0 ) return 0;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) && pExpr->iRightJoinTable==pSubst->iTable ){
|
||||
pExpr->iRightJoinTable = pSubst->iNewTable;
|
||||
}
|
||||
if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){
|
||||
if( pExpr->iColumn<0 ){
|
||||
pExpr->op = TK_NULL;
|
||||
@@ -3285,7 +3289,13 @@ static void substSelect(
|
||||
** due to ticket [2f7170d73bf9abf80] from 2015-02-09.)
|
||||
**
|
||||
** (3) The subquery is not the right operand of a left outer join
|
||||
** (Originally ticket #306. Strengthened by ticket #3300)
|
||||
** or (3b) the subquery is not a join and (3c) does not contain any
|
||||
** result-set columns that could be non-NULL even if columns of the
|
||||
** subquery table are NULL.
|
||||
** to be more restrictive - disallowing subquery if it was the
|
||||
** right operand of a left join. See tickets #306 and #3300. Relaxed
|
||||
** to allow simple subqueries as the right operand of a left join
|
||||
** on 2017-04-14.)
|
||||
**
|
||||
** (4) The subquery is not DISTINCT.
|
||||
**
|
||||
@@ -3403,6 +3413,8 @@ static int flattenSubquery(
|
||||
SrcList *pSubSrc; /* The FROM clause of the subquery */
|
||||
ExprList *pList; /* The result set of the outer query */
|
||||
int iParent; /* VDBE cursor number of the pSub result set temp table */
|
||||
int iNewParent = -1;/* Replacement table for iParent */
|
||||
int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
|
||||
int i; /* Loop counter */
|
||||
Expr *pWhere; /* The WHERE clause */
|
||||
struct SrcList_item *pSubitem; /* The subquery */
|
||||
@@ -3429,7 +3441,7 @@ static int flattenSubquery(
|
||||
return 0; /* Restriction (2b) */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pSubSrc = pSub->pSrc;
|
||||
assert( pSubSrc );
|
||||
/* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants,
|
||||
@@ -3467,10 +3479,9 @@ static int flattenSubquery(
|
||||
return 0; /* Restriction (23) */
|
||||
}
|
||||
|
||||
/* OBSOLETE COMMENT 1:
|
||||
** Restriction 3: If the subquery is a join, make sure the subquery is
|
||||
** not used as the right operand of an outer join. Examples of why this
|
||||
** is not allowed:
|
||||
/*
|
||||
** If the subquery is the right operand of a LEFT JOIN, then the
|
||||
** subquery may not be a join itself. Example of why this is not allowed:
|
||||
**
|
||||
** t1 LEFT OUTER JOIN (t2 JOIN t3)
|
||||
**
|
||||
@@ -3480,27 +3491,15 @@ static int flattenSubquery(
|
||||
**
|
||||
** which is not at all the same thing.
|
||||
**
|
||||
** OBSOLETE COMMENT 2:
|
||||
** Restriction 12: If the subquery is the right operand of a left outer
|
||||
** join, make sure the subquery has no WHERE clause.
|
||||
** An examples of why this is not allowed:
|
||||
**
|
||||
** t1 LEFT OUTER JOIN (SELECT * FROM t2 WHERE t2.x>0)
|
||||
**
|
||||
** If we flatten the above, we would get
|
||||
**
|
||||
** (t1 LEFT OUTER JOIN t2) WHERE t2.x>0
|
||||
**
|
||||
** But the t2.x>0 test will always fail on a NULL row of t2, which
|
||||
** effectively converts the OUTER JOIN into an INNER JOIN.
|
||||
**
|
||||
** THIS OVERRIDES OBSOLETE COMMENTS 1 AND 2 ABOVE:
|
||||
** Ticket #3300 shows that flattening the right term of a LEFT JOIN
|
||||
** is fraught with danger. Best to avoid the whole thing. If the
|
||||
** subquery is the right term of a LEFT JOIN, then do not flatten.
|
||||
** See also tickets #306 and #3300.
|
||||
*/
|
||||
if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
|
||||
return 0;
|
||||
isLeftJoin = 1;
|
||||
if( pSubSrc->nSrc>1 ){
|
||||
return 0; /* Restriction (3b) */
|
||||
}
|
||||
/* TBD: Deal with the case of result-set expressions that are non-NULL even
|
||||
** when the RHS table of a LEFT JOIN is NULL. */
|
||||
}
|
||||
|
||||
/* Restriction 17: If the sub-query is a compound SELECT, then it must
|
||||
@@ -3709,6 +3708,7 @@ static int flattenSubquery(
|
||||
sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing);
|
||||
assert( pSrc->a[i+iFrom].fg.isTabFunc==0 );
|
||||
pSrc->a[i+iFrom] = pSubSrc->a[i];
|
||||
iNewParent = pSubSrc->a[i].iCursor;
|
||||
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
|
||||
}
|
||||
pSrc->a[iFrom].fg.jointype = jointype;
|
||||
@@ -3754,6 +3754,9 @@ static int flattenSubquery(
|
||||
pSub->pOrderBy = 0;
|
||||
}
|
||||
pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
|
||||
if( isLeftJoin ){
|
||||
setJoinExpr(pWhere, iNewParent);
|
||||
}
|
||||
if( subqueryIsAgg ){
|
||||
assert( pParent->pHaving==0 );
|
||||
pParent->pHaving = pParent->pWhere;
|
||||
@@ -3770,6 +3773,7 @@ static int flattenSubquery(
|
||||
SubstContext x;
|
||||
x.pParse = pParse;
|
||||
x.iTable = iParent;
|
||||
x.iNewTable = iNewParent;
|
||||
x.pEList = pSub->pEList;
|
||||
substSelect(&x, pParent, 0);
|
||||
}
|
||||
@@ -3878,6 +3882,7 @@ static int pushDownWhereTerms(
|
||||
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
|
||||
x.pParse = pParse;
|
||||
x.iTable = iCursor;
|
||||
x.iNewTable = iCursor;
|
||||
x.pEList = pSubq->pEList;
|
||||
pNew = substExpr(&x, pNew);
|
||||
pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew);
|
||||
|
||||
Reference in New Issue
Block a user