Include some new code for outer joins. Disabled by default, but enable by

including the following in your Makefile.custom:
 CFLAGS+= -DENABLE_OUTER_JOINS -DEXEC_MERGEJOINDEBUG
This commit is contained in:
Thomas G. Lockhart 1999-02-23 07:46:42 +00:00
parent 449020f782
commit b4def32439
1 changed files with 222 additions and 20 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.28 1999/02/13 23:17:07 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.29 1999/02/23 07:46:42 thomas Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,7 +26,9 @@
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_coerce.h"
#include "nodes/print.h"
#include "parse.h"
#define ORDER_CLAUSE 0
@ -37,7 +39,15 @@ static char *clauseText[] = {"ORDER", "GROUP"};
static TargetEntry *
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
static void parseFromClause(ParseState *pstate, List *frmList);
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
Attr *makeAttr(char *relname, char *attname);
#ifdef ENABLE_OUTER_JOINS
Node *transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname);
#endif
char *transformTableEntry(ParseState *pstate, RangeVar *r);
/*
@ -46,18 +56,18 @@ static void parseFromClause(ParseState *pstate, List *frmList);
* from_clause.
*/
void
makeRangeTable(ParseState *pstate, char *relname, List *frmList)
makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual)
{
RangeTblEntry *rte;
int sublevels_up;
parseFromClause(pstate, frmList);
parseFromClause(pstate, frmList, qual);
if (relname == NULL)
return;
if (refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0 ||
sublevels_up != 0)
if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
|| (sublevels_up != 0))
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE);
else
rte = refnameRangeTableEntry(pstate, relname);
@ -77,17 +87,35 @@ makeRangeTable(ParseState *pstate, char *relname, List *frmList)
* transformWhereClause -
* transforms the qualification and make sure it is of type Boolean
*
* Now accept an additional argument, which is a qualification derived
* from the JOIN/ON or JOIN/USING syntax.
* - thomas 1998-12-16
*/
Node *
transformWhereClause(ParseState *pstate, Node *a_expr)
transformWhereClause(ParseState *pstate, Node *a_expr, Node *o_expr)
{
A_Expr *expr;
Node *qual;
if (a_expr == NULL)
if ((a_expr == NULL) && (o_expr == NULL))
return NULL; /* no qualifiers */
if ((a_expr != NULL) && (o_expr != NULL))
{
A_Expr *a = makeNode(A_Expr);
a->oper = AND;
a->opname = NULL;
a->lexpr = o_expr;
a->rexpr = a_expr;
expr = a;
}
else if (o_expr != NULL)
expr = (A_Expr *)o_expr;
else
expr = (A_Expr *)a_expr;
pstate->p_in_where_clause = true;
qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST);
qual = transformExpr(pstate, (Node *)expr, EXPR_COLUMN_FIRST);
pstate->p_in_where_clause = false;
if (exprType(qual) != BOOLOID)
@ -98,6 +126,122 @@ transformWhereClause(ParseState *pstate, Node *a_expr)
return qual;
}
Attr *
makeAttr(char *relname, char *attname)
{
Attr *a = makeNode(Attr);
a->relname = relname;
a->paramNo = NULL;
a->attrs = lcons(makeString(attname), NIL);
a->indirection = NULL;
return a;
}
#ifdef ENABLE_OUTER_JOINS
/* transformUsingClause()
* Take an ON or USING clause from a join expression and expand if necessary.
*/
Node *
transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
{
A_Expr *expr = NULL;
List *on;
Node *qual;
foreach (on, onList)
{
qual = lfirst(on);
/* Ident node means it is just a column name from a real USING clause... */
if (IsA(qual, Ident))
{
Ident *i = (Ident *)qual;
Attr *lattr = makeAttr(lname, i->name);
Attr *rattr = makeAttr(rname, i->name);
A_Expr *e = makeNode(A_Expr);
#ifdef PARSEDEBUG
printf("transformUsingClause- transform %s", nodeToString(i));
#endif
e->oper = OP;
e->opname = "=";
e->lexpr = (Node *)lattr;
e->rexpr = (Node *)rattr;
if (expr != NULL)
{
A_Expr *a = makeNode(A_Expr);
a->oper = AND;
a->opname = NULL;
a->lexpr = (Node *)expr;
a->rexpr = (Node *)e;
expr = a;
}
else
expr = e;
}
/* otherwise, we have an expression from an ON clause... */
else
{
if (expr != NULL)
{
A_Expr *a = makeNode(A_Expr);
a->oper = AND;
a->opname = NULL;
a->lexpr = (Node *)expr;
a->rexpr = (Node *)qual;
expr = a;
}
else
{
expr = (A_Expr *)qual;
}
#ifdef PARSEDEBUG
printf("transformUsingClause- transform %s", nodeToString(qual));
#endif
}
#ifdef PARSEDEBUG
printf(" to %s\n", nodeToString(expr));
#endif
}
return ((Node *)transformExpr(pstate, (Node *)expr, EXPR_COLUMN_FIRST));
}
#endif
char *
transformTableEntry(ParseState *pstate, RangeVar *r)
{
RelExpr *baserel = r->relExpr;
char *relname = baserel->relname;
char *refname = r->name;
RangeTblEntry *rte;
if (refname == NULL)
refname = relname;
/*
* marks this entry to indicate it comes from the FROM clause. In
* SQL, the target list can only refer to range variables
* specified in the from clause but we follow the more powerful
* POSTQUEL semantics and automatically generate the range
* variable if not specified. However there are times we need to
* know whether the entries are legitimate.
*
* eg. select * from foo f where f.x = 1; will generate wrong answer
* if we expand * to foo.x.
*/
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
return refname;
}
/*
* parseFromClause -
* turns the table references specified in the from-clause into a
@ -106,23 +250,23 @@ transformWhereClause(ParseState *pstate, Node *a_expr)
* allow references to relations not specified in the from-clause. We
* also allow now as an extension.)
*
* The FROM clause can now contain JoinExpr nodes, which contain parsing info
* for inner and outer joins. The USING clause must be expanded into a qualification
* for an inner join at least, since that is compatible with the old syntax.
* Not sure yet how to handle outer joins, but it will become clear eventually?
* - thomas 1998-12-16
*/
static void
parseFromClause(ParseState *pstate, List *frmList)
parseFromClause(ParseState *pstate, List *frmList, Node **qual)
{
List *fl;
if (qual != NULL)
*qual = NULL;
foreach(fl, frmList)
{
RangeVar *r = lfirst(fl);
RelExpr *baserel = r->relExpr;
char *relname = baserel->relname;
char *refname = r->name;
RangeTblEntry *rte;
if (refname == NULL)
refname = relname;
Node *n = lfirst(fl);
/*
* marks this entry to indicate it comes from the FROM clause. In
* SQL, the target list can only refer to range variables
@ -134,7 +278,65 @@ parseFromClause(ParseState *pstate, List *frmList)
* eg. select * from foo f where f.x = 1; will generate wrong answer
* if we expand * to foo.x.
*/
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
if (IsA(n, RangeVar))
{
transformTableEntry(pstate, (RangeVar *)n);
}
else if (IsA(n, JoinExpr))
{
JoinExpr *j = (JoinExpr *)n;
char *lname = transformTableEntry(pstate, (RangeVar *)j->larg);
char *rname;
if (IsA((Node *)j->rarg, RangeVar))
rname = transformTableEntry(pstate, (RangeVar *)j->rarg);
else
elog(ERROR, "Nested JOINs are not yet supported");
#ifdef ENABLE_OUTER_JOINS
if (j->jointype == INNER_P)
{
/* This is an inner join, so rip apart the join node
* and transform into a traditional FROM list.
* NATURAL JOIN and USING clauses both change the shape
* of the result. Need to generate a list of result columns
* to use for target list expansion and validation.
* Not doing this yet though!
*/
if (IsA(j->quals, List))
j->quals = lcons(transformUsingClause(pstate, (List *)j->quals, lname, rname), NIL);
Assert(qual != NULL);
if (*qual == NULL)
*qual = lfirst(j->quals);
else
elog(ERROR, "Multiple JOIN/ON clauses not handled (internal error)");
/* if we are transforming this node back into a FROM list,
* then we will need to replace the node with two nodes.
* Will need access to the previous list item to change
* the link pointer to reference these new nodes.
* Try accumulating and returning a new list.
* - thomas 1999-01-08
* Not doing this yet though!
*/
}
else if ((j->jointype == LEFT)
|| (j->jointype == RIGHT)
|| (j->jointype == FULL))
elog(ERROR, "OUTER JOIN is not implemented");
else
elog(ERROR, "Unrecognized JOIN clause; tag is %d (internal error)",
j->jointype);
#else
elog(ERROR, "JOIN expressions are not yet implemented");
#endif
}
else
elog(ERROR, "parseFromClause: unexpected FROM clause node (internal error)"
"\n\t%s", nodeToString(n));
}
}