Include some new code for outer joins. Disabled by default, but enable by
authorThomas G. Lockhart
Tue, 23 Feb 1999 07:46:42 +0000 (07:46 +0000)
committerThomas G. Lockhart
Tue, 23 Feb 1999 07:46:42 +0000 (07:46 +0000)
 including the following in your Makefile.custom:
 CFLAGS+= -DENABLE_OUTER_JOINS -DEXEC_MERGEJOINDEBUG

src/backend/parser/parse_clause.c

index 41984a7927b100dcaf0bdf11732ed6f576ad3497..0085a4781b932821932f8fea44b81b761926d097 100644 (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));
    }
 }