aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <>2022-04-07 01:11:13 +0000
committerdrh <>2022-04-07 01:11:13 +0000
commitd44f8b2385eeb97d799fe5172c7514972c6f3359 (patch)
tree0350d9b592093c18ddbd7eb5c8363e2dae5ba206 /src
parent200adc9e75fdc08beaf70536d3983b3434e45ded (diff)
downloadsqlite-d44f8b2385eeb97d799fe5172c7514972c6f3359.tar.gz
sqlite-d44f8b2385eeb97d799fe5172c7514972c6f3359.zip
Improved technique for parsing the ON and USING clauses of a join is faster
and uses less memory. FossilOrigin-Name: 158156a3e3d50042cafc75dea3aaaa68b1f2efb9c3d178518ea6e68e32e0d21c
Diffstat (limited to 'src')
-rw-r--r--src/alter.c16
-rw-r--r--src/attach.c6
-rw-r--r--src/build.c31
-rw-r--r--src/delete.c4
-rw-r--r--src/expr.c20
-rw-r--r--src/parse.y47
-rw-r--r--src/resolve.c15
-rw-r--r--src/select.c48
-rw-r--r--src/sqliteInt.h20
-rw-r--r--src/whereexpr.c4
10 files changed, 124 insertions, 87 deletions
diff --git a/src/alter.c b/src/alter.c
index 1ec74cdcc..62075df75 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -858,11 +858,10 @@ static void unmapColumnIdlistNames(
Parse *pParse,
const IdList *pIdList
){
- if( pIdList ){
- int ii;
- for(ii=0; ii<pIdList->nId; ii++){
- sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName);
- }
+ int ii;
+ assert( pIdList!=0 );
+ for(ii=0; ii<pIdList->nId; ii++){
+ sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName);
}
}
@@ -890,8 +889,11 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){
SrcList *pSrc = p->pSrc;
for(i=0; i<pSrc->nSrc; i++){
sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
- sqlite3WalkExpr(pWalker, pSrc->a[i].pOn);
- unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing);
+ if( pSrc->a[i].fg.isUsing==0 ){
+ sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn);
+ }else{
+ unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing);
+ }
}
}
diff --git a/src/attach.c b/src/attach.c
index e587f6cc5..1732be27b 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -480,7 +480,11 @@ static int fixSelectCb(Walker *p, Select *pSelect){
pItem->fg.fromDDL = 1;
}
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
- if( sqlite3WalkExpr(&pFix->w, pList->a[i].pOn) ) return WRC_Abort;
+ if( pList->a[i].fg.isUsing==0
+ && sqlite3WalkExpr(&pFix->w, pList->a[i].u3.pOn)
+ ){
+ return WRC_Abort;
+ }
#endif
}
if( pSelect->pWith ){
diff --git a/src/build.c b/src/build.c
index 35f1ea363..da862fa70 100644
--- a/src/build.c
+++ b/src/build.c
@@ -4710,7 +4710,7 @@ void sqlite3IdListDelete(sqlite3 *db, IdList *pList){
*/
int sqlite3IdListIndex(IdList *pList, const char *zName){
int i;
- if( pList==0 ) return -1;
+ assert( pList!=0 );
for(i=0; i<pList->nId; i++){
if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i;
}
@@ -4913,8 +4913,11 @@ void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
sqlite3DeleteTable(db, pItem->pTab);
if( pItem->pSelect ) sqlite3SelectDelete(db, pItem->pSelect);
- if( pItem->pOn ) sqlite3ExprDelete(db, pItem->pOn);
- if( pItem->pUsing ) sqlite3IdListDelete(db, pItem->pUsing);
+ if( pItem->fg.isUsing ){
+ sqlite3IdListDelete(db, pItem->u3.pUsing);
+ }else if( pItem->u3.pOn ){
+ sqlite3ExprDelete(db, pItem->u3.pOn);
+ }
}
sqlite3DbFreeNN(db, pList);
}
@@ -4942,14 +4945,13 @@ SrcList *sqlite3SrcListAppendFromTerm(
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 */
+ OnOrUsing *pOnUsing /* Either the ON clause or the USING clause */
){
SrcItem *pItem;
sqlite3 *db = pParse->db;
- if( !p && (pOn || pUsing) ){
+ if( !p && pOnUsing!=0 && (pOnUsing->pOn || pOnUsing->pUsing) ){
sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s",
- (pOn ? "ON" : "USING")
+ (pOnUsing->pOn ? "ON" : "USING")
);
goto append_from_error;
}
@@ -4970,14 +4972,21 @@ SrcList *sqlite3SrcListAppendFromTerm(
pItem->zAlias = sqlite3NameFromToken(db, pAlias);
}
pItem->pSelect = pSubquery;
- pItem->pOn = pOn;
- pItem->pUsing = pUsing;
+ assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 );
+ assert( pItem->fg.isUsing==0 );
+ if( pOnUsing==0 ){
+ pItem->u3.pOn = 0;
+ }else if( pOnUsing->pUsing ){
+ pItem->fg.isUsing = 1;
+ pItem->u3.pUsing = pOnUsing->pUsing;
+ }else{
+ pItem->u3.pOn = pOnUsing->pOn;
+ }
return p;
append_from_error:
assert( p==0 );
- sqlite3ExprDelete(db, pOn);
- sqlite3IdListDelete(db, pUsing);
+ sqlite3ClearOnOrUsing(db, pOnUsing);
sqlite3SelectDelete(db, pSubquery);
return 0;
}
diff --git a/src/delete.c b/src/delete.c
index a4c5d53c5..df378d2d5 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -128,8 +128,8 @@ void sqlite3MaterializeView(
assert( pFrom->nSrc==1 );
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
- assert( pFrom->a[0].pOn==0 );
- assert( pFrom->a[0].pUsing==0 );
+ assert( pFrom->a[0].fg.isUsing==0 );
+ assert( pFrom->a[0].u3.pOn==0 );
}
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy,
SF_IncludeHidden, pLimit);
diff --git a/src/expr.c b/src/expr.c
index 79889bdd7..2c00bb498 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1249,6 +1249,18 @@ void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p ) sqlite3ExprDeleteNN(db, p);
}
+/*
+** Clear both elements of an OnOrUsing object
+*/
+void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
+ if( p==0 ){
+ /* Nothing to clear */
+ }else if( p->pOn ){
+ sqlite3ExprDeleteNN(db, p->pOn);
+ }else if( p->pUsing ){
+ sqlite3IdListDelete(db, p->pUsing);
+ }
+}
/*
** Arrange to cause pExpr to be deleted when the pParse is deleted.
@@ -1671,8 +1683,12 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
pTab->nTabRef++;
}
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags);
- pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags);
- pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing);
+ if( pOldItem->fg.isUsing ){
+ assert( pNewItem->fg.isUsing );
+ pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing);
+ }else{
+ pNewItem->u3.pOn = sqlite3ExprDup(db, pOldItem->u3.pOn, flags);
+ }
pNewItem->colUsed = pOldItem->colUsed;
}
return pNew;
diff --git a/src/parse.y b/src/parse.y
index e413739ff..7b3af1f1c 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -570,7 +570,7 @@ selectnowith(A) ::= selectnowith(A) multiselect_op(Y) oneselect(Z). {
Token x;
x.n = 0;
parserDoubleLinkSelect(pParse, pRhs);
- pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0);
+ pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0);
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
@@ -695,30 +695,26 @@ stl_prefix(A) ::= seltablist(A) joinop(Y). {
if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].fg.jointype = (u8)Y;
}
stl_prefix(A) ::= . {A = 0;}
-seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
- A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,N,U);
+seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) on_using(N). {
+ A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,&N);
}
-seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) indexed_by(I)
- on_opt(N) using_opt(U). {
- A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,N,U);
+seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) indexed_by(I) on_using(N). {
+ A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,&N);
sqlite3SrcListIndexedBy(pParse, A, &I);
}
-seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z)
- on_opt(N) using_opt(U). {
- A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,N,U);
+seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N). {
+ A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,&N);
sqlite3SrcListFuncArgs(pParse, A, E);
}
%ifndef SQLITE_OMIT_SUBQUERY
- seltablist(A) ::= stl_prefix(A) LP select(S) RP
- as(Z) on_opt(N) using_opt(U). {
- A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,N,U);
+ seltablist(A) ::= stl_prefix(A) LP select(S) RP as(Z) on_using(N). {
+ A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,&N);
}
- seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP
- as(Z) on_opt(N) using_opt(U). {
- if( A==0 && Z.n==0 && N==0 && U==0 ){
+ seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP as(Z) on_using(N). {
+ if( A==0 && Z.n==0 && N.pOn==0 && N.pUsing==0 ){
A = F;
}else if( F->nSrc==1 ){
- A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,N,U);
+ A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,&N);
if( A ){
SrcItem *pNew = &A->a[A->nSrc-1];
SrcItem *pOld = F->a;
@@ -739,7 +735,7 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z)
Select *pSubquery;
sqlite3SrcListShiftJoinType(F);
pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,SF_NestedFrom,0);
- A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,pSubquery,N,U);
+ A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,pSubquery,&N);
}
}
%endif SQLITE_OMIT_SUBQUERY
@@ -797,13 +793,14 @@ joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN.
//
// INSERT INTO tab SELECT * FROM aaa JOIN bbb WHERE true ON CONFLICT ...
//
-// The [AND] and [OR] precedence marks in the rules for on_opt cause the
+// The [AND] and [OR] precedence marks in the rules for on_using cause the
// ON in this context to always be interpreted as belonging to the JOIN.
//
-%type on_opt {Expr*}
-%destructor on_opt {sqlite3ExprDelete(pParse->db, $$);}
-on_opt(N) ::= ON expr(E). {N = E;}
-on_opt(N) ::= . [OR] {N = 0;}
+%type on_using {OnOrUsing}
+//%destructor on_using {sqlite3ClearOnOrUsing(pParse->db, &$$);}
+on_using(N) ::= ON expr(E). {N.pOn = E; N.pUsing = 0;}
+on_using(N) ::= USING LP idlist(L) RP. {N.pOn = 0; N.pUsing = L;}
+on_using(N) ::= . [OR] {N.pOn = 0; N.pUsing = 0;}
// Note that this block abuses the Token type just a little. If there is
// no "INDEXED BY" clause, the returned token is empty (z==0 && n==0). If
@@ -822,12 +819,6 @@ indexed_opt(A) ::= indexed_by(A).
indexed_by(A) ::= INDEXED BY nm(X). {A = X;}
indexed_by(A) ::= NOT INDEXED. {A.z=0; A.n=1;}
-%type using_opt {IdList*}
-%destructor using_opt {sqlite3IdListDelete(pParse->db, $$);}
-using_opt(U) ::= USING LP idlist(L) RP. {U = L;}
-using_opt(U) ::= . {U = 0;}
-
-
%type orderby_opt {ExprList*}
%destructor orderby_opt {sqlite3ExprListDelete(pParse->db, $$);}
diff --git a/src/resolve.c b/src/resolve.c
index 480694f6f..30785ca70 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -123,11 +123,10 @@ static void resolveAlias(
** zCol.
*/
static int nameInUsingClause(IdList *pUsing, const char *zCol){
- if( pUsing ){
- int k;
- for(k=0; k<pUsing->nId; k++){
- if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1;
- }
+ int k;
+ assert( pUsing!=0 );
+ for(k=0; k<pUsing->nId; k++){
+ if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1;
}
return 0;
}
@@ -346,7 +345,11 @@ static int lookupName(
*/
if( cnt==1 ){
if( pItem->fg.jointype & JT_NATURAL ) continue;
- if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
+ if( pItem->fg.isUsing
+ && nameInUsingClause(pItem->u3.pUsing, zCol)
+ ){
+ continue;
+ }
}
cnt++;
pMatch = pItem;
diff --git a/src/select.c b/src/select.c
index a0d94c0c8..989446af6 100644
--- a/src/select.c
+++ b/src/select.c
@@ -468,7 +468,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
** every column that the two tables have in common.
*/
if( pRight->fg.jointype & JT_NATURAL ){
- if( pRight->pOn || pRight->pUsing ){
+ if( pRight->fg.isUsing || pRight->u3.pOn ){
sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
"an ON or USING clause", 0);
return 1;
@@ -487,23 +487,6 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
}
}
- /* Disallow both ON and USING clauses in the same join
- */
- if( pRight->pOn && pRight->pUsing ){
- sqlite3ErrorMsg(pParse, "cannot have both ON and USING "
- "clauses in the same join");
- return 1;
- }
-
- /* Add the ON clause to the end of the WHERE clause, connected by
- ** an AND operator.
- */
- if( pRight->pOn ){
- if( isOuter ) sqlite3SetJoinExpr(pRight->pOn, pRight->iCursor);
- p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->pOn);
- pRight->pOn = 0;
- }
-
/* Create extra terms on the WHERE clause for each column named
** in the USING clause. Example: If the two tables to be joined are
** A and B and the USING clause names X, Y, and Z, then add this
@@ -511,8 +494,9 @@ 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( pRight->pUsing ){
- IdList *pList = pRight->pUsing;
+ if( pRight->fg.isUsing ){
+ IdList *pList = pRight->u3.pUsing;
+ assert( pList!=0 );
for(j=0; j<pList->nId; j++){
char *zName; /* Name of the term in the USING clause */
int iLeft; /* Table on the left with matching column name */
@@ -532,6 +516,15 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
isOuter, &p->pWhere);
}
}
+
+ /* Add the ON clause to the end of the WHERE clause, connected by
+ ** an AND operator.
+ */
+ else if( pRight->u3.pOn ){
+ if( isOuter ) sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor);
+ p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
+ pRight->u3.pOn = 0;
+ }
}
return 0;
}
@@ -4222,7 +4215,7 @@ static int flattenSubquery(
pSubitem->zName = 0;
pSubitem->zAlias = 0;
pSubitem->pSelect = 0;
- assert( pSubitem->pOn==0 );
+ assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 );
/* If the sub-query is a compound SELECT statement, then (by restrictions
** 17 and 18 above) it must be a UNION ALL and the parent query must
@@ -4366,9 +4359,10 @@ static int flattenSubquery(
** outer query.
*/
for(i=0; i<nSubSrc; i++){
- sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing);
- assert( pSrc->a[i+iFrom].fg.isTabFunc==0 );
- pSrc->a[i+iFrom] = pSubSrc->a[i];
+ SrcItem *pItem = &pSrc->a[i+iFrom];
+ if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
+ assert( pItem->fg.isTabFunc==0 );
+ *pItem = pSubSrc->a[i];
iNewParent = pSubSrc->a[i].iCursor;
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
@@ -5092,7 +5086,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
pNew = sqlite3DbMallocZero(db, sizeof(*pNew) );
if( pNew==0 ) return WRC_Abort;
memset(&dummy, 0, sizeof(dummy));
- pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0);
+ pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0);
if( pNewSrc==0 ) return WRC_Abort;
*pNew = *p;
p->pSrc = pNewSrc;
@@ -5703,7 +5697,9 @@ static int selectExpander(Walker *pWalker, Select *p){
** table to the right of the join */
continue;
}
- if( sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){
+ if( pFrom->fg.isUsing
+ && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0
+ ){
/* In a join with a USING clause, omit columns in the
** using clause from the table on the right. */
continue;
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index a77df1d8b..9f95ec8a9 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1191,6 +1191,7 @@ typedef struct Lookaside Lookaside;
typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
+typedef struct OnOrUsing OnOrUsing;
typedef struct Parse Parse;
typedef struct ParseCleanup ParseCleanup;
typedef struct PreUpdate PreUpdate;
@@ -3075,10 +3076,13 @@ struct SrcItem {
unsigned fromDDL :1; /* Comes from sqlite_schema */
unsigned isCte :1; /* This is a CTE */
unsigned notCte :1; /* This item may not match a CTE */
+ unsigned isUsing :1; /* u3.pUsing is valid */
} fg;
int iCursor; /* The VDBE cursor number used to access this table */
- Expr *pOn; /* The ON clause of a join */
- IdList *pUsing; /* The USING clause of a join */
+ union {
+ Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */
+ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */
+ } u3;
Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
union {
char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
@@ -3091,6 +3095,15 @@ struct SrcItem {
};
/*
+** The OnOrUsing object represents either an ON clause or a USING clause.
+** It can never be both at the same time, but it can be neither.
+*/
+struct OnOrUsing {
+ Expr *pOn; /* The ON clause of a join */
+ IdList *pUsing; /* The USING clause of a join */
+};
+
+/*
** The following structure describes the FROM clause of a SELECT statement.
** Each table or subquery in the FROM clause is a separate element of
** the SrcList.a[] array.
@@ -4610,13 +4623,14 @@ SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int);
SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2);
SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*);
SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
- Token*, Select*, Expr*, IdList*);
+ Token*, Select*, OnOrUsing*);
void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
int sqlite3IndexedByLookup(Parse *, SrcItem *);
void sqlite3SrcListShiftJoinType(SrcList*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
void sqlite3IdListDelete(sqlite3*, IdList*);
+void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*);
void sqlite3SrcListDelete(sqlite3*, SrcList*);
Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
diff --git a/src/whereexpr.c b/src/whereexpr.c
index 19dd886de..26a521fff 100644
--- a/src/whereexpr.c
+++ b/src/whereexpr.c
@@ -951,7 +951,9 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
int i;
for(i=0; i<pSrc->nSrc; i++){
mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect);
- mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn);
+ if( pSrc->a[i].fg.isUsing==0 ){
+ mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn);
+ }
if( pSrc->a[i].fg.isTabFunc ){
mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg);
}