From b4def32439e1069a17bd9756932c7c8a2469ae6c Mon Sep 17 00:00:00 2001 From: "Thomas G. Lockhart" Date: Tue, 23 Feb 1999 07:46:42 +0000 Subject: [PATCH] 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 --- src/backend/parser/parse_clause.c | 242 ++++++++++++++++++++++++++++++++++---- 1 file changed, 222 insertions(+), 20 deletions(-) diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 41984a7927..0085a4781b 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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)); } } -- 2.11.0