aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_clause.c
diff options
context:
space:
mode:
authorThomas G. Lockhart <lockhart@fourpalms.org>1999-02-23 07:46:42 +0000
committerThomas G. Lockhart <lockhart@fourpalms.org>1999-02-23 07:46:42 +0000
commitb4def32439e1069a17bd9756932c7c8a2469ae6c (patch)
tree6c46e7133486165abcf9675cf6a30aec54a05c33 /src/backend/parser/parse_clause.c
parent449020f782b0897d9c2c8e1903774c9f7e841e36 (diff)
downloadpostgresql-b4def32439e1069a17bd9756932c7c8a2469ae6c.tar.gz
postgresql-b4def32439e1069a17bd9756932c7c8a2469ae6c.zip
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
Diffstat (limited to 'src/backend/parser/parse_clause.c')
-rw-r--r--src/backend/parser/parse_clause.c242
1 files changed, 222 insertions, 20 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 41984a7927b..0085a4781b9 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));
}
}