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
diff --git a/src/build.c b/src/build.c
index eeceded..00b89f2 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 @@
}
/*
-** 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 @@
}
/*
+** 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 2ea27f5..1d5e186 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 @@
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 2ba60a7..eac525e 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 @@
// 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) ::= . {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 @@
%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 68e348f..343a305 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 @@
/* 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 @@
/* 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 @@
/* 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 @@
** 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 @@
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 @@
**
** 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 @@
** 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 @@
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 a3d4317..b000f0a 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 @@
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 700485e..1d640cb 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 @@
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 @@
** 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);