mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-15 11:41:13 +03:00
Add support for RIGHT and FULL OUTER JOINs.
FossilOrigin-Name: fa9d206f904280e3eafc6f4ba6c0c7325948364c62eeeb9f0fdc5825d622ec35
This commit is contained in:
29
src/build.c
29
src/build.c
@@ -5067,14 +5067,33 @@ void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){
|
||||
** The operator is "natural cross join". The A and B operands are stored
|
||||
** in p->a[0] and p->a[1], respectively. The parser initially stores the
|
||||
** operator with A. This routine shifts that operator over to B.
|
||||
**
|
||||
** Additional changes:
|
||||
**
|
||||
** * All tables to the left of the right-most RIGHT JOIN are tagged with
|
||||
** JT_LTORJ (mnemonic: Left Table Of Right Join) so that the
|
||||
** code generator can easily tell that the table is part of
|
||||
** the left operand of at least one RIGHT JOIN.
|
||||
*/
|
||||
void sqlite3SrcListShiftJoinType(SrcList *p){
|
||||
if( p ){
|
||||
int i;
|
||||
for(i=p->nSrc-1; i>0; i--){
|
||||
p->a[i].fg.jointype = p->a[i-1].fg.jointype;
|
||||
}
|
||||
if( p && p->nSrc>1 ){
|
||||
int i = p->nSrc-1;
|
||||
u8 allFlags = 0;
|
||||
do{
|
||||
allFlags |= p->a[i].fg.jointype = p->a[i-1].fg.jointype;
|
||||
}while( (--i)>0 );
|
||||
p->a[0].fg.jointype = 0;
|
||||
|
||||
/* All terms to the left of a RIGHT JOIN should be tagged with the
|
||||
** JT_LTORJ flags */
|
||||
if( allFlags & JT_RIGHT ){
|
||||
for(i=p->nSrc-1; ALWAYS(i>0) && (p->a[i].fg.jointype&JT_RIGHT)==0; i--){}
|
||||
i--;
|
||||
assert( i>=0 );
|
||||
do{
|
||||
p->a[i--].fg.jointype |= JT_LTORJ;
|
||||
}while( i>=0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2179,7 +2179,7 @@ Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
|
||||
static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
|
||||
|
||||
/* If pWalker->eCode is 2 then any term of the expression that comes from
|
||||
** the ON or USING clauses of a left join disqualifies the expression
|
||||
** the ON or USING clauses of an outer join disqualifies the expression
|
||||
** from being considered constant. */
|
||||
if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){
|
||||
pWalker->eCode = 0;
|
||||
@@ -5817,7 +5817,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
|
||||
** in an incorrect answer.
|
||||
**
|
||||
** Terms of p that are marked with EP_FromJoin (and hence that come from
|
||||
** the ON or USING clauses of LEFT JOINS) are excluded from the analysis.
|
||||
** the ON or USING clauses of OUTER JOINS) are excluded from the analysis.
|
||||
**
|
||||
** This routine is used to check if a LEFT JOIN can be converted into
|
||||
** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE
|
||||
|
||||
@@ -367,9 +367,7 @@ static int lookupName(
|
||||
pExpr->iTable = pMatch->iCursor;
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
pExpr->y.pTab = pMatch->pTab;
|
||||
/* RIGHT JOIN not (yet) supported */
|
||||
assert( (pMatch->fg.jointype & JT_RIGHT)==0 );
|
||||
if( (pMatch->fg.jointype & JT_LEFT)!=0 ){
|
||||
if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){
|
||||
ExprSetProperty(pExpr, EP_CanBeNull);
|
||||
}
|
||||
pSchema = pExpr->y.pTab->pSchema;
|
||||
|
||||
129
src/select.c
129
src/select.c
@@ -298,13 +298,9 @@ int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
|
||||
const char *zSp2 = " ";
|
||||
if( pB==0 ){ zSp1++; }
|
||||
if( pC==0 ){ zSp2++; }
|
||||
sqlite3ErrorMsg(pParse, "unknown or unsupported join type: "
|
||||
sqlite3ErrorMsg(pParse, "unknown join type: "
|
||||
"%T%s%T%s%T", pA, zSp1, pB, zSp2, pC);
|
||||
jointype = JT_INNER;
|
||||
}else if( (jointype & JT_RIGHT)!=0 ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"RIGHT and FULL OUTER JOINs are not currently supported");
|
||||
jointype = JT_INNER;
|
||||
}
|
||||
return jointype;
|
||||
}
|
||||
@@ -377,7 +373,7 @@ static void addWhereTerm(
|
||||
int iColLeft, /* Index of column in first table */
|
||||
int iRight, /* Index of second table in pSrc */
|
||||
int iColRight, /* Index of column in second table */
|
||||
int isOuterJoin, /* True if this is an OUTER join */
|
||||
u32 joinType, /* EP_FromJoin or EP_InnerJoin */
|
||||
Expr **ppWhere /* IN/OUT: The WHERE clause to add to */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
@@ -397,8 +393,8 @@ static void addWhereTerm(
|
||||
assert( pE2!=0 || pEq==0 ); /* Due to db->mallocFailed test
|
||||
** in sqlite3DbMallocRawNN() called from
|
||||
** sqlite3PExpr(). */
|
||||
if( pEq && isOuterJoin ){
|
||||
ExprSetProperty(pEq, EP_FromJoin);
|
||||
if( pEq ){
|
||||
ExprSetProperty(pEq, joinType);
|
||||
assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
|
||||
ExprSetVVAProperty(pEq, EP_NoReduce);
|
||||
pEq->w.iJoin = pE2->iTable;
|
||||
@@ -412,7 +408,7 @@ static void addWhereTerm(
|
||||
** expression.
|
||||
**
|
||||
** The EP_FromJoin property is used on terms of an expression to tell
|
||||
** the LEFT OUTER JOIN processing logic that this term is part of the
|
||||
** the OUTER JOIN processing logic that this term is part of the
|
||||
** join restriction specified in the ON or USING clause and not a part
|
||||
** of the more general WHERE clause. These terms are moved over to the
|
||||
** WHERE clause during join processing but we need to remember that they
|
||||
@@ -432,9 +428,10 @@ static void addWhereTerm(
|
||||
** after the t1 loop and rows with t1.x!=5 will never appear in
|
||||
** the output, which is incorrect.
|
||||
*/
|
||||
void sqlite3SetJoinExpr(Expr *p, int iTable){
|
||||
void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){
|
||||
assert( joinFlag==EP_FromJoin || joinFlag==EP_InnerJoin );
|
||||
while( p ){
|
||||
ExprSetProperty(p, EP_FromJoin);
|
||||
ExprSetProperty(p, joinFlag);
|
||||
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
|
||||
ExprSetVVAProperty(p, EP_NoReduce);
|
||||
p->w.iJoin = iTable;
|
||||
@@ -443,11 +440,11 @@ void sqlite3SetJoinExpr(Expr *p, int iTable){
|
||||
if( p->x.pList ){
|
||||
int i;
|
||||
for(i=0; i<p->x.pList->nExpr; i++){
|
||||
sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable);
|
||||
sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3SetJoinExpr(p->pLeft, iTable);
|
||||
sqlite3SetJoinExpr(p->pLeft, iTable, joinFlag);
|
||||
p = p->pRight;
|
||||
}
|
||||
}
|
||||
@@ -463,6 +460,7 @@ static void unsetJoinExpr(Expr *p, int iTable){
|
||||
if( ExprHasProperty(p, EP_FromJoin)
|
||||
&& (iTable<0 || p->w.iJoin==iTable) ){
|
||||
ExprClearProperty(p, EP_FromJoin);
|
||||
ExprSetProperty(p, EP_InnerJoin);
|
||||
}
|
||||
if( p->op==TK_COLUMN && p->iTable==iTable ){
|
||||
ExprClearProperty(p, EP_CanBeNull);
|
||||
@@ -506,10 +504,10 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
pRight = &pLeft[1];
|
||||
for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){
|
||||
Table *pRightTab = pRight->pTab;
|
||||
int isOuter;
|
||||
u32 joinType;
|
||||
|
||||
if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue;
|
||||
isOuter = (pRight->fg.jointype & JT_OUTER)!=0;
|
||||
joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_FromJoin : EP_InnerJoin;
|
||||
|
||||
/* When the NATURAL keyword is present, add WHERE clause terms for
|
||||
** every column that the two tables have in common.
|
||||
@@ -529,7 +527,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
zName = pRightTab->aCol[j].zCnName;
|
||||
if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 1) ){
|
||||
addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j,
|
||||
isOuter, &p->pWhere);
|
||||
joinType, &p->pWhere);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -560,7 +558,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
return 1;
|
||||
}
|
||||
addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol,
|
||||
isOuter, &p->pWhere);
|
||||
joinType, &p->pWhere);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,7 +566,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
** an AND operator.
|
||||
*/
|
||||
else if( pRight->u3.pOn ){
|
||||
if( isOuter ) sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor);
|
||||
sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
|
||||
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
|
||||
pRight->u3.pOn = 0;
|
||||
}
|
||||
@@ -3656,12 +3654,40 @@ static int multiSelectOrderBy(
|
||||
**
|
||||
** All references to columns in table iTable are to be replaced by corresponding
|
||||
** expressions in pEList.
|
||||
**
|
||||
** ## About "isOuterJoin":
|
||||
**
|
||||
** The isOuterJoin column indicates that the replacement will occur into a
|
||||
** position in the parent that NULL-able due to an OUTER JOIN. Either the
|
||||
** target slot in the parent is the right operand of a LEFT JOIN, or one of
|
||||
** the left operands of a RIGHT JOIN. In either case, we need to potentially
|
||||
** bypass the substituted expression with OP_IfNullRow.
|
||||
**
|
||||
** Suppose the original expression integer constant. Even though the table
|
||||
** has the nullRow flag set, because the expression is an integer constant,
|
||||
** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode
|
||||
** that checks to see if the nullRow flag is set on the table. If the nullRow
|
||||
** flag is set, then the value in the register is set to NULL and the original
|
||||
** expression is bypassed. If the nullRow flag is not set, then the original
|
||||
** expression runs to populate the register.
|
||||
**
|
||||
** Example where this is needed:
|
||||
**
|
||||
** CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT);
|
||||
** CREATE TABLE t2(x INT UNIQUE);
|
||||
**
|
||||
** SELECT a,b,m,x FROM t1 LEFT JOIN (SELECT 59 AS m,x FROM t2) ON b=x;
|
||||
**
|
||||
** When the subquery on the right side of the LEFT JOIN is flattened, we
|
||||
** have to add OP_IfNullRow in front of the OP_Integer that implements the
|
||||
** "m" value of the subquery so that a NULL will be loaded instead of 59
|
||||
** when processing a non-matched row of the left.
|
||||
*/
|
||||
typedef struct SubstContext {
|
||||
Parse *pParse; /* The parsing context */
|
||||
int iTable; /* Replace references to this table */
|
||||
int iNewTable; /* New table number */
|
||||
int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
|
||||
int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
|
||||
ExprList *pEList; /* Replacement expressions */
|
||||
} SubstContext;
|
||||
|
||||
@@ -3711,7 +3737,7 @@ static Expr *substExpr(
|
||||
sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
|
||||
}else{
|
||||
sqlite3 *db = pSubst->pParse->db;
|
||||
if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){
|
||||
if( pSubst->isOuterJoin && pCopy->op!=TK_COLUMN ){
|
||||
memset(&ifNullRow, 0, sizeof(ifNullRow));
|
||||
ifNullRow.op = TK_IF_NULL_ROW;
|
||||
ifNullRow.pLeft = pCopy;
|
||||
@@ -3725,11 +3751,12 @@ static Expr *substExpr(
|
||||
sqlite3ExprDelete(db, pNew);
|
||||
return pExpr;
|
||||
}
|
||||
if( pSubst->isLeftJoin ){
|
||||
if( pSubst->isOuterJoin ){
|
||||
ExprSetProperty(pNew, EP_CanBeNull);
|
||||
}
|
||||
if( ExprHasProperty(pExpr,EP_FromJoin) ){
|
||||
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin);
|
||||
if( ExprHasProperty(pExpr,EP_FromJoin|EP_InnerJoin) ){
|
||||
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
|
||||
pExpr->flags & (EP_FromJoin|EP_InnerJoin));
|
||||
}
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
pExpr = pNew;
|
||||
@@ -3979,6 +4006,7 @@ static void renumberCursors(
|
||||
** table and
|
||||
** (3c) the outer query may not be an aggregate.
|
||||
** (3d) the outer query may not be DISTINCT.
|
||||
** See also (26) for restrictions on RIGHT JOIN.
|
||||
**
|
||||
** (4) The subquery can not be DISTINCT.
|
||||
**
|
||||
@@ -4077,6 +4105,9 @@ static void renumberCursors(
|
||||
** function in the select list or ORDER BY clause, flattening
|
||||
** is not attempted.
|
||||
**
|
||||
** (26) The subquery may not be the right operand of a RIGHT JOIN.
|
||||
** See also (3) for restrictions on LEFT JOIN.
|
||||
**
|
||||
**
|
||||
** In this routine, the "p" parameter is a pointer to the outer query.
|
||||
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
|
||||
@@ -4102,7 +4133,7 @@ static int flattenSubquery(
|
||||
SrcList *pSubSrc; /* The FROM clause of the subquery */
|
||||
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 isOuterJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
|
||||
int i; /* Loop counter */
|
||||
Expr *pWhere; /* The WHERE clause */
|
||||
SrcItem *pSubitem; /* The subquery */
|
||||
@@ -4175,23 +4206,24 @@ static int flattenSubquery(
|
||||
**
|
||||
** See also tickets #306, #350, and #3300.
|
||||
*/
|
||||
if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
|
||||
isLeftJoin = 1;
|
||||
if( pSubSrc->nSrc>1 /* (3a) */
|
||||
|| isAgg /* (3b) */
|
||||
|| IsVirtual(pSubSrc->a[0].pTab) /* (3c) */
|
||||
|| (p->selFlags & SF_Distinct)!=0 /* (3d) */
|
||||
if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){
|
||||
if( pSubSrc->nSrc>1 /* (3a) */
|
||||
|| isAgg /* (3b) */
|
||||
|| IsVirtual(pSubSrc->a[0].pTab) /* (3c) */
|
||||
|| (p->selFlags & SF_Distinct)!=0 /* (3d) */
|
||||
|| (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
isOuterJoin = 1;
|
||||
}
|
||||
#ifdef SQLITE_EXTRA_IFNULLROW
|
||||
else if( iFrom>0 && !isAgg ){
|
||||
/* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for
|
||||
/* Setting isOuterJoin to -1 causes OP_IfNullRow opcodes to be generated for
|
||||
** every reference to any result column from subquery in a join, even
|
||||
** though they are not necessary. This will stress-test the OP_IfNullRow
|
||||
** opcode. */
|
||||
isLeftJoin = -1;
|
||||
isOuterJoin = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4204,7 +4236,7 @@ static int flattenSubquery(
|
||||
if( pSub->pOrderBy ){
|
||||
return 0; /* Restriction (20) */
|
||||
}
|
||||
if( isAgg || (p->selFlags & SF_Distinct)!=0 || isLeftJoin>0 ){
|
||||
if( isAgg || (p->selFlags & SF_Distinct)!=0 || isOuterJoin>0 ){
|
||||
return 0; /* (17d1), (17d2), or (17f) */
|
||||
}
|
||||
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
|
||||
@@ -4372,6 +4404,7 @@ static int flattenSubquery(
|
||||
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
|
||||
int nSubSrc;
|
||||
u8 jointype = 0;
|
||||
u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ;
|
||||
assert( pSub!=0 );
|
||||
pSubSrc = pSub->pSrc; /* FROM clause of subquery */
|
||||
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
|
||||
@@ -4410,10 +4443,12 @@ static int flattenSubquery(
|
||||
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
|
||||
assert( pItem->fg.isTabFunc==0 );
|
||||
*pItem = pSubSrc->a[i];
|
||||
pItem->fg.jointype |= ltorj;
|
||||
iNewParent = pSubSrc->a[i].iCursor;
|
||||
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
|
||||
}
|
||||
pSrc->a[iFrom].fg.jointype = jointype;
|
||||
pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
|
||||
pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
|
||||
|
||||
/* Now begin substituting subquery result set expressions for
|
||||
** references to the iParent in the outer query.
|
||||
@@ -4448,8 +4483,8 @@ static int flattenSubquery(
|
||||
}
|
||||
pWhere = pSub->pWhere;
|
||||
pSub->pWhere = 0;
|
||||
if( isLeftJoin>0 ){
|
||||
sqlite3SetJoinExpr(pWhere, iNewParent);
|
||||
if( isOuterJoin>0 ){
|
||||
sqlite3SetJoinExpr(pWhere, iNewParent, EP_FromJoin);
|
||||
}
|
||||
if( pWhere ){
|
||||
if( pParent->pWhere ){
|
||||
@@ -4463,7 +4498,7 @@ static int flattenSubquery(
|
||||
x.pParse = pParse;
|
||||
x.iTable = iParent;
|
||||
x.iNewTable = iNewParent;
|
||||
x.isLeftJoin = isLeftJoin;
|
||||
x.isOuterJoin = isOuterJoin;
|
||||
x.pEList = pSub->pEList;
|
||||
substSelect(&x, pParent, 0);
|
||||
}
|
||||
@@ -4924,7 +4959,7 @@ static int pushDownWhereTerms(
|
||||
x.pParse = pParse;
|
||||
x.iTable = iCursor;
|
||||
x.iNewTable = iCursor;
|
||||
x.isLeftJoin = 0;
|
||||
x.isOuterJoin = 0;
|
||||
x.pEList = pSubq->pEList;
|
||||
pNew = substExpr(&x, pNew);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
@@ -6503,7 +6538,7 @@ int sqlite3Select(
|
||||
/* Convert LEFT JOIN into JOIN if there are terms of the right table
|
||||
** of the LEFT JOIN used in the WHERE clause.
|
||||
*/
|
||||
if( (pItem->fg.jointype & JT_LEFT)!=0
|
||||
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==JT_LEFT
|
||||
&& sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor)
|
||||
&& OptimizationEnabled(db, SQLITE_SimplifyJoin)
|
||||
){
|
||||
@@ -6589,7 +6624,7 @@ int sqlite3Select(
|
||||
&& i==0
|
||||
&& (p->selFlags & SF_ComplexResult)!=0
|
||||
&& (pTabList->nSrc==1
|
||||
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0)
|
||||
|| (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0)
|
||||
){
|
||||
continue;
|
||||
}
|
||||
@@ -6711,6 +6746,7 @@ int sqlite3Select(
|
||||
if( OptimizationEnabled(db, SQLITE_PushDown)
|
||||
&& (pItem->fg.isCte==0
|
||||
|| (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2))
|
||||
&& (pItem->fg.jointype & JT_RIGHT)==0
|
||||
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
|
||||
(pItem->fg.jointype & JT_OUTER)!=0)
|
||||
){
|
||||
@@ -6731,18 +6767,19 @@ int sqlite3Select(
|
||||
|
||||
/* Generate code to implement the subquery
|
||||
**
|
||||
** The subquery is implemented as a co-routine if:
|
||||
** The subquery is implemented as a co-routine all of the following are
|
||||
** true:
|
||||
**
|
||||
** (1) the subquery is guaranteed to be the outer loop (so that
|
||||
** it does not need to be computed more than once), and
|
||||
** (2) the subquery is not a CTE that should be materialized
|
||||
**
|
||||
** TODO: Are there other reasons beside (1) and (2) to use a co-routine
|
||||
** implementation?
|
||||
** (3) the subquery is not part of a left operand for a RIGHT JOIN
|
||||
*/
|
||||
if( i==0
|
||||
&& (pTabList->nSrc==1
|
||||
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
|
||||
&& (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */
|
||||
|| (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) /* (1) */
|
||||
&& (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */
|
||||
&& (pTabList->a[0].fg.jointype & JT_LTORJ)==0 /* (3) */
|
||||
){
|
||||
/* Implement a co-routine that will return a single row of the result
|
||||
** set on each invocation.
|
||||
|
||||
@@ -2887,7 +2887,7 @@ struct Expr {
|
||||
#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
|
||||
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
|
||||
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
|
||||
/* 0x400000 // Available */
|
||||
#define EP_InnerJoin 0x400000 /* Originates in ON/USING of an inner join */
|
||||
#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
|
||||
#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
|
||||
#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
|
||||
@@ -3132,13 +3132,14 @@ struct SrcList {
|
||||
/*
|
||||
** Permitted values of the SrcList.a.jointype field
|
||||
*/
|
||||
#define JT_INNER 0x0001 /* Any kind of inner or cross join */
|
||||
#define JT_CROSS 0x0002 /* Explicit use of the CROSS keyword */
|
||||
#define JT_NATURAL 0x0004 /* True for a "natural" join */
|
||||
#define JT_LEFT 0x0008 /* Left outer join */
|
||||
#define JT_RIGHT 0x0010 /* Right outer join */
|
||||
#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */
|
||||
#define JT_ERROR 0x0040 /* unknown or unsupported join type */
|
||||
#define JT_INNER 0x01 /* Any kind of inner or cross join */
|
||||
#define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */
|
||||
#define JT_NATURAL 0x04 /* True for a "natural" join */
|
||||
#define JT_LEFT 0x08 /* Left outer join */
|
||||
#define JT_RIGHT 0x10 /* Right outer join */
|
||||
#define JT_OUTER 0x20 /* The "OUTER" keyword is present */
|
||||
#define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN */
|
||||
#define JT_ERROR 0x80 /* unknown or unsupported join type */
|
||||
|
||||
|
||||
/*
|
||||
@@ -3162,7 +3163,7 @@ struct SrcList {
|
||||
#define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */
|
||||
#define WHERE_AGG_DISTINCT 0x0400 /* Query is "SELECT agg(DISTINCT ...)" */
|
||||
#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */
|
||||
/* 0x1000 not currently used */
|
||||
#define WHERE_RIGHT_JOIN 0x1000 /* Processing a RIGHT JOIN */
|
||||
/* 0x2000 not currently used */
|
||||
#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */
|
||||
/* 0x8000 not currently used */
|
||||
@@ -4828,7 +4829,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
|
||||
|
||||
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
|
||||
int sqlite3ColumnIndex(Table *pTab, const char *zCol);
|
||||
void sqlite3SetJoinExpr(Expr*,int);
|
||||
void sqlite3SetJoinExpr(Expr*,int,u32);
|
||||
void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
|
||||
void sqlite3DeferForeignKey(Parse*, int);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
|
||||
@@ -144,11 +144,18 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
|
||||
sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx",
|
||||
pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed);
|
||||
}
|
||||
if( pItem->fg.jointype & JT_LEFT ){
|
||||
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){
|
||||
sqlite3_str_appendf(&x, " FULL-OUTER-JOIN");
|
||||
}else if( pItem->fg.jointype & JT_LEFT ){
|
||||
sqlite3_str_appendf(&x, " LEFT-JOIN");
|
||||
}else if( pItem->fg.jointype & JT_RIGHT ){
|
||||
sqlite3_str_appendf(&x, " RIGHT-JOIN");
|
||||
}else if( pItem->fg.jointype & JT_CROSS ){
|
||||
sqlite3_str_appendf(&x, " CROSS-JOIN");
|
||||
}
|
||||
if( pItem->fg.jointype & JT_LTORJ ){
|
||||
sqlite3_str_appendf(&x, " LTORJ");
|
||||
}
|
||||
if( pItem->fg.fromDDL ){
|
||||
sqlite3_str_appendf(&x, " DDL");
|
||||
}
|
||||
|
||||
10
src/vdbe.c
10
src/vdbe.c
@@ -993,8 +993,12 @@ jump_to_p2:
|
||||
|
||||
/* Opcode: Return P1 P2 P3 * *
|
||||
**
|
||||
** Jump to the next instruction after the address in register P1. After
|
||||
** the jump, register P1 becomes undefined.
|
||||
** Jump to the next instruction after the address stored in register P1.
|
||||
**
|
||||
** It used to be that after the jump, register P1 would become undefined.
|
||||
** However, for the subroutine used for the inner loop of a RIGHT JOIN,
|
||||
** it is useful for R1 register to be unchanged, so that is what happens
|
||||
** now.
|
||||
**
|
||||
** P2 is not used by the byte-code engine. However, if P2 is positive
|
||||
** and also less than the current address, then the "EXPLAIN" output
|
||||
@@ -1012,7 +1016,7 @@ case OP_Return: { /* in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
assert( pIn1->flags==MEM_Int );
|
||||
pOp = &aOp[pIn1->u.i];
|
||||
pIn1->flags = MEM_Undefined;
|
||||
/* pIn1->flags = MEM_Undefined; */
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
130
src/where.c
130
src/where.c
@@ -756,13 +756,13 @@ static int termCanDriveIndex(
|
||||
char aff;
|
||||
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
|
||||
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0;
|
||||
if( (pSrc->fg.jointype & JT_LEFT)
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
||||
&& (pTerm->eOperator & WO_IS)
|
||||
){
|
||||
/* Cannot use an IS term from the WHERE clause as an index driver for
|
||||
** the RHS of a LEFT JOIN. Such a term can only be used if it is from
|
||||
** the ON clause. */
|
||||
** the RHS of a LEFT JOIN or for the LHS of a RIGHT JOIN. Such a term
|
||||
** can only be used if it is from the ON clause. */
|
||||
return 0;
|
||||
}
|
||||
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
|
||||
@@ -832,7 +832,8 @@ 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
|
||||
&& ((pSrc->fg.jointype&JT_LEFT)==0 || ExprHasProperty(pExpr,EP_FromJoin))
|
||||
&& ((pSrc->fg.jointype&(JT_LEFT|JT_LTORJ))==0
|
||||
|| ExprHasProperty(pExpr,EP_FromJoin))
|
||||
&& sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor)
|
||||
){
|
||||
pPartial = sqlite3ExprAnd(pParse, pPartial,
|
||||
@@ -1105,7 +1106,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
|
||||
const SrcItem *pTabItem;
|
||||
pLevel = &pWInfo->a[iLevel];
|
||||
pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
|
||||
if( pTabItem->fg.jointype & JT_LEFT ) continue;
|
||||
if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue;
|
||||
pLoop = pLevel->pWLoop;
|
||||
if( NEVER(pLoop==0) ) continue;
|
||||
if( pLoop->prereq & notReady ) continue;
|
||||
@@ -1178,9 +1179,10 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
assert( pTerm->u.x.leftColumn<pTab->nCol );
|
||||
|
||||
/* tag-20191211-002: WHERE-clause constraints are not useful to the
|
||||
** right-hand table of a LEFT JOIN. See tag-20191211-001 for the
|
||||
** right-hand table of a LEFT JOIN nor to the left-hand table of a
|
||||
** RIGHT JOIN. See tag-20191211-001 for the
|
||||
** equivalent restriction for ordinary tables. */
|
||||
if( (pSrc->fg.jointype & JT_LEFT)!=0
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
||||
){
|
||||
continue;
|
||||
@@ -2621,10 +2623,11 @@ static void whereLoopOutputAdjust(
|
||||
**
|
||||
** 2022-03-24: Self-culling only applies if either the extra terms
|
||||
** are straight comparison operators that are non-true with NULL
|
||||
** operand, or if the loop is not a LEFT JOIN.
|
||||
** operand, or if the loop is not an OUTER JOIN.
|
||||
*/
|
||||
if( (pTerm->eOperator & 0x3f)!=0
|
||||
|| (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype & JT_LEFT)==0
|
||||
|| (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype
|
||||
& (JT_LEFT|JT_LTORJ))==0
|
||||
){
|
||||
pLoop->wsFlags |= WHERE_SELFCULL;
|
||||
}
|
||||
@@ -2831,9 +2834,10 @@ static int whereLoopAddBtreeIndex(
|
||||
if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue;
|
||||
|
||||
/* tag-20191211-001: Do not allow constraints from the WHERE clause to
|
||||
** be used by the right table of a LEFT JOIN. Only constraints in the
|
||||
** be used by the right table of a LEFT JOIN nor by the left table of a
|
||||
** RIGHT JOIN. Only constraints in the
|
||||
** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */
|
||||
if( (pSrc->fg.jointype & JT_LEFT)!=0
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
||||
){
|
||||
continue;
|
||||
@@ -3320,6 +3324,7 @@ static int whereLoopAddBtree(
|
||||
&& HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
|
||||
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
|
||||
&& !pSrc->fg.isRecursive /* Not a recursive common table expression. */
|
||||
&& (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */
|
||||
){
|
||||
/* Generate auto-index WhereLoops */
|
||||
LogEst rLogSize; /* Logarithm of the number of rows in the table */
|
||||
@@ -4011,6 +4016,9 @@ static int whereLoopAddOr(
|
||||
pItem = pWInfo->pTabList->a + pNew->iTab;
|
||||
iCur = pItem->iCursor;
|
||||
|
||||
/* The multi-index OR optimization does not work for RIGHT and FULL JOIN */
|
||||
if( pItem->fg.jointype & JT_RIGHT ) return SQLITE_OK;
|
||||
|
||||
for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
|
||||
if( (pTerm->eOperator & WO_OR)!=0
|
||||
&& (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0
|
||||
@@ -4135,9 +4143,9 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
|
||||
pNew->iTab = iTab;
|
||||
pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR;
|
||||
pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
|
||||
if( (pItem->fg.jointype & (JT_LEFT|JT_CROSS))!=0 ){
|
||||
if( (pItem->fg.jointype & (JT_OUTER|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. */
|
||||
** right-hand-side of a OUTER or CROSS JOIN. */
|
||||
mPrereq = mPrior;
|
||||
}else{
|
||||
mPrereq = 0;
|
||||
@@ -4146,7 +4154,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
|
||||
if( IsVirtual(pItem->pTab) ){
|
||||
SrcItem *p;
|
||||
for(p=&pItem[1]; p<pEnd; p++){
|
||||
if( mUnusable || (p->fg.jointype & (JT_LEFT|JT_CROSS)) ){
|
||||
if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){
|
||||
mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
|
||||
}
|
||||
}
|
||||
@@ -5762,8 +5770,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
/* noop */
|
||||
}else
|
||||
#endif
|
||||
if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
|
||||
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){
|
||||
if( ((pLoop->wsFlags & WHERE_IDX_ONLY)==0
|
||||
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0)
|
||||
|| (pTabItem->fg.jointype & (JT_LTORJ|JT_RIGHT))!=0
|
||||
){
|
||||
int op = OP_OpenRead;
|
||||
if( pWInfo->eOnePass!=ONEPASS_OFF ){
|
||||
op = OP_OpenWrite;
|
||||
@@ -5865,6 +5875,37 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
}
|
||||
if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb);
|
||||
if( (pTabItem->fg.jointype & JT_RIGHT)!=0
|
||||
&& (pLevel->pRJ = sqlite3WhereMalloc(pWInfo, sizeof(WhereRightJoin)))!=0
|
||||
){
|
||||
WhereRightJoin *pRJ = pLevel->pRJ;
|
||||
pRJ->iMatch = pParse->nTab++;
|
||||
pRJ->regBloom = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom);
|
||||
pRJ->regReturn = ++pParse->nMem;
|
||||
pRJ->addrInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, pRJ->regReturn);
|
||||
assert( pTab==pTabItem->pTab );
|
||||
if( HasRowid(pTab) ){
|
||||
KeyInfo *pInfo;
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1);
|
||||
pInfo = sqlite3KeyInfoAlloc(pParse->db, 1, 0);
|
||||
if( pInfo ){
|
||||
pInfo->aColl[0] = 0;
|
||||
pInfo->aSortFlags[0] = 0;
|
||||
sqlite3VdbeAppendP4(v, pInfo, P4_KEYINFO);
|
||||
}
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, pPk->nKeyCol);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
}
|
||||
pLoop->wsFlags &= ~WHERE_IDX_ONLY;
|
||||
/* The nature of RIGHT JOIN processing is such that it messes up
|
||||
** the output order. So omit any ORDER BY/GROUP BY elimination
|
||||
** optimizations. We need to do an actual sort for RIGHT JOIN. */
|
||||
pWInfo->nOBSat = 0;
|
||||
pWInfo->eDistinct = WHERE_DISTINCT_UNORDERED;
|
||||
}
|
||||
}
|
||||
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
|
||||
if( db->mallocFailed ) goto whereBeginError;
|
||||
@@ -5977,6 +6018,17 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
for(i=pWInfo->nLevel-1; i>=0; i--){
|
||||
int addr;
|
||||
pLevel = &pWInfo->a[i];
|
||||
if( pLevel->pRJ ){
|
||||
/* Terminate the subroutine that forms the interior of the loop of
|
||||
** the RIGHT JOIN table */
|
||||
WhereRightJoin *pRJ = pLevel->pRJ;
|
||||
int addrHere = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeChangeP1(v, pRJ->addrSubrtn-1, addrHere);
|
||||
sqlite3VdbeChangeP1(v, pRJ->addrInit, addrHere);
|
||||
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
|
||||
pLevel->addrCont = 0;
|
||||
sqlite3VdbeAddOp2(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn);
|
||||
}
|
||||
pLoop = pLevel->pWLoop;
|
||||
if( pLevel->op!=OP_Noop ){
|
||||
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
|
||||
@@ -6004,7 +6056,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
}
|
||||
#endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */
|
||||
/* The common case: Advance to the next row */
|
||||
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
|
||||
if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont);
|
||||
sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
|
||||
sqlite3VdbeChangeP5(v, pLevel->p5);
|
||||
VdbeCoverage(v);
|
||||
@@ -6019,7 +6071,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
|
||||
if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek);
|
||||
#endif
|
||||
}else{
|
||||
}else if( pLevel->addrCont ){
|
||||
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
|
||||
}
|
||||
if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){
|
||||
@@ -6112,11 +6164,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
pWInfo->pTabList->a[pLevel->iFrom].pTab->zName));
|
||||
}
|
||||
|
||||
/* The "break" point is here, just past the end of the outer loop.
|
||||
** Set it.
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
|
||||
|
||||
assert( pWInfo->nLevel<=pTabList->nSrc );
|
||||
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
|
||||
int k, last;
|
||||
@@ -6127,6 +6174,15 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
assert( pTab!=0 );
|
||||
pLoop = pLevel->pWLoop;
|
||||
|
||||
/* Do RIGHT JOIN processing. Generate code that will output the
|
||||
** unmatched rows of the right operand of the RIGHT JOIN with
|
||||
** all of the columns of the left operand set to NULL.
|
||||
*/
|
||||
if( pLevel->pRJ ){
|
||||
sqlite3WhereRightJoinLoop(pWInfo, i, pLevel);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* For a co-routine, change all OP_Column references to the table of
|
||||
** the co-routine into OP_Copy of result contained in a register.
|
||||
** OP_Rowid becomes OP_Null.
|
||||
@@ -6138,29 +6194,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE
|
||||
/* Close all of the cursors that were opened by sqlite3WhereBegin.
|
||||
** Except, do not close cursors that will be reused by the OR optimization
|
||||
** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors
|
||||
** created for the ONEPASS optimization.
|
||||
*/
|
||||
if( (pTab->tabFlags & TF_Ephemeral)==0
|
||||
&& !IsView(pTab)
|
||||
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
|
||||
){
|
||||
int ws = pLoop->wsFlags;
|
||||
if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
|
||||
}
|
||||
if( (ws & WHERE_INDEXED)!=0
|
||||
&& (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0
|
||||
&& pLevel->iIdxCur!=pWInfo->aiCurOnePass[1]
|
||||
){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If this scan uses an index, make VDBE code substitutions to read data
|
||||
** from the index instead of from the table where possible. In some cases
|
||||
** this optimization prevents the table from ever being read, which can
|
||||
@@ -6261,6 +6294,11 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
}
|
||||
}
|
||||
|
||||
/* The "break" point is here, just past the end of the outer loop.
|
||||
** Set it.
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
|
||||
|
||||
/* Final cleanup
|
||||
*/
|
||||
if( pWInfo->pExprMods ) whereUndoExprMods(pWInfo);
|
||||
|
||||
@@ -33,6 +33,7 @@ typedef struct WhereScan WhereScan;
|
||||
typedef struct WhereOrCost WhereOrCost;
|
||||
typedef struct WhereOrSet WhereOrSet;
|
||||
typedef struct WhereMemBlock WhereMemBlock;
|
||||
typedef struct WhereRightJoin WhereRightJoin;
|
||||
|
||||
/*
|
||||
** This object is a header on a block of allocated memory that will be
|
||||
@@ -43,6 +44,17 @@ struct WhereMemBlock {
|
||||
u8 sz; /* Bytes of space */
|
||||
};
|
||||
|
||||
/*
|
||||
** Extra information attached to a WhereLevel that is a RIGHT JOIN.
|
||||
*/
|
||||
struct WhereRightJoin {
|
||||
int iMatch; /* Cursor used to determine prior matched rows */
|
||||
int regBloom; /* Bloom filter for iRJMatch */
|
||||
int regReturn; /* Return register for the interior subroutine */
|
||||
int addrSubrtn; /* Starting address for the interior subroutine */
|
||||
int addrInit; /* OP_Integer used for early init of regReturn */
|
||||
};
|
||||
|
||||
/*
|
||||
** This object contains information needed to implement a single nested
|
||||
** loop in WHERE clause.
|
||||
@@ -75,6 +87,7 @@ struct WhereLevel {
|
||||
int addrLikeRep; /* LIKE range processing address */
|
||||
#endif
|
||||
int regFilter; /* Bloom filter */
|
||||
WhereRightJoin *pRJ; /* Extra information for RIGHT JOIN */
|
||||
u8 iFrom; /* Which entry in the FROM clause */
|
||||
u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */
|
||||
int p1, p2; /* Operands of the opcode used to end the loop */
|
||||
@@ -552,6 +565,11 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
WhereLevel *pLevel, /* The current level pointer */
|
||||
Bitmask notReady /* Which tables are currently available */
|
||||
);
|
||||
SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
|
||||
WhereInfo *pWInfo,
|
||||
int iLevel,
|
||||
WhereLevel *pLevel
|
||||
);
|
||||
|
||||
/* whereexpr.c: */
|
||||
void sqlite3WhereClauseInit(WhereClause*,WhereInfo*);
|
||||
|
||||
146
src/wherecode.c
146
src/wherecode.c
@@ -204,6 +204,9 @@ int sqlite3WhereExplainOneScan(
|
||||
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
|
||||
}
|
||||
#endif
|
||||
if( pItem->fg.jointype & JT_LEFT ){
|
||||
sqlite3_str_appendf(&str, " LEFT-JOIN");
|
||||
}
|
||||
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
|
||||
if( pLoop->nOut>=10 ){
|
||||
sqlite3_str_appendf(&str, " (~%llu rows)",
|
||||
@@ -1150,7 +1153,7 @@ static void codeDeferredSeek(
|
||||
|
||||
pWInfo->bDeferredSeek = 1;
|
||||
sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur);
|
||||
if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
|
||||
if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))
|
||||
&& DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
|
||||
){
|
||||
int i;
|
||||
@@ -1502,7 +1505,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** initialize a memory cell that records if this table matches any
|
||||
** row of the left table of the join.
|
||||
*/
|
||||
assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
|
||||
assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))
|
||||
|| pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0
|
||||
);
|
||||
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
|
||||
@@ -2140,7 +2143,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
|
||||
/* Seek the table cursor, if required */
|
||||
omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
|
||||
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0;
|
||||
&& (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0;
|
||||
if( omitTable ){
|
||||
/* pIdx is a covering index. No need to access the main table. */
|
||||
}else if( HasRowid(pIdx->pTable) ){
|
||||
@@ -2174,7 +2177,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** move forward to the next index.
|
||||
** https://sqlite.org/src/info/4e8e4857d32d401f
|
||||
*/
|
||||
if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){
|
||||
if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ){
|
||||
whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
|
||||
}
|
||||
|
||||
@@ -2193,7 +2196,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
/* The following assert() is not a requirement, merely an observation:
|
||||
** The OR-optimization doesn't work for the right hand table of
|
||||
** a LEFT JOIN: */
|
||||
assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 );
|
||||
assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 );
|
||||
}
|
||||
|
||||
/* Record the instruction used to terminate the loop. */
|
||||
@@ -2605,7 +2608,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}
|
||||
pE = pTerm->pExpr;
|
||||
assert( pE!=0 );
|
||||
if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){
|
||||
if( (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ))
|
||||
&& !ExprHasProperty(pE,EP_FromJoin)
|
||||
){
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2667,7 +2672,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue;
|
||||
if( (pTerm->eOperator & WO_EQUIV)==0 ) continue;
|
||||
if( pTerm->leftCursor!=iCur ) continue;
|
||||
if( pTabItem->fg.jointype & JT_LEFT ) continue;
|
||||
if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue;
|
||||
pE = pTerm->pExpr;
|
||||
#ifdef WHERETRACE_ENABLED /* 0x800 */
|
||||
if( sqlite3WhereTrace & 0x800 ){
|
||||
@@ -2719,6 +2724,55 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}
|
||||
}
|
||||
|
||||
/* For a RIGHT OUTER JOIN, record the fact that the current row has
|
||||
** been matched at least once.
|
||||
*/
|
||||
if( pLevel->pRJ ){
|
||||
Table *pTab;
|
||||
int nPk;
|
||||
int r;
|
||||
int jmp1 = 0;
|
||||
WhereRightJoin *pRJ = pLevel->pRJ;
|
||||
|
||||
/* pTab is the right-hand table of the RIGHT JOIN. Generate code that
|
||||
** will record that the current row of that table has been matched at
|
||||
** least once. This is accomplished by storing the PK for the row in
|
||||
** both the iMatch index and the regBloom Bloom filter.
|
||||
*/
|
||||
pTab = pWInfo->pTabList->a[pLevel->iFrom].pTab;
|
||||
if( HasRowid(pTab) ){
|
||||
r = sqlite3GetTempRange(pParse, 2);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1);
|
||||
nPk = 1;
|
||||
}else{
|
||||
int iPk;
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
nPk = pPk->nKeyCol;
|
||||
r = sqlite3GetTempRange(pParse, nPk+1);
|
||||
for(iPk=0; iPk<nPk; iPk++){
|
||||
int iCol = pPk->aiColumn[iPk];
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+1+iPk);
|
||||
}
|
||||
}
|
||||
jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, 0, r+1, nPk);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, r+1, nPk, r);
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pRJ->iMatch, r, r+1, nPk);
|
||||
sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pRJ->regBloom, 0, r+1, nPk);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
sqlite3VdbeJumpHere(v, jmp1);
|
||||
sqlite3ReleaseTempRange(pParse, r, nPk+1);
|
||||
|
||||
/* Create a subroutine used to process all interior loops and code
|
||||
** of the RIGHT JOIN. During normal operation, the subroutine will
|
||||
** be in-line with the rest of the code. But at the end, a separate
|
||||
** loop will run that invokes this subroutine for unmatched rows
|
||||
** of pTab, with all tables to left begin set to NULL.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pRJ->regReturn);
|
||||
pRJ->addrSubrtn = sqlite3VdbeCurrentAddr(v);
|
||||
}
|
||||
|
||||
#if WHERETRACE_ENABLED /* 0x20800 */
|
||||
if( sqlite3WhereTrace & 0x20000 ){
|
||||
sqlite3DebugPrintf("All WHERE-clause terms after coding level %d:\n",
|
||||
@@ -2732,3 +2786,81 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
#endif
|
||||
return pLevel->notReady;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate the code for the loop that finds all non-matched terms
|
||||
** for a RIGHT JOIN.
|
||||
*/
|
||||
SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
|
||||
WhereInfo *pWInfo,
|
||||
int iLevel,
|
||||
WhereLevel *pLevel
|
||||
){
|
||||
Parse *pParse = pWInfo->pParse;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
WhereRightJoin *pRJ = pLevel->pRJ;
|
||||
Expr *pSubWhere = 0;
|
||||
WhereClause *pWC = &pWInfo->sWC;
|
||||
WhereInfo *pSubWInfo;
|
||||
WhereLoop *pLoop = pLevel->pWLoop;
|
||||
SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
|
||||
SrcList sFrom;
|
||||
Bitmask mAll = 0;
|
||||
int k;
|
||||
|
||||
for(k=0; k<iLevel; k++){
|
||||
int iIdxCur;
|
||||
mAll |= pWInfo->a[k].pWLoop->maskSelf;
|
||||
sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur);
|
||||
iIdxCur = pWInfo->a[k].iIdxCur;
|
||||
if( iIdxCur ){
|
||||
sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur);
|
||||
}
|
||||
}
|
||||
mAll |= pLoop->maskSelf;
|
||||
for(k=0; k<pWC->nTerm; k++){
|
||||
WhereTerm *pTerm = &pWC->a[k];
|
||||
if( pTerm->wtFlags & TERM_VIRTUAL ) break;
|
||||
if( pTerm->prereqAll & ~mAll ) continue;
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin|EP_InnerJoin) ) continue;
|
||||
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
|
||||
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
|
||||
}
|
||||
sFrom.nSrc = 1;
|
||||
sFrom.nAlloc = 1;
|
||||
memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem));
|
||||
sFrom.a[0].fg.jointype = 0;
|
||||
ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName));
|
||||
pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0,
|
||||
WHERE_RIGHT_JOIN, 0);
|
||||
if( pSubWInfo ){
|
||||
int iCur = pLevel->iTabCur;
|
||||
int r = ++pParse->nMem;
|
||||
int nPk;
|
||||
int jmp;
|
||||
int addrCont = sqlite3WhereContinueLabel(pSubWInfo);
|
||||
Table *pTab = pTabItem->pTab;
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r);
|
||||
nPk = 1;
|
||||
}else{
|
||||
int iPk;
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
nPk = pPk->nKeyCol;
|
||||
pParse->nMem += nPk - 1;
|
||||
for(iPk=0; iPk<nPk; iPk++){
|
||||
int iCol = pPk->aiColumn[iPk];
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+iPk);
|
||||
}
|
||||
}
|
||||
jmp = sqlite3VdbeAddOp4Int(v, OP_Filter, pRJ->regBloom, 0, r, nPk);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, addrCont, r, nPk);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, jmp);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pRJ->regReturn, pRJ->addrSubrtn);
|
||||
sqlite3WhereEnd(pSubWInfo);
|
||||
}
|
||||
sqlite3ExprDelete(pParse->db, pSubWhere);
|
||||
ExplainQueryPlanPop(pParse);
|
||||
}
|
||||
|
||||
@@ -1809,6 +1809,7 @@ void sqlite3WhereTabFuncArgs(
|
||||
if( pArgs==0 ) return;
|
||||
for(j=k=0; j<pArgs->nExpr; j++){
|
||||
Expr *pRhs;
|
||||
u32 joinType;
|
||||
while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;}
|
||||
if( k>=pTab->nCol ){
|
||||
sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d",
|
||||
@@ -1825,9 +1826,12 @@ void sqlite3WhereTabFuncArgs(
|
||||
pRhs = sqlite3PExpr(pParse, TK_UPLUS,
|
||||
sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
|
||||
pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
|
||||
if( pItem->fg.jointype & JT_LEFT ){
|
||||
sqlite3SetJoinExpr(pTerm, pItem->iCursor);
|
||||
if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ) ){
|
||||
joinType = EP_FromJoin;
|
||||
}else{
|
||||
joinType = EP_InnerJoin;
|
||||
}
|
||||
sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType);
|
||||
whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user