Skip to content
Snippets Groups Projects
Commit b4def324 authored by Thomas G. Lockhart's avatar Thomas G. Lockhart
Browse files

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
parent 449020f7
No related branches found
No related tags found
No related merge requests found
......@@ -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));
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment