diff options
author | drh <drh@noemail.net> | 2006-12-16 16:25:15 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2006-12-16 16:25:15 +0000 |
commit | 61dfc31d807d8958c18a37c0dcb0c155bd349b69 (patch) | |
tree | 44916e207f8ed871edf708e6ad60af6b90a86e0f /src | |
parent | f0fa1c1b9fca7b7c849ffaf2bc15d6cf9f97ff2e (diff) | |
download | sqlite-61dfc31d807d8958c18a37c0dcb0c155bd349b69.tar.gz sqlite-61dfc31d807d8958c18a37c0dcb0c155bd349b69.zip |
Query optimizer enhancement: In "FROM a,b,c left join d" allow the C table
to be reordered with A and B. This used to be the case but the capability
was removed by (3203) and (3052) in response to ticket #1652. This change
restores the capability. (CVS 3529)
FossilOrigin-Name: 7393c81b8cb9d4344ae744de9eabcb3af64f1db8
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 79 | ||||
-rw-r--r-- | src/expr.c | 35 | ||||
-rw-r--r-- | src/parse.y | 31 | ||||
-rw-r--r-- | src/select.c | 31 | ||||
-rw-r--r-- | src/sqliteInt.h | 6 | ||||
-rw-r--r-- | src/where.c | 7 |
6 files changed, 117 insertions, 72 deletions
diff --git a/src/build.c b/src/build.c index eeceded71..00b89f2fd 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.411 2006/09/11 23:45:49 drh Exp $ +** $Id: build.c,v 1.412 2006/12/16 16:25:15 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -2941,15 +2941,6 @@ void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ } /* -** Add an alias to the last identifier on the given identifier list. -*/ -void sqlite3SrcListAddAlias(SrcList *pList, Token *pToken){ - if( pList && pList->nSrc>0 ){ - pList->a[pList->nSrc-1].zAlias = sqlite3NameFromToken(pToken); - } -} - -/* ** Delete an entire SrcList including all its substructure. */ void sqlite3SrcListDelete(SrcList *pList){ @@ -2969,6 +2960,74 @@ void sqlite3SrcListDelete(SrcList *pList){ } /* +** This routine is called by the parser to add a new term to the +** end of a growing FROM clause. The "p" parameter is the part of +** the FROM clause that has already been constructed. "p" is NULL +** if this is the first term of the FROM clause. pTable and pDatabase +** are the name of the table and database named in the FROM clause term. +** pDatabase is NULL if the database name qualifier is missing - the +** usual case. If the term has a alias, then pAlias points to the +** alias token. If the term is a subquery, then pSubquery is the +** SELECT statement that the subquery encodes. The pTable and +** pDatabase parameters are NULL for subqueries. The pOn and pUsing +** parameters are the content of the ON and USING clauses. +** +** Return a new SrcList which encodes is the FROM with the new +** term added. +*/ +SrcList *sqlite3SrcListAppendFromTerm( + SrcList *p, /* The left part of the FROM clause already seen */ + Token *pTable, /* Name of the table to add to the FROM clause */ + Token *pDatabase, /* Name of the database containing pTable */ + Token *pAlias, /* The right-hand side of the AS subexpression */ + Select *pSubquery, /* A subquery used in place of a table name */ + Expr *pOn, /* The ON clause of a join */ + IdList *pUsing /* The USING clause of a join */ +){ + struct SrcList_item *pItem; + p = sqlite3SrcListAppend(p, pTable, pDatabase); + if( p==0 || p->nSrc==0 ){ + sqlite3ExprDelete(pOn); + sqlite3IdListDelete(pUsing); + sqlite3SelectDelete(pSubquery); + return p; + } + pItem = &p->a[p->nSrc-1]; + if( pAlias && pAlias->n ){ + pItem->zAlias = sqlite3NameFromToken(pAlias); + } + pItem->pSelect = pSubquery; + pItem->pOn = pOn; + pItem->pUsing = pUsing; + return p; +} + +/* +** When building up a FROM clause in the parser, the join operator +** is initially attached to the left operand. But the code generator +** expects the join operator to be on the right operand. This routine +** Shifts all join operators from left to right for an entire FROM +** clause. +** +** Example: Suppose the join is like this: +** +** A natural cross join B +** +** 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. +*/ +void sqlite3SrcListShiftJoinType(SrcList *p){ + if( p && p->a ){ + int i; + for(i=p->nSrc-1; i>0; i--){ + p->a[i].jointype = p->a[i-1].jointype; + } + p->a[0].jointype = 0; + } +} + +/* ** Begin a transaction */ void sqlite3BeginTransaction(Parse *pParse, int type){ diff --git a/src/expr.c b/src/expr.c index 2ea27f5b8..1d5e186a0 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.269 2006/11/23 11:59:13 drh Exp $ +** $Id: expr.c,v 1.270 2006/12/16 16:25:15 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -891,22 +891,23 @@ static int lookupName( pExpr->iColumn = j==pTab->iPKey ? -1 : j; pExpr->affinity = pTab->aCol[j].affinity; pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0); - if( pItem->jointype & JT_NATURAL ){ - /* If this match occurred in the left table of a natural join, - ** then skip the right table to avoid a duplicate match */ - pItem++; - i++; - } - if( (pUsing = pItem->pUsing)!=0 ){ - /* If this match occurs on a column that is in the USING clause - ** of a join, skip the search of the right table of the join - ** to avoid a duplicate match there. */ - int k; - for(k=0; k<pUsing->nId; k++){ - if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){ - pItem++; - i++; - break; + if( i<pSrcList->nSrc-1 ){ + if( pItem[1].jointype & JT_NATURAL ){ + /* If this match occurred in the left table of a natural join, + ** then skip the right table to avoid a duplicate match */ + pItem++; + i++; + }else if( (pUsing = pItem[1].pUsing)!=0 ){ + /* If this match occurs on a column that is in the USING clause + ** of a join, skip the search of the right table of the join + ** to avoid a duplicate match there. */ + int k; + for(k=0; k<pUsing->nId; k++){ + if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){ + pItem++; + i++; + break; + } } } } diff --git a/src/parse.y b/src/parse.y index 2ba60a7a3..eac525ea1 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.210 2006/09/21 11:02:17 drh Exp $ +** @(#) $Id: parse.y,v 1.211 2006/12/16 16:25:15 drh Exp $ */ // All token codes are small integers with #defines that begin with "TK_" @@ -444,7 +444,10 @@ as(X) ::= . {X.n = 0;} // A complete FROM clause. // from(A) ::= . {A = sqliteMalloc(sizeof(*A));} -from(A) ::= FROM seltablist(X). {A = X;} +from(A) ::= FROM seltablist(X). { + A = X; + sqlite3SrcListShiftJoinType(A); +} // "seltablist" is a "Select Table List" - the content of the FROM clause // in a SELECT statement. "stl_prefix" is a prefix of this list. @@ -455,31 +458,12 @@ stl_prefix(A) ::= seltablist(X) joinop(Y). { } stl_prefix(A) ::= . {A = 0;} seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). { - A = sqlite3SrcListAppend(X,&Y,&D); - if( Z.n ) sqlite3SrcListAddAlias(A,&Z); - if( N ){ - if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; } - else { sqlite3ExprDelete(N); } - } - if( U ){ - if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; } - else { sqlite3IdListDelete(U); } - } + A = sqlite3SrcListAppendFromTerm(X,&Y,&D,&Z,0,N,U); } %ifndef SQLITE_OMIT_SUBQUERY seltablist(A) ::= stl_prefix(X) LP seltablist_paren(S) RP as(Z) on_opt(N) using_opt(U). { - A = sqlite3SrcListAppend(X,0,0); - if( A && A->nSrc>0 ) A->a[A->nSrc-1].pSelect = S; - if( Z.n ) sqlite3SrcListAddAlias(A,&Z); - if( N ){ - if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; } - else { sqlite3ExprDelete(N); } - } - if( U ){ - if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; } - else { sqlite3IdListDelete(U); } - } + A = sqlite3SrcListAppendFromTerm(X,0,0,&Z,S,N,U); } // A seltablist_paren nonterminal represents anything in a FROM that @@ -490,6 +474,7 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). { %destructor seltablist_paren {sqlite3SelectDelete($$);} seltablist_paren(A) ::= select(S). {A = S;} seltablist_paren(A) ::= seltablist(F). { + sqlite3SrcListShiftJoinType(F); A = sqlite3SelectNew(0,F,0,0,0,0,0,0,0); } %endif SQLITE_OMIT_SUBQUERY diff --git a/src/select.c b/src/select.c index 68e348ff9..343a30558 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.322 2006/10/13 15:34:17 drh Exp $ +** $Id: select.c,v 1.323 2006/12/16 16:25:15 drh Exp $ */ #include "sqliteInt.h" @@ -301,8 +301,8 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ /* When the NATURAL keyword is present, add WHERE clause terms for ** every column that the two tables have in common. */ - if( pLeft->jointype & JT_NATURAL ){ - if( pLeft->pOn || pLeft->pUsing ){ + if( pRight->jointype & JT_NATURAL ){ + if( pRight->pOn || pRight->pUsing ){ sqlite3ErrorMsg(pParse, "a NATURAL join may not have " "an ON or USING clause", 0); return 1; @@ -320,7 +320,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ /* Disallow both ON and USING clauses in the same join */ - if( pLeft->pOn && pLeft->pUsing ){ + if( pRight->pOn && pRight->pUsing ){ sqlite3ErrorMsg(pParse, "cannot have both ON and USING " "clauses in the same join"); return 1; @@ -329,10 +329,10 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ /* Add the ON clause to the end of the WHERE clause, connected by ** an AND operator. */ - if( pLeft->pOn ){ - setJoinExpr(pLeft->pOn, pRight->iCursor); - p->pWhere = sqlite3ExprAnd(p->pWhere, pLeft->pOn); - pLeft->pOn = 0; + if( pRight->pOn ){ + setJoinExpr(pRight->pOn, pRight->iCursor); + p->pWhere = sqlite3ExprAnd(p->pWhere, pRight->pOn); + pRight->pOn = 0; } /* Create extra terms on the WHERE clause for each column named @@ -342,8 +342,8 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ ** Report an error if any column mentioned in the USING clause is ** not contained in both tables to be joined. */ - if( pLeft->pUsing ){ - IdList *pList = pLeft->pUsing; + if( pRight->pUsing ){ + IdList *pList = pRight->pUsing; for(j=0; j<pList->nId; j++){ char *zName = pList->a[j].zName; if( columnIndex(pLeftTab, zName)<0 || columnIndex(pRightTab, zName)<0 ){ @@ -1309,13 +1309,13 @@ static int prepSelectStmt(Parse *pParse, Select *p){ if( i>0 ){ struct SrcList_item *pLeft = &pTabList->a[i-1]; - if( (pLeft->jointype & JT_NATURAL)!=0 && + if( (pLeft[1].jointype & JT_NATURAL)!=0 && columnIndex(pLeft->pTab, zName)>=0 ){ /* In a NATURAL join, omit the join columns from the ** table on the right */ continue; } - if( sqlite3IdListIndex(pLeft->pUsing, zName)>=0 ){ + if( sqlite3IdListIndex(pLeft[1].pUsing, zName)>=0 ){ /* In a join with a USING clause, omit columns in the ** using clause from the table on the right. */ continue; @@ -2175,7 +2175,7 @@ static int flattenSubquery( ** ** which is not at all the same thing. */ - if( pSubSrc->nSrc>1 && iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0 ){ + if( pSubSrc->nSrc>1 && (pSubitem->jointype & JT_OUTER)!=0 ){ return 0; } @@ -2192,8 +2192,7 @@ static int flattenSubquery( ** 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. */ - if( iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0 - && pSub->pWhere!=0 ){ + if( (pSubitem->jointype & JT_OUTER)!=0 && pSub->pWhere!=0 ){ return 0; } @@ -2232,7 +2231,7 @@ static int flattenSubquery( pSrc->a[i+iFrom] = pSubSrc->a[i]; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } - pSrc->a[iFrom+nSubSrc-1].jointype = jointype; + pSrc->a[iFrom].jointype = jointype; } /* Now begin substituting subquery result set expressions for diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a3d4317d2..b000f0ae3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.530 2006/11/09 00:24:54 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.531 2006/12/16 16:25:16 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1617,7 +1617,9 @@ int sqlite3ArrayAllocate(void**,int,int); IdList *sqlite3IdListAppend(IdList*, Token*); int sqlite3IdListIndex(IdList*,const char*); SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*); -void sqlite3SrcListAddAlias(SrcList*, Token*); +SrcList *sqlite3SrcListAppendFromTerm(SrcList*, Token*, Token*, Token*, + Select*, Expr*, IdList*); +void sqlite3SrcListShiftJoinType(SrcList*); void sqlite3SrcListAssignCursors(Parse*, SrcList*); void sqlite3IdListDelete(IdList*); void sqlite3SrcListDelete(SrcList*); diff --git a/src/where.c b/src/where.c index 700485ebf..1d640cb26 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.232 2006/11/06 15:10:05 drh Exp $ +** $Id: where.c,v 1.233 2006/12/16 16:25:16 drh Exp $ */ #include "sqliteInt.h" @@ -1854,8 +1854,7 @@ WhereInfo *sqlite3WhereBegin( for(j=iFrom, pTabItem=&pTabList->a[j]; j<pTabList->nSrc; j++, pTabItem++){ int doNotReorder; /* True if this table should not be reordered */ - doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0 - || (j>0 && (pTabItem[-1].jointype & (JT_LEFT|JT_CROSS))!=0); + doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0; if( once && doNotReorder ) break; m = getMask(&maskSet, pTabItem->iCursor); if( (m & notReady)==0 ){ @@ -2031,7 +2030,7 @@ WhereInfo *sqlite3WhereBegin( ** initialize a memory cell that records if this table matches any ** row of the left table of the join. */ - if( pLevel->iFrom>0 && (pTabItem[-1].jointype & JT_LEFT)!=0 ){ + if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){ if( !pParse->nMem ) pParse->nMem++; pLevel->iLeftJoin = pParse->nMem++; sqlite3VdbeAddOp(v, OP_MemInt, 0, pLevel->iLeftJoin); |