1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-14 00:22:38 +03:00

Initial implementation of LEFT OUTER JOIN including the expanded SQL92 join

syntax. The basic functionality is there but there is still a lot of testing
to do. (CVS 587)

FossilOrigin-Name: 99bd1f5b9a1a20bfeefe15c00d96a34a5f40923e
This commit is contained in:
drh
2002-05-24 20:31:36 +00:00
parent 01f3f25376
commit ad2d8307ac
10 changed files with 313 additions and 39 deletions

View File

@@ -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.83 2002/05/24 16:14:15 drh Exp $
** $Id: select.c,v 1.84 2002/05/24 20:31:37 drh Exp $
*/
#include "sqliteInt.h"
@@ -106,7 +106,11 @@ int sqliteJoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
break;
}
}
if( (jointype & ~JT_INNER)!=0 ){
if(
(jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) ||
(jointype & JT_ERROR)!=0 ||
(jointype & JT_RIGHT)==JT_RIGHT
){
static Token dummy = { 0, 0 };
char *zSp1 = " ", *zSp2 = " ";
if( pB==0 ){ pB = &dummy; zSp1 = 0; }
@@ -119,6 +123,137 @@ int sqliteJoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
return jointype;
}
/*
** Return the index of a column in a table. Return -1 if the column
** is not contained in the table.
*/
static int columnIndex(Table *pTab, const char *zCol){
int i;
for(i=0; i<pTab->nCol; i++){
if( sqliteStrICmp(pTab->aCol[i].zName, zCol)==0 ) return i;
}
return -1;
}
/*
** Add a term to the WHERE expression in *ppExpr that requires the
** zCol column to be equal in the two tables pTab1 and pTab2.
*/
static void addWhereTerm(
const char *zCol, /* Name of the column */
const Table *pTab1, /* First table */
const Table *pTab2, /* Second table */
Expr **ppExpr /* Add the equality term to this expression */
){
Token dummy;
Expr *pE1a, *pE1b, *pE1c;
Expr *pE2a, *pE2b, *pE2c;
Expr *pE;
dummy.z = zCol;
dummy.n = strlen(zCol);
pE1a = sqliteExpr(TK_ID, 0, 0, &dummy);
pE2a = sqliteExpr(TK_ID, 0, 0, &dummy);
dummy.z = pTab1->zName;
dummy.n = strlen(dummy.z);
pE1b = sqliteExpr(TK_ID, 0, 0, &dummy);
dummy.z = pTab2->zName;
dummy.n = strlen(dummy.z);
pE2b = sqliteExpr(TK_ID, 0, 0, &dummy);
pE1c = sqliteExpr(TK_DOT, pE1b, pE1a, 0);
pE2c = sqliteExpr(TK_DOT, pE2b, pE2a, 0);
pE = sqliteExpr(TK_EQ, pE1c, pE2c, 0);
if( *ppExpr ){
*ppExpr = sqliteExpr(TK_AND, *ppExpr, pE, 0);
}else{
*ppExpr = pE;
}
}
/*
** This routine processes the join information for a SELECT statement.
** ON and USING clauses are converted into extra terms of the WHERE clause.
** NATURAL joins also create extra WHERE clause terms.
**
** This routine returns the number of errors encountered.
*/
static int sqliteProcessJoin(Parse *pParse, Select *p){
SrcList *pSrc;
int i, j;
pSrc = p->pSrc;
for(i=0; i<pSrc->nSrc-1; i++){
struct SrcList_item *pTerm = &pSrc->a[i];
struct SrcList_item *pOther = &pSrc->a[i+1];
if( pTerm->pTab==0 || pOther->pTab==0 ) continue;
/* When the NATURAL keyword is present, add WHERE clause terms for
** every column that the two tables have in common.
*/
if( pTerm->jointype & JT_NATURAL ){
Table *pTab;
if( pTerm->pOn || pTerm->pUsing ){
sqliteSetString(&pParse->zErrMsg, "a NATURAL join may not have "
"an ON or USING clause", 0);
pParse->nErr++;
return 1;
}
pTab = pTerm->pTab;
for(j=0; j<pTab->nCol; j++){
if( columnIndex(pOther->pTab, pTab->aCol[j].zName)>=0 ){
addWhereTerm(pTab->aCol[j].zName, pTab, pOther->pTab, &p->pWhere);
}
}
}
/* Disallow both ON and USING clauses in the same join
*/
if( pTerm->pOn && pTerm->pUsing ){
sqliteSetString(&pParse->zErrMsg, "cannot have both ON and USING "
"clauses in the same join", 0);
pParse->nErr++;
return 1;
}
/* Add the ON clause to the end of the WHERE clause, connected by
** and AND operator.
*/
if( pTerm->pOn ){
if( p->pWhere==0 ){
p->pWhere = pTerm->pOn;
}else{
p->pWhere = sqliteExpr(TK_AND, p->pWhere, pTerm->pOn, 0);
}
pTerm->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
** to the WHERE clause: A.X=B.X AND A.Y=B.Y AND A.Z=B.Z
** Report an error if any column mentioned in the USING clause is
** not contained in both tables to be joined.
*/
if( pTerm->pUsing ){
IdList *pList;
int j;
assert( i<pSrc->nSrc-1 );
pList = pTerm->pUsing;
for(j=0; j<pList->nId; j++){
if( columnIndex(pTerm->pTab, pList->a[i].zName)<0 ||
columnIndex(pOther->pTab, pList->a[i].zName)<0 ){
sqliteSetString(&pParse->zErrMsg, "cannot join using column ",
pList->a[i].zName, " - column not present in both tables", 0);
pParse->nErr++;
return 1;
}
addWhereTerm(pList->a[i].zName, pTerm->pTab, pOther->pTab, &p->pWhere);
}
}
}
return 0;
}
/*
** Delete the given Select structure and all of its substructures.
*/
@@ -414,12 +549,15 @@ Table *sqliteResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
}
/*
** For the given SELECT statement, do two things.
** For the given SELECT statement, do three things.
**
** (1) Fill in the pTabList->a[].pTab fields in the SrcList that
** defines the set of tables that should be scanned.
**
** (2) Scan the list of columns in the result set (pEList) looking
** (2) Add terms to the WHERE clause to accomodate the NATURAL keyword
** on joins and the ON and USING clause of joins.
**
** (3) Scan the list of columns in the result set (pEList) looking
** for instances of the "*" operator or the TABLE.* operator.
** If found, expand each "*" to be every column in every table
** and TABLE.* to be every column in TABLE.
@@ -447,6 +585,12 @@ static int fillInColumnList(Parse *pParse, Select *p){
if( pTabList->a[i].zName==0 ){
/* A sub-query in the FROM clause of a SELECT */
assert( pTabList->a[i].pSelect!=0 );
if( pTabList->a[i].zAlias==0 ){
char zFakeName[60];
sprintf(zFakeName, "sqlite_subquery_%p_",
(void*)pTabList->a[i].pSelect);
sqliteSetString(&pTabList->a[i].zAlias, zFakeName, 0);
}
pTabList->a[i].pTab = pTab =
sqliteResultSetOfSelect(pParse, pTabList->a[i].zAlias,
pTabList->a[i].pSelect);
@@ -473,6 +617,10 @@ static int fillInColumnList(Parse *pParse, Select *p){
}
}
/* Process NATURAL keywords, and ON and USING clauses of joins.
*/
if( sqliteProcessJoin(pParse, p) ) return 1;
/* For every "*" that occurs in the column list, insert the names of
** all columns in all tables. And for every TABLE.* insert the names
** of all columns in TABLE. The parser inserted a special expression
@@ -531,10 +679,23 @@ static int fillInColumnList(Parse *pParse, Select *p){
tableSeen = 1;
for(j=0; j<pTab->nCol; j++){
Expr *pExpr, *pLeft, *pRight;
char *zName = pTab->aCol[j].zName;
if( i>0 && (pTabList->a[i-1].jointype & JT_NATURAL)!=0 &&
columnIndex(pTabList->a[i-1].pTab, zName)>=0 ){
/* In a NATURAL join, omit the join columns from the
** table on the right */
continue;
}
if( i>0 && sqliteIdListIndex(pTabList->a[i-1].pUsing, zName)>=0 ){
/* In a join with a USING clause, omit columns in the
** using clause from the table on the right. */
continue;
}
pRight = sqliteExpr(TK_ID, 0, 0, 0);
if( pRight==0 ) break;
pRight->token.z = pTab->aCol[j].zName;
pRight->token.n = strlen(pTab->aCol[j].zName);
pRight->token.z = zName;
pRight->token.n = strlen(zName);
if( zTabName ){
pLeft = sqliteExpr(TK_ID, 0, 0, 0);
if( pLeft==0 ) break;
@@ -1295,6 +1456,7 @@ int sqliteSelect(
if( fillInColumnList(pParse, p) ){
goto select_end;
}
pWhere = p->pWhere;
pEList = p->pEList;
if( pEList==0 ) goto select_end;