aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <>2024-04-08 11:50:07 +0000
committerdrh <>2024-04-08 11:50:07 +0000
commitd59afcfbfede7bc6fbf41f72a3e333f67d2d0a94 (patch)
treebf9170fcadd7fcde45dfa269b9cd40f725b2f95b /src
parent0389d5f8d0838c4b7eb415d6ce515a07aeb958d1 (diff)
parentac94cf3ecedfdaac757f5ae295703dff259cce7d (diff)
downloadsqlite-d59afcfbfede7bc6fbf41f72a3e333f67d2d0a94.tar.gz
sqlite-d59afcfbfede7bc6fbf41f72a3e333f67d2d0a94.zip
Enhance the WHERE-clause push-down optimization so that it is able to
push down WHERE clause terms that contain uncorrelated subqueries. FossilOrigin-Name: 69ec714b2d698acf9e37635256c01b233ce32f22e8323e226441d5ddd948a940
Diffstat (limited to 'src')
-rw-r--r--src/expr.c57
-rw-r--r--src/resolve.c2
-rw-r--r--src/select.c2
-rw-r--r--src/sqliteInt.h4
-rw-r--r--src/where.c4
5 files changed, 54 insertions, 15 deletions
diff --git a/src/expr.c b/src/expr.c
index 9ec77e823..2257422b8 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2500,7 +2500,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
return WRC_Continue;
}
}
-static int exprIsConst(Parse *pParse, Expr *p, int initFlag, int iCur){
+static int exprIsConst(Parse *pParse, Expr *p, int initFlag){
Walker w;
w.eCode = initFlag;
w.pParse = pParse;
@@ -2509,7 +2509,6 @@ static int exprIsConst(Parse *pParse, Expr *p, int initFlag, int iCur){
#ifdef SQLITE_DEBUG
w.xSelectCallback2 = sqlite3SelectWalkAssert2;
#endif
- w.u.iCur = iCur;
sqlite3WalkExpr(&w, p);
return w.eCode;
}
@@ -2529,7 +2528,7 @@ static int exprIsConst(Parse *pParse, Expr *p, int initFlag, int iCur){
** function and on its parameters.
*/
int sqlite3ExprIsConstant(Parse *pParse, Expr *p){
- return exprIsConst(pParse, p, 1, 0);
+ return exprIsConst(pParse, p, 1);
}
/*
@@ -2546,7 +2545,23 @@ int sqlite3ExprIsConstant(Parse *pParse, Expr *p){
** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce().
*/
static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){
- return exprIsConst(pParse, p, 2, 0);
+ return exprIsConst(pParse, p, 2);
+}
+
+/*
+** This routine examines sub-SELECT statements as an expression is being
+** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered
+** constant as long as they are uncorrelated - meaning that they do not
+** contain any terms from outer contexts.
+*/
+static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){
+ assert( pSelect!=0 );
+ assert( pWalker->eCode==3 || pWalker->eCode==0 );
+ if( (pSelect->selFlags & SF_Correlated)!=0 ){
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
+ return WRC_Prune;
}
/*
@@ -2554,9 +2569,26 @@ static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){
** for any single row of the table with cursor iCur. In other words, the
** expression must not refer to any non-deterministic function nor any
** table other than iCur.
+**
+** Consider uncorrelated subqueries to be constants if the bAllowSubq
+** parameter is true.
*/
-int sqlite3ExprIsTableConstant(Expr *p, int iCur){
- return exprIsConst(0, p, 3, iCur);
+static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){
+ Walker w;
+ w.eCode = 3;
+ w.pParse = 0;
+ w.xExprCallback = exprNodeIsConstant;
+ if( bAllowSubq ){
+ w.xSelectCallback = exprSelectWalkTableConstant;
+ }else{
+ w.xSelectCallback = sqlite3SelectWalkFail;
+#ifdef SQLITE_DEBUG
+ w.xSelectCallback2 = sqlite3SelectWalkAssert2;
+#endif
+ }
+ w.u.iCur = iCur;
+ sqlite3WalkExpr(&w, p);
+ return w.eCode;
}
/*
@@ -2574,7 +2606,10 @@ int sqlite3ExprIsTableConstant(Expr *p, int iCur){
**
** (1) pExpr cannot refer to any table other than pSrc->iCursor.
**
-** (2) pExpr cannot use subqueries or non-deterministic functions.
+** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is
+** true and the subquery is non-correlated
+**
+** (2b) pExpr cannot use non-deterministic functions.
**
** (3) pSrc cannot be part of the left operand for a RIGHT JOIN.
** (Is there some way to relax this constraint?)
@@ -2603,7 +2638,8 @@ int sqlite3ExprIsTableConstant(Expr *p, int iCur){
int sqlite3ExprIsSingleTableConstraint(
Expr *pExpr, /* The constraint */
const SrcList *pSrcList, /* Complete FROM clause */
- int iSrc /* Which element of pSrcList to use */
+ int iSrc, /* Which element of pSrcList to use */
+ int bAllowSubq /* Allow non-correlated subqueries */
){
const SrcItem *pSrc = &pSrcList->a[iSrc];
if( pSrc->fg.jointype & JT_LTORJ ){
@@ -2628,7 +2664,8 @@ int sqlite3ExprIsSingleTableConstraint(
}
}
}
- return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */
+ /* Rules (1), (2a), and (2b) handled by the following: */
+ return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq);
}
@@ -2713,7 +2750,7 @@ int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprList *pGroupBy){
*/
int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){
assert( isInit==0 || isInit==1 );
- return exprIsConst(0, p, 4+isInit, 0);
+ return exprIsConst(0, p, 4+isInit);
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
diff --git a/src/resolve.c b/src/resolve.c
index 86532db15..68a1d94a2 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -1354,6 +1354,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pNC->ncFlags & NC_PartIdx );
testcase( pNC->ncFlags & NC_IdxExpr );
testcase( pNC->ncFlags & NC_GenCol );
+ assert( pExpr->x.pSelect );
if( pNC->ncFlags & NC_SelfRef ){
notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
}else{
@@ -1362,6 +1363,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
+ pExpr->x.pSelect->selFlags |= SF_Correlated;
}
pNC->ncFlags |= NC_Subquery;
}
diff --git a/src/select.c b/src/select.c
index 821f9a0c4..9c2bf3b93 100644
--- a/src/select.c
+++ b/src/select.c
@@ -5272,7 +5272,7 @@ static int pushDownWhereTerms(
}
#endif
- if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){
+ if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){
nChng++;
pSubq->selFlags |= SF_PushDown;
while( pSubq ){
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index d7e339d18..0d33f9ee2 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3585,6 +3585,7 @@ struct Select {
#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
+#define SF_Correlated 0x20000000 /* True if references the outer context */
/* True if S exists and has SF_NestedFrom */
#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0)
@@ -5081,8 +5082,7 @@ int sqlite3ExprTruthValue(const Expr*);
int sqlite3ExprIsConstant(Parse*,Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*, u8);
int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
-int sqlite3ExprIsTableConstant(Expr*,int);
-int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int);
+int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int);
#ifdef SQLITE_ENABLE_CURSOR_HINTS
int sqlite3ExprContainsSubquery(Expr*);
#endif
diff --git a/src/where.c b/src/where.c
index a1837b587..65b6ae2c9 100644
--- a/src/where.c
+++ b/src/where.c
@@ -942,7 +942,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
** WHERE clause (or the ON clause of a LEFT join) that constrain which
** rows of the target table (pSrc) that can be used. */
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
- && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom)
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0)
){
pPartial = sqlite3ExprAnd(pParse, pPartial,
sqlite3ExprDup(pParse->db, pExpr, 0));
@@ -1211,7 +1211,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
Expr *pExpr = pTerm->pExpr;
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
- && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc)
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0)
){
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
}