]> granicus.if.org Git - postgresql/commitdiff
Restructure representation of join alias variables. An explicit JOIN
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Mar 2002 00:52:10 +0000 (00:52 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Mar 2002 00:52:10 +0000 (00:52 +0000)
now has an RTE of its own, and references to its outputs now are Vars
referencing the JOIN RTE, rather than CASE-expressions.  This allows
reverse-listing in ruleutils.c to use the correct alias easily, rather
than painfully reverse-engineering the alias namespace as it used to do.
Also, nested FULL JOINs work correctly, because the result of the inner
joins are simple Vars that the planner can cope with.  This fixes a bug
reported a couple times now, notably by Tatsuo on 18-Nov-01.  The alias
Vars are expanded into COALESCE expressions where needed at the very end
of planning, rather than during parsing.
Also, beginnings of support for showing plan qualifier expressions in
EXPLAIN.  There are probably still cases that need work.
initdb forced due to change of stored-rule representation.

41 files changed:
src/backend/commands/explain.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/path/joinrels.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/relnode.c
src/backend/optimizer/util/var.c
src/backend/parser/analyze.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/catversion.h
src/include/nodes/parsenodes.h
src/include/nodes/plannodes.h
src/include/nodes/primnodes.h
src/include/nodes/relation.h
src/include/optimizer/pathnode.h
src/include/optimizer/planmain.h
src/include/optimizer/var.h
src/include/parser/parse_relation.h
src/include/parser/parsetree.h
src/include/utils/builtins.h
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index 319a9c83d82ee5ad7de8ec4f075179f4d5a41f5c..e230ba598a00fae5a6a56d5269f2a33e9a6361c1 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.70 2002/03/06 06:09:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.71 2002/03/12 00:51:35 tgl Exp $
  *
  */
 
 #include "executor/instrument.h"
 #include "lib/stringinfo.h"
 #include "nodes/print.h"
+#include "optimizer/clauses.h"
 #include "optimizer/planner.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteHandler.h"
 #include "tcop/pquery.h"
+#include "utils/builtins.h"
 #include "utils/relcache.h"
 
+
 typedef struct ExplainState
 {
        /* options */
@@ -32,6 +35,14 @@ typedef struct ExplainState
 
 static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
 static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest);
+static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
+                                                  int scanrelid,
+                                                  StringInfo str, int indent, ExplainState *es);
+static void show_upper_qual(List *qual, const char *qlabel,
+                                                       const char *outer_name, int outer_varno, Plan *outer_plan,
+                                                       const char *inner_name, int inner_varno, Plan *inner_plan,
+                                                       StringInfo str, int indent, ExplainState *es);
+static Node *make_ors_ands_explicit(List *orclauses);
 
 /* Convert a null string pointer into "<>" */
 #define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
@@ -40,7 +51,6 @@ static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDes
 /*
  * ExplainQuery -
  *       print out the execution plan for a given query
- *
  */
 void
 ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
@@ -81,7 +91,6 @@ ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
 /*
  * ExplainOneQuery -
  *       print out the execution plan for one query
- *
  */
 static void
 ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
@@ -176,9 +185,6 @@ ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
        pfree(es);
 }
 
-/*****************************************************************************
- *
- *****************************************************************************/
 
 /*
  * explain_outNode -
@@ -341,6 +347,90 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
        }
        appendStringInfo(str, "\n");
 
+       /* quals */
+       switch (nodeTag(plan))
+       {
+               case T_IndexScan:
+                       show_scan_qual(((IndexScan *) plan)->indxqualorig, true,
+                                                  "indxqual",
+                                                  ((Scan *) plan)->scanrelid,
+                                                  str, indent, es);
+                       show_scan_qual(plan->qual, false, "qual",
+                                                  ((Scan *) plan)->scanrelid,
+                                                  str, indent, es);
+                       break;
+               case T_SeqScan:
+               case T_TidScan:
+                       show_scan_qual(plan->qual, false, "qual",
+                                                  ((Scan *) plan)->scanrelid,
+                                                  str, indent, es);
+                       break;
+               case T_NestLoop:
+                       show_upper_qual(((NestLoop *) plan)->join.joinqual, "joinqual",
+                                                       "outer", OUTER, outerPlan(plan),
+                                                       "inner", INNER, innerPlan(plan),
+                                                       str, indent, es);
+                       show_upper_qual(plan->qual, "qual",
+                                                       "outer", OUTER, outerPlan(plan),
+                                                       "inner", INNER, innerPlan(plan),
+                                                       str, indent, es);
+                       break;
+               case T_MergeJoin:
+                       show_upper_qual(((MergeJoin *) plan)->mergeclauses, "merge",
+                                                       "outer", OUTER, outerPlan(plan),
+                                                       "inner", INNER, innerPlan(plan),
+                                                       str, indent, es);
+                       show_upper_qual(((MergeJoin *) plan)->join.joinqual, "joinqual",
+                                                       "outer", OUTER, outerPlan(plan),
+                                                       "inner", INNER, innerPlan(plan),
+                                                       str, indent, es);
+                       show_upper_qual(plan->qual, "qual",
+                                                       "outer", OUTER, outerPlan(plan),
+                                                       "inner", INNER, innerPlan(plan),
+                                                       str, indent, es);
+                       break;
+               case T_HashJoin:
+                       show_upper_qual(((HashJoin *) plan)->hashclauses, "hash",
+                                                       "outer", OUTER, outerPlan(plan),
+                                                       "inner", INNER, innerPlan(plan),
+                                                       str, indent, es);
+                       show_upper_qual(((HashJoin *) plan)->join.joinqual, "joinqual",
+                                                       "outer", OUTER, outerPlan(plan),
+                                                       "inner", INNER, innerPlan(plan),
+                                                       str, indent, es);
+                       show_upper_qual(plan->qual, "qual",
+                                                       "outer", OUTER, outerPlan(plan),
+                                                       "inner", INNER, innerPlan(plan),
+                                                       str, indent, es);
+                       break;
+               case T_SubqueryScan:
+                       show_upper_qual(plan->qual, "qual",
+                                                       "subplan", 1, ((SubqueryScan *) plan)->subplan,
+                                                       "", 0, NULL,
+                                                       str, indent, es);
+                       break;
+               case T_Agg:
+               case T_Group:
+                       show_upper_qual(plan->qual, "qual",
+                                                       "subplan", 0, outerPlan(plan),
+                                                       "", 0, NULL,
+                                                       str, indent, es);
+                       break;
+               case T_Result:
+                       show_upper_qual((List *) ((Result *) plan)->resconstantqual,
+                                                       "constqual",
+                                                       "subplan", OUTER, outerPlan(plan),
+                                                       "", 0, NULL,
+                                                       str, indent, es);
+                       show_upper_qual(plan->qual, "qual",
+                                                       "subplan", OUTER, outerPlan(plan),
+                                                       "", 0, NULL,
+                                                       str, indent, es);
+                       break;
+               default:
+                       break;
+       }
+
        /* initPlan-s */
        if (plan->initPlan)
        {
@@ -448,3 +538,121 @@ Explain_PlanToString(Plan *plan, ExplainState *es)
                explain_outNode(str, plan, 0, es);
        return str;
 }
+
+/*
+ * Show a qualifier expression for a scan plan node
+ */
+static void
+show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
+                          int scanrelid,
+                          StringInfo str, int indent, ExplainState *es)
+{
+       RangeTblEntry *rte;
+       List       *context;
+       Node       *node;
+       char       *exprstr;
+       int                     i;
+
+       /* No work if empty qual */
+       if (qual == NIL)
+               return;
+       if (is_or_qual)
+       {
+               if (lfirst(qual) == NIL && lnext(qual) == NIL)
+                       return;
+       }
+
+       /* Generate deparse context */
+       Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
+       rte = rt_fetch(scanrelid, es->rtable);
+
+       /* Assume it's on a real relation */
+       Assert(rte->relname);
+
+       context = deparse_context_for(rte->relname, rte->relid);
+
+       /* Fix qual --- indexqual requires different processing */
+       if (is_or_qual)
+               node = make_ors_ands_explicit(qual);
+       else
+               node = (Node *) make_ands_explicit(qual);
+
+       /* Deparse the expression */
+       exprstr = deparse_expression(node, context, false);
+
+       /* And add to str */
+       for (i = 0; i < indent; i++)
+               appendStringInfo(str, "  ");
+       appendStringInfo(str, "  %s: %s\n", qlabel, exprstr);
+}
+
+/*
+ * Show a qualifier expression for an upper-level plan node
+ */
+static void
+show_upper_qual(List *qual, const char *qlabel,
+                               const char *outer_name, int outer_varno, Plan *outer_plan,
+                               const char *inner_name, int inner_varno, Plan *inner_plan,
+                               StringInfo str, int indent, ExplainState *es)
+{
+       List       *context;
+       Node       *outercontext;
+       Node       *innercontext;
+       Node       *node;
+       char       *exprstr;
+       int                     i;
+
+       /* No work if empty qual */
+       if (qual == NIL)
+               return;
+
+       /* Generate deparse context */
+       if (outer_plan)
+               outercontext = deparse_context_for_subplan(outer_name,
+                                                                                                  outer_plan->targetlist,
+                                                                                                  es->rtable);
+       else
+               outercontext = NULL;
+       if (inner_plan)
+               innercontext = deparse_context_for_subplan(inner_name,
+                                                                                                  inner_plan->targetlist,
+                                                                                                  es->rtable);
+       else
+               innercontext = NULL;
+       context = deparse_context_for_plan(outer_varno, outercontext,
+                                                                          inner_varno, innercontext);
+
+       /* Deparse the expression */
+       node = (Node *) make_ands_explicit(qual);
+       exprstr = deparse_expression(node, context, (inner_plan != NULL));
+
+       /* And add to str */
+       for (i = 0; i < indent; i++)
+               appendStringInfo(str, "  ");
+       appendStringInfo(str, "  %s: %s\n", qlabel, exprstr);
+}
+
+/*
+ * Indexscan qual lists have an implicit OR-of-ANDs structure.  Make it
+ * explicit so deparsing works properly.
+ */
+static Node *
+make_ors_ands_explicit(List *orclauses)
+{
+       if (orclauses == NIL)
+               return NULL;                    /* probably can't happen */
+       else if (lnext(orclauses) == NIL)
+               return (Node *) make_ands_explicit(lfirst(orclauses));
+       else
+       {
+               List   *args = NIL;
+               List   *orptr;
+
+               foreach(orptr, orclauses)
+               {
+                       args = lappend(args, make_ands_explicit(lfirst(orptr)));
+               }
+
+               return (Node *) make_orclause(args);
+       }
+}
index ae9ac430456dc1c4ee3af3ac55d47ebb4411eb9c..97eeb35ea38303ff68ed809bde2b9399e6fd3605 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.168 2002/03/08 04:37:16 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.169 2002/03/12 00:51:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -323,6 +323,7 @@ CopyJoinFields(Join *from, Join *newnode)
 {
        newnode->jointype = from->jointype;
        Node_Copy(from, newnode, joinqual);
+       newnode->joinrti = from->joinrti;
        /* subPlan list must point to subplans in the new subtree, not the old */
        if (from->plan.subPlan != NIL)
                newnode->plan.subPlan = nconc(newnode->plan.subPlan,
@@ -970,8 +971,7 @@ _copyJoinExpr(JoinExpr *from)
        Node_Copy(from, newnode, using);
        Node_Copy(from, newnode, quals);
        Node_Copy(from, newnode, alias);
-       Node_Copy(from, newnode, colnames);
-       Node_Copy(from, newnode, colvars);
+       newnode->rtindex = from->rtindex;
 
        return newnode;
 }
@@ -1081,16 +1081,13 @@ _copyArrayRef(ArrayRef *from)
  *             _copyRelOptInfo
  * ----------------
  */
-/*
- *     when you change this, also make sure to fix up xfunc_copyRelOptInfo in
- *     planner/path/xfunc.c accordingly!!!
- *             -- JMH, 8/2/93
- */
 static RelOptInfo *
 _copyRelOptInfo(RelOptInfo *from)
 {
        RelOptInfo *newnode = makeNode(RelOptInfo);
 
+       newnode->reloptkind = from->reloptkind;
+
        newnode->relids = listCopy(from->relids);
 
        newnode->rows = from->rows;
@@ -1109,6 +1106,9 @@ _copyRelOptInfo(RelOptInfo *from)
        newnode->tuples = from->tuples;
        Node_Copy(from, newnode, subplan);
 
+       newnode->joinrti = from->joinrti;
+       newnode->joinrteids = listCopy(from->joinrteids);
+
        Node_Copy(from, newnode, baserestrictinfo);
        newnode->baserestrictcost = from->baserestrictcost;
        newnode->outerjoinset = listCopy(from->outerjoinset);
@@ -1487,10 +1487,16 @@ _copyRangeTblEntry(RangeTblEntry *from)
 {
        RangeTblEntry *newnode = makeNode(RangeTblEntry);
 
+       newnode->rtekind = from->rtekind;
        if (from->relname)
                newnode->relname = pstrdup(from->relname);
        newnode->relid = from->relid;
        Node_Copy(from, newnode, subquery);
+       newnode->jointype = from->jointype;
+       newnode->joincoltypes = listCopy(from->joincoltypes);
+       newnode->joincoltypmods = listCopy(from->joincoltypmods);
+       newnode->joinleftcols = listCopy(from->joinleftcols);
+       newnode->joinrightcols = listCopy(from->joinrightcols);
        Node_Copy(from, newnode, alias);
        Node_Copy(from, newnode, eref);
        newnode->inh = from->inh;
index b9269c5a834a92949898d63c079350537b1b3f28..760554dbb63f40d119a2ee1dd96694bc44e8b78b 100644 (file)
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.116 2002/03/08 04:37:16 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.117 2002/03/12 00:51:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -335,9 +335,7 @@ _equalJoinExpr(JoinExpr *a, JoinExpr *b)
                return false;
        if (!equal(a->alias, b->alias))
                return false;
-       if (!equal(a->colnames, b->colnames))
-               return false;
-       if (!equal(a->colvars, b->colvars))
+       if (a->rtindex != b->rtindex)
                return false;
 
        return true;
@@ -1639,12 +1637,24 @@ _equalTargetEntry(TargetEntry *a, TargetEntry *b)
 static bool
 _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
 {
+       if (a->rtekind != b->rtekind)
+               return false;
        if (!equalstr(a->relname, b->relname))
                return false;
        if (a->relid != b->relid)
                return false;
        if (!equal(a->subquery, b->subquery))
                return false;
+       if (a->jointype != b->jointype)
+               return false;
+       if (!equali(a->joincoltypes, b->joincoltypes))
+               return false;
+       if (!equali(a->joincoltypmods, b->joincoltypmods))
+               return false;
+       if (!equali(a->joinleftcols, b->joinleftcols))
+               return false;
+       if (!equali(a->joinrightcols, b->joinrightcols))
+               return false;
        if (!equal(a->alias, b->alias))
                return false;
        if (!equal(a->eref, b->eref))
index d3b737ed21306a2ee3e57503582c6901988b9a87..3699fc38ffb6e9e0b57d0768c5d2db88eca5f33a 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.148 2002/03/06 06:09:49 momjian Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.149 2002/03/12 00:51:39 tgl Exp $
  *
  * NOTES
  *       Every (plan) node in POSTGRES has an associated "out" routine which
@@ -410,6 +410,8 @@ _outJoin(StringInfo str, Join *node)
        appendStringInfo(str, " :jointype %d :joinqual ",
                                         (int) node->jointype);
        _outNode(str, node->joinqual);
+       appendStringInfo(str, " :joinrti %d ",
+                                        node->joinrti);
 }
 
 /*
@@ -423,6 +425,8 @@ _outNestLoop(StringInfo str, NestLoop *node)
        appendStringInfo(str, " :jointype %d :joinqual ",
                                         (int) node->join.jointype);
        _outNode(str, node->join.joinqual);
+       appendStringInfo(str, " :joinrti %d ",
+                                        node->join.joinrti);
 }
 
 /*
@@ -436,6 +440,8 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
        appendStringInfo(str, " :jointype %d :joinqual ",
                                         (int) node->join.jointype);
        _outNode(str, node->join.joinqual);
+       appendStringInfo(str, " :joinrti %d ",
+                                        node->join.joinrti);
 
        appendStringInfo(str, " :mergeclauses ");
        _outNode(str, node->mergeclauses);
@@ -452,6 +458,8 @@ _outHashJoin(StringInfo str, HashJoin *node)
        appendStringInfo(str, " :jointype %d :joinqual ",
                                         (int) node->join.jointype);
        _outNode(str, node->join.joinqual);
+       appendStringInfo(str, " :joinrti %d ",
+                                        node->join.joinrti);
 
        appendStringInfo(str, " :hashclauses ");
        _outNode(str, node->hashclauses);
@@ -939,10 +947,7 @@ _outJoinExpr(StringInfo str, JoinExpr *node)
        _outNode(str, node->quals);
        appendStringInfo(str, " :alias ");
        _outNode(str, node->alias);
-       appendStringInfo(str, " :colnames ");
-       _outNode(str, node->colnames);
-       appendStringInfo(str, " :colvars ");
-       _outNode(str, node->colvars);
+       appendStringInfo(str, " :rtindex %d ", node->rtindex);
 }
 
 /*
@@ -961,12 +966,21 @@ _outTargetEntry(StringInfo str, TargetEntry *node)
 static void
 _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 {
-       appendStringInfo(str, " RTE :relname ");
+       appendStringInfo(str, " RTE :rtekind %d :relname ",
+                                        (int) node->rtekind);
        _outToken(str, node->relname);
-       appendStringInfo(str, " :relid %u ",
+       appendStringInfo(str, " :relid %u :subquery ",
                                         node->relid);
-       appendStringInfo(str, " :subquery ");
        _outNode(str, node->subquery);
+       appendStringInfo(str, " :jointype %d :joincoltypes ",
+                                        (int) node->jointype);
+       _outOidList(str, node->joincoltypes);
+       appendStringInfo(str, " :joincoltypmods ");
+       _outIntList(str, node->joincoltypmods);
+       appendStringInfo(str, " :joinleftcols ");
+       _outIntList(str, node->joinleftcols);
+       appendStringInfo(str, " :joinrightcols ");
+       _outIntList(str, node->joinrightcols);
        appendStringInfo(str, " :alias ");
        _outNode(str, node->alias);
        appendStringInfo(str, " :eref ");
index 7cdd0c73c39aa751aa0bfd670bac3b81d9203c73..c0794123b3e54552172481f644437390424df788 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.115 2002/03/01 06:01:18 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.116 2002/03/12 00:51:39 tgl Exp $
  *
  * NOTES
  *       Most of the read functions for plan nodes are tested. (In fact, they
@@ -421,6 +421,10 @@ _getJoin(Join *node)
 
        token = pg_strtok(&length); /* skip the :joinqual */
        node->joinqual = nodeRead(true);        /* get the joinqual */
+
+       token = pg_strtok(&length); /* skip the :joinrti */
+       token = pg_strtok(&length); /* get the joinrti */
+       node->joinrti = atoi(token);
 }
 
 
@@ -1343,7 +1347,7 @@ _readJoinExpr(void)
        local_node->jointype = (JoinType) atoi(token);
 
        token = pg_strtok(&length); /* eat :isNatural */
-       token = pg_strtok(&length); /* get :isNatural */
+       token = pg_strtok(&length); /* get isNatural */
        local_node->isNatural = strtobool(token);
 
        token = pg_strtok(&length); /* eat :larg */
@@ -1361,11 +1365,9 @@ _readJoinExpr(void)
        token = pg_strtok(&length); /* eat :alias */
        local_node->alias = nodeRead(true); /* now read it */
 
-       token = pg_strtok(&length); /* eat :colnames */
-       local_node->colnames = nodeRead(true);          /* now read it */
-
-       token = pg_strtok(&length); /* eat :colvars */
-       local_node->colvars = nodeRead(true);           /* now read it */
+       token = pg_strtok(&length); /* eat :rtindex */
+       token = pg_strtok(&length); /* get rtindex */
+       local_node->rtindex = atoi(token);
 
        return local_node;
 }
@@ -1424,6 +1426,10 @@ _readRangeTblEntry(void)
 
        local_node = makeNode(RangeTblEntry);
 
+       token = pg_strtok(&length); /* eat :rtekind */
+       token = pg_strtok(&length); /* get :rtekind */
+       local_node->rtekind = (RTEKind) atoi(token);
+
        token = pg_strtok(&length); /* eat :relname */
        token = pg_strtok(&length); /* get :relname */
        local_node->relname = nullable_string(token, length);
@@ -1435,6 +1441,22 @@ _readRangeTblEntry(void)
        token = pg_strtok(&length); /* eat :subquery */
        local_node->subquery = nodeRead(true);          /* now read it */
 
+       token = pg_strtok(&length); /* eat :jointype */
+       token = pg_strtok(&length); /* get jointype */
+       local_node->jointype = (JoinType) atoi(token);
+
+       token = pg_strtok(&length); /* eat :joincoltypes */
+       local_node->joincoltypes = toOidList(nodeRead(true));
+
+       token = pg_strtok(&length); /* eat :joincoltypmods */
+       local_node->joincoltypmods = toIntList(nodeRead(true));
+
+       token = pg_strtok(&length); /* eat :joinleftcols */
+       local_node->joinleftcols = toIntList(nodeRead(true));
+
+       token = pg_strtok(&length); /* eat :joinrightcols */
+       local_node->joinrightcols = toIntList(nodeRead(true));
+
        token = pg_strtok(&length); /* eat :alias */
        local_node->alias = nodeRead(true); /* now read it */
 
index e215a6cd3669e6e400aa23eef3049ab170931562..2bc0bb8a137b5daab11e50b048ca2fe8842fbac4 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.82 2002/03/01 20:50:20 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.83 2002/03/12 00:51:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -597,7 +597,7 @@ cost_mergejoin(Path *path, Query *root,
 
        leftvar = get_leftop(firstclause->clause);
        Assert(IsA(leftvar, Var));
-       if (intMember(leftvar->varno, outer_path->parent->relids))
+       if (VARISRELMEMBER(leftvar->varno, outer_path->parent))
        {
                /* left side of clause is outer */
                outerscansel = firstclause->left_mergescansel;
@@ -748,7 +748,7 @@ cost_hashjoin(Path *path, Query *root,
         * a large query, we cache the bucketsize estimate in the RestrictInfo
         * node to avoid repeated lookups of statistics.
         */
-       if (intMember(right->varno, inner_path->parent->relids))
+       if (VARISRELMEMBER(right->varno, inner_path->parent))
        {
                /* righthand side is inner */
                innerbucketsize = restrictinfo->right_bucketsize;
@@ -761,7 +761,7 @@ cost_hashjoin(Path *path, Query *root,
        }
        else
        {
-               Assert(intMember(left->varno, inner_path->parent->relids));
+               Assert(VARISRELMEMBER(left->varno, inner_path->parent));
                /* lefthand side is inner */
                innerbucketsize = restrictinfo->left_bucketsize;
                if (innerbucketsize < 0)
index 4d60569e7ef6fc01ab9e5fb283c56dc5df7c35a8..11bc8a9f7d31bebfa56cb2dced84ccaea88c9799 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.67 2001/11/11 19:18:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.68 2002/03/12 00:51:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 
+
 static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
                                         RelOptInfo *outerrel, RelOptInfo *innerrel,
                                         List *restrictlist, List *mergeclause_list,
@@ -720,8 +721,6 @@ hash_inner_and_outer(Query *root,
                                         List *restrictlist,
                                         JoinType jointype)
 {
-       Relids          outerrelids = outerrel->relids;
-       Relids          innerrelids = innerrel->relids;
        bool            isouterjoin;
        List       *i;
 
@@ -773,13 +772,13 @@ hash_inner_and_outer(Query *root,
                /*
                 * Check if clause is usable with these input rels.
                 */
-               if (intMember(left->varno, outerrelids) &&
-                       intMember(right->varno, innerrelids))
+               if (VARISRELMEMBER(left->varno, outerrel) &&
+                       VARISRELMEMBER(right->varno, innerrel))
                {
                        /* righthand side is inner */
                }
-               else if (intMember(left->varno, innerrelids) &&
-                                intMember(right->varno, outerrelids))
+               else if (VARISRELMEMBER(left->varno, innerrel) &&
+                                VARISRELMEMBER(right->varno, outerrel))
                {
                        /* lefthand side is inner */
                }
@@ -901,8 +900,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
                                                 JoinType jointype)
 {
        List       *result_list = NIL;
-       Relids          outerrelids = outerrel->relids;
-       Relids          innerrelids = innerrel->relids;
        bool            isouterjoin = IS_OUTER_JOIN(jointype);
        List       *i;
 
@@ -952,10 +949,10 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
                left = get_leftop(clause);
                right = get_rightop(clause);
 
-               if ((intMember(left->varno, outerrelids) &&
-                        intMember(right->varno, innerrelids)) ||
-                       (intMember(left->varno, innerrelids) &&
-                        intMember(right->varno, outerrelids)))
+               if ((VARISRELMEMBER(left->varno, outerrel) &&
+                        VARISRELMEMBER(right->varno, innerrel)) ||
+                       (VARISRELMEMBER(left->varno, innerrel) &&
+                        VARISRELMEMBER(right->varno, outerrel)))
                        result_list = lcons(restrictinfo, result_list);
        }
 
index 745a1eb0b5b5e50dda2378932db76fb47c0107d9..d2d511e75e7ca25bdf554043715b354bf4f06e88 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.55 2001/10/25 05:49:32 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.56 2002/03/12 00:51:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -340,7 +340,7 @@ make_jointree_rel(Query *root, Node *jtnode)
        {
                int                     varno = ((RangeTblRef *) jtnode)->rtindex;
 
-               return build_base_rel(root, varno);
+               return find_base_rel(root, varno);
        }
        else if (IsA(jtnode, FromExpr))
        {
index f91e25cdb4fc986056efa91f0e86c314d90e8f05..60e05ca340f3e79adcaa6970484e7f3bcf307be6 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.36 2001/11/11 20:33:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.37 2002/03/12 00:51:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -854,7 +854,8 @@ make_pathkeys_for_mergeclauses(Query *root,
                cache_mergeclause_pathkeys(root, restrictinfo);
 
                key = (Node *) get_leftop(restrictinfo->clause);
-               if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids))
+               if (IsA(key, Var) &&
+                       VARISRELMEMBER(((Var *) key)->varno, rel))
                {
                        /* Rel is left side of mergeclause */
                        pathkey = restrictinfo->left_pathkey;
@@ -862,7 +863,8 @@ make_pathkeys_for_mergeclauses(Query *root,
                else
                {
                        key = (Node *) get_rightop(restrictinfo->clause);
-                       if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids))
+                       if (IsA(key, Var) &&
+                               VARISRELMEMBER(((Var *) key)->varno, rel))
                        {
                                /* Rel is right side of mergeclause */
                                pathkey = restrictinfo->right_pathkey;
index 88c5499b3024d0b274c4c61fd301e645dcd5c394..79d90bf5a3ccaa6d0bd0e21240774fc34345638c 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.111 2001/10/28 06:25:44 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.112 2002/03/12 00:51:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,7 +43,8 @@ static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
                                        List *scan_clauses);
 static SubqueryScan *create_subqueryscan_plan(Path *best_path,
                                                 List *tlist, List *scan_clauses);
-static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist,
+static NestLoop *create_nestloop_plan(Query *root,
+                                        NestPath *best_path, List *tlist,
                                         List *joinclauses, List *otherclauses,
                                         Plan *outer_plan, List *outer_tlist,
                                         Plan *inner_plan, List *inner_tlist);
@@ -52,7 +53,8 @@ static MergeJoin *create_mergejoin_plan(Query *root,
                                          List *joinclauses, List *otherclauses,
                                          Plan *outer_plan, List *outer_tlist,
                                          Plan *inner_plan, List *inner_tlist);
-static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
+static HashJoin *create_hashjoin_plan(Query *root,
+                                        HashPath *best_path, List *tlist,
                                         List *joinclauses, List *otherclauses,
                                         Plan *outer_plan, List *outer_tlist,
                                         Plan *inner_plan, List *inner_tlist);
@@ -78,18 +80,18 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
 static NestLoop *make_nestloop(List *tlist,
                          List *joinclauses, List *otherclauses,
                          Plan *lefttree, Plan *righttree,
-                         JoinType jointype);
+                         JoinType jointype, Index joinrti);
 static HashJoin *make_hashjoin(List *tlist,
                          List *joinclauses, List *otherclauses,
                          List *hashclauses,
                          Plan *lefttree, Plan *righttree,
-                         JoinType jointype);
+                         JoinType jointype, Index joinrti);
 static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
 static MergeJoin *make_mergejoin(List *tlist,
                           List *joinclauses, List *otherclauses,
                           List *mergeclauses,
                           Plan *lefttree, Plan *righttree,
-                          JoinType jointype);
+                          JoinType jointype, Index joinrti);
 
 /*
  * create_plan
@@ -259,7 +261,8 @@ create_join_plan(Query *root, JoinPath *best_path)
                                                                                                  inner_tlist);
                        break;
                case T_HashJoin:
-                       plan = (Join *) create_hashjoin_plan((HashPath *) best_path,
+                       plan = (Join *) create_hashjoin_plan(root,
+                                                                                                (HashPath *) best_path,
                                                                                                 join_tlist,
                                                                                                 joinclauses,
                                                                                                 otherclauses,
@@ -269,7 +272,8 @@ create_join_plan(Query *root, JoinPath *best_path)
                                                                                                 inner_tlist);
                        break;
                case T_NestLoop:
-                       plan = (Join *) create_nestloop_plan((NestPath *) best_path,
+                       plan = (Join *) create_nestloop_plan(root,
+                                                                                                (NestPath *) best_path,
                                                                                                 join_tlist,
                                                                                                 joinclauses,
                                                                                                 otherclauses,
@@ -576,7 +580,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
  *****************************************************************************/
 
 static NestLoop *
-create_nestloop_plan(NestPath *best_path,
+create_nestloop_plan(Query *root,
+                                        NestPath *best_path,
                                         List *tlist,
                                         List *joinclauses,
                                         List *otherclauses,
@@ -586,6 +591,7 @@ create_nestloop_plan(NestPath *best_path,
                                         List *inner_tlist)
 {
        NestLoop   *join_plan;
+       Index           joinrti = best_path->path.parent->joinrti;
 
        if (IsA(inner_plan, IndexScan))
        {
@@ -630,19 +636,25 @@ create_nestloop_plan(NestPath *best_path,
 
                        /* only refs to outer vars get changed in the inner indexqual */
                        innerscan->indxqualorig = join_references(indxqualorig,
+                                                                                                         root,
                                                                                                          outer_tlist,
                                                                                                          NIL,
-                                                                                                         innerrel);
+                                                                                                         innerrel,
+                                                                                                         joinrti);
                        innerscan->indxqual = join_references(innerscan->indxqual,
+                                                                                                 root,
                                                                                                  outer_tlist,
                                                                                                  NIL,
-                                                                                                 innerrel);
+                                                                                                 innerrel,
+                                                                                                 joinrti);
                        /* fix the inner qpqual too, if it has join clauses */
                        if (NumRelids((Node *) inner_plan->qual) > 1)
                                inner_plan->qual = join_references(inner_plan->qual,
+                                                                                                  root,
                                                                                                   outer_tlist,
                                                                                                   NIL,
-                                                                                                  innerrel);
+                                                                                                  innerrel,
+                                                                                                  joinrti);
                }
        }
        else if (IsA(inner_plan, TidScan))
@@ -650,9 +662,11 @@ create_nestloop_plan(NestPath *best_path,
                TidScan    *innerscan = (TidScan *) inner_plan;
 
                innerscan->tideval = join_references(innerscan->tideval,
+                                                                                        root,
                                                                                         outer_tlist,
                                                                                         inner_tlist,
-                                                                                        innerscan->scan.scanrelid);
+                                                                                        innerscan->scan.scanrelid,
+                                                                                        joinrti);
        }
        else if (IsA_Join(inner_plan))
        {
@@ -671,20 +685,25 @@ create_nestloop_plan(NestPath *best_path,
         * Set quals to contain INNER/OUTER var references.
         */
        joinclauses = join_references(joinclauses,
+                                                                 root,
                                                                  outer_tlist,
                                                                  inner_tlist,
-                                                                 (Index) 0);
+                                                                 (Index) 0,
+                                                                 joinrti);
        otherclauses = join_references(otherclauses,
+                                                                  root,
                                                                   outer_tlist,
                                                                   inner_tlist,
-                                                                  (Index) 0);
+                                                                  (Index) 0,
+                                                                  joinrti);
 
        join_plan = make_nestloop(tlist,
                                                          joinclauses,
                                                          otherclauses,
                                                          outer_plan,
                                                          inner_plan,
-                                                         best_path->jointype);
+                                                         best_path->jointype,
+                                                         joinrti);
 
        copy_path_costsize(&join_plan->join.plan, &best_path->path);
 
@@ -704,6 +723,7 @@ create_mergejoin_plan(Query *root,
 {
        List       *mergeclauses;
        MergeJoin  *join_plan;
+       Index           joinrti = best_path->jpath.path.parent->joinrti;
 
        mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
 
@@ -713,26 +733,32 @@ create_mergejoin_plan(Query *root,
         * clauses to contain INNER/OUTER var references.
         */
        joinclauses = join_references(set_difference(joinclauses, mergeclauses),
+                                                                 root,
                                                                  outer_tlist,
                                                                  inner_tlist,
-                                                                 (Index) 0);
+                                                                 (Index) 0,
+                                                                 joinrti);
 
        /*
         * Fix the additional qpquals too.
         */
        otherclauses = join_references(otherclauses,
+                                                                  root,
                                                                   outer_tlist,
                                                                   inner_tlist,
-                                                                  (Index) 0);
+                                                                  (Index) 0,
+                                                                  joinrti);
 
        /*
         * Now set the references in the mergeclauses and rearrange them so
         * that the outer variable is always on the left.
         */
        mergeclauses = switch_outer(join_references(mergeclauses,
+                                                                                               root,
                                                                                                outer_tlist,
                                                                                                inner_tlist,
-                                                                                               (Index) 0));
+                                                                                               (Index) 0,
+                                                                                               joinrti));
 
        /*
         * Create explicit sort nodes for the outer and inner join paths if
@@ -798,7 +824,8 @@ create_mergejoin_plan(Query *root,
                                                           mergeclauses,
                                                           outer_plan,
                                                           inner_plan,
-                                                          best_path->jpath.jointype);
+                                                          best_path->jpath.jointype,
+                                                          joinrti);
 
        copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
 
@@ -806,7 +833,8 @@ create_mergejoin_plan(Query *root,
 }
 
 static HashJoin *
-create_hashjoin_plan(HashPath *best_path,
+create_hashjoin_plan(Query *root,
+                                        HashPath *best_path,
                                         List *tlist,
                                         List *joinclauses,
                                         List *otherclauses,
@@ -819,6 +847,7 @@ create_hashjoin_plan(HashPath *best_path,
        HashJoin   *join_plan;
        Hash       *hash_plan;
        Node       *innerhashkey;
+       Index           joinrti = best_path->jpath.path.parent->joinrti;
 
        /*
         * NOTE: there will always be exactly one hashclause in the list
@@ -834,26 +863,32 @@ create_hashjoin_plan(HashPath *best_path,
         * clauses to contain INNER/OUTER var references.
         */
        joinclauses = join_references(set_difference(joinclauses, hashclauses),
+                                                                 root,
                                                                  outer_tlist,
                                                                  inner_tlist,
-                                                                 (Index) 0);
+                                                                 (Index) 0,
+                                                                 joinrti);
 
        /*
         * Fix the additional qpquals too.
         */
        otherclauses = join_references(otherclauses,
+                                                                  root,
                                                                   outer_tlist,
                                                                   inner_tlist,
-                                                                  (Index) 0);
+                                                                  (Index) 0,
+                                                                  joinrti);
 
        /*
         * Now set the references in the hashclauses and rearrange them so
         * that the outer variable is always on the left.
         */
        hashclauses = switch_outer(join_references(hashclauses,
+                                                                                          root,
                                                                                           outer_tlist,
                                                                                           inner_tlist,
-                                                                                          (Index) 0));
+                                                                                          (Index) 0,
+                                                                                          joinrti));
 
        /* Now the righthand op of the sole hashclause is the inner hash key. */
        innerhashkey = (Node *) get_rightop(lfirst(hashclauses));
@@ -868,7 +903,8 @@ create_hashjoin_plan(HashPath *best_path,
                                                          hashclauses,
                                                          outer_plan,
                                                          (Plan *) hash_plan,
-                                                         best_path->jpath.jointype);
+                                                         best_path->jpath.jointype,
+                                                         joinrti);
 
        copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
 
@@ -1327,7 +1363,8 @@ make_nestloop(List *tlist,
                          List *otherclauses,
                          Plan *lefttree,
                          Plan *righttree,
-                         JoinType jointype)
+                         JoinType jointype,
+                         Index joinrti)
 {
        NestLoop   *node = makeNode(NestLoop);
        Plan       *plan = &node->join.plan;
@@ -1340,6 +1377,7 @@ make_nestloop(List *tlist,
        plan->righttree = righttree;
        node->join.jointype = jointype;
        node->join.joinqual = joinclauses;
+       node->join.joinrti = joinrti;
 
        return node;
 }
@@ -1351,7 +1389,8 @@ make_hashjoin(List *tlist,
                          List *hashclauses,
                          Plan *lefttree,
                          Plan *righttree,
-                         JoinType jointype)
+                         JoinType jointype,
+                         Index joinrti)
 {
        HashJoin   *node = makeNode(HashJoin);
        Plan       *plan = &node->join.plan;
@@ -1365,6 +1404,7 @@ make_hashjoin(List *tlist,
        node->hashclauses = hashclauses;
        node->join.jointype = jointype;
        node->join.joinqual = joinclauses;
+       node->join.joinrti = joinrti;
 
        return node;
 }
@@ -1399,7 +1439,8 @@ make_mergejoin(List *tlist,
                           List *mergeclauses,
                           Plan *lefttree,
                           Plan *righttree,
-                          JoinType jointype)
+                          JoinType jointype,
+                          Index joinrti)
 {
        MergeJoin  *node = makeNode(MergeJoin);
        Plan       *plan = &node->join.plan;
@@ -1413,6 +1454,7 @@ make_mergejoin(List *tlist,
        node->mergeclauses = mergeclauses;
        node->join.jointype = jointype;
        node->join.joinqual = joinclauses;
+       node->join.joinrti = joinrti;
 
        return node;
 }
index 76677b4059caf5b1cb13723c7e5b21e645ec52b4..2c9acc73b7ff4a30d5a3c3fb1d1baba6dab94d7c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.66 2002/03/01 06:01:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.67 2002/03/12 00:51:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,67 +53,29 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
 
 /*****************************************************************************
  *
- *      TARGET LISTS
+ *      JOIN TREES
  *
  *****************************************************************************/
 
 /*
- * build_base_rel_tlists
- *       Creates rel nodes for every relation mentioned in the target list
- *       'tlist' (if a node hasn't already been created) and adds them to
- *       root->base_rel_list.  Creates targetlist entries for each var seen
- *       in 'tlist' and adds them to the tlist of the appropriate rel node.
- */
-void
-build_base_rel_tlists(Query *root, List *tlist)
-{
-       List       *tlist_vars = pull_var_clause((Node *) tlist, false);
-
-       add_vars_to_targetlist(root, tlist_vars);
-       freeList(tlist_vars);
-}
-
-/*
- * add_vars_to_targetlist
- *       For each variable appearing in the list, add it to the relation's
- *       targetlist if not already present.  Corresponding base rel nodes
- *       will be created if not already present.
- */
-static void
-add_vars_to_targetlist(Query *root, List *vars)
-{
-       List       *temp;
-
-       foreach(temp, vars)
-       {
-               Var                *var = (Var *) lfirst(temp);
-               RelOptInfo *rel = build_base_rel(root, var->varno);
-
-               add_var_to_tlist(rel, var);
-       }
-}
-
-/*----------
- * add_missing_rels_to_query
+ * add_base_rels_to_query
+ *
+ *       Scan the query's jointree and create baserel RelOptInfos for all
+ *       the base relations (ie, table and subquery RTEs) appearing in the
+ *       jointree.  Also, create otherrel RelOptInfos for join RTEs.
  *
- *       If we have a relation listed in the join tree that does not appear
- *       in the target list nor qualifications, we must add it to the base
- *       relation list so that it can be processed.  For instance,
- *                     select count(*) from foo;
- *       would fail to scan foo if this routine were not called.  More subtly,
- *                     select f.x from foo f, foo f2
- *       is a join of f and f2.  Note that if we have
- *                     select foo.x from foo f
- *       this also gets turned into a join (between foo as foo and foo as f).
+ * The return value is a list of all the baserel indexes (but not join RTE
+ * indexes) included in the scanned jointree.  This is actually just an
+ * internal convenience for marking join otherrels properly; no outside
+ * caller uses the result.
  *
- *       Returns a list of all the base relations (RelOptInfo nodes) that appear
- *       in the join tree.  This list can be used for cross-checking in the
- *       reverse direction, ie, that we have a join tree entry for every
- *       relation used in the query.
- *----------
+ * At the end of this process, there should be one baserel RelOptInfo for
+ * every non-join RTE that is used in the query.  Therefore, this routine
+ * is the only place that should call build_base_rel.  But build_other_rel
+ * will be used again later to build rels for inheritance children.
  */
 List *
-add_missing_rels_to_query(Query *root, Node *jtnode)
+add_base_rels_to_query(Query *root, Node *jtnode)
 {
        List       *result = NIL;
 
@@ -123,10 +85,8 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
        {
                int                     varno = ((RangeTblRef *) jtnode)->rtindex;
 
-               /* This call to build_base_rel does the primary work... */
-               RelOptInfo *rel = build_base_rel(root, varno);
-
-               result = makeList1(rel);
+               build_base_rel(root, varno);
+               result = makeListi1(varno);
        }
        else if (IsA(jtnode, FromExpr))
        {
@@ -136,24 +96,98 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
                foreach(l, f->fromlist)
                {
                        result = nconc(result,
-                                                  add_missing_rels_to_query(root, lfirst(l)));
+                                                  add_base_rels_to_query(root, lfirst(l)));
                }
        }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
+               RelOptInfo *jrel;
 
-               result = add_missing_rels_to_query(root, j->larg);
+               result = add_base_rels_to_query(root, j->larg);
                result = nconc(result,
-                                          add_missing_rels_to_query(root, j->rarg));
+                                          add_base_rels_to_query(root, j->rarg));
+               /* the join's own rtindex is NOT added to result */
+               jrel = build_other_rel(root, j->rtindex);
+               /*
+                * Mark the join's otherrel with outerjoinset = list of baserel ids
+                * included in the join.  Note we must copy here because result list
+                * is destructively modified by nconcs at higher levels.
+                */
+               jrel->outerjoinset = listCopy(result);
+               /*
+                * Safety check: join RTEs should not be SELECT FOR UPDATE targets
+                */
+               if (intMember(j->rtindex, root->rowMarks))
+                       elog(ERROR, "SELECT FOR UPDATE cannot be applied to a join");
        }
        else
-               elog(ERROR, "add_missing_rels_to_query: unexpected node type %d",
+               elog(ERROR, "add_base_rels_to_query: unexpected node type %d",
                         nodeTag(jtnode));
        return result;
 }
 
 
+/*****************************************************************************
+ *
+ *      TARGET LISTS
+ *
+ *****************************************************************************/
+
+/*
+ * build_base_rel_tlists
+ *       Creates targetlist entries for each var seen in 'tlist' and adds
+ *       them to the tlist of the appropriate rel node.
+ */
+void
+build_base_rel_tlists(Query *root, List *tlist)
+{
+       List       *tlist_vars = pull_var_clause((Node *) tlist, false);
+
+       add_vars_to_targetlist(root, tlist_vars);
+       freeList(tlist_vars);
+}
+
+/*
+ * add_vars_to_targetlist
+ *       For each variable appearing in the list, add it to the owning
+ *       relation's targetlist if not already present.
+ *
+ * Note that join alias variables will be attached to the otherrel for
+ * the join RTE.  They will later be transferred to the tlist of
+ * the corresponding joinrel.  We will also cause entries to be made
+ * for the Vars that the alias will eventually depend on.
+ */
+static void
+add_vars_to_targetlist(Query *root, List *vars)
+{
+       List       *temp;
+
+       foreach(temp, vars)
+       {
+               Var                *var = (Var *) lfirst(temp);
+               RelOptInfo *rel = find_base_rel(root, var->varno);
+
+               add_var_to_tlist(rel, var);
+
+               if (rel->reloptkind == RELOPT_OTHER_JOIN_REL)
+               {
+                       /* Var is an alias */
+                       Var        *leftsubvar,
+                                  *rightsubvar;
+
+                       build_join_alias_subvars(root, var,
+                                                                        &leftsubvar, &rightsubvar);
+
+                       rel = find_base_rel(root, leftsubvar->varno);
+                       add_var_to_tlist(rel, leftsubvar);
+                       rel = find_base_rel(root, rightsubvar->varno);
+                       add_var_to_tlist(rel, rightsubvar);
+               }
+       }
+}
+
+
 /*****************************************************************************
  *
  *       QUALIFICATIONS
@@ -165,10 +199,9 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
  * distribute_quals_to_rels
  *       Recursively scan the query's join tree for WHERE and JOIN/ON qual
  *       clauses, and add these to the appropriate RestrictInfo and JoinInfo
- *       lists belonging to base RelOptInfos.  New base rel entries are created
- *       as needed.  Also, base RelOptInfos are marked with outerjoinset
- *       information, to aid in proper positioning of qual clauses that appear
- *       above outer joins.
+ *       lists belonging to base RelOptInfos.  Also, base RelOptInfos are marked
+ *       with outerjoinset information, to aid in proper positioning of qual
+ *       clauses that appear above outer joins.
  *
  * NOTE: when dealing with inner joins, it is appropriate to let a qual clause
  * be evaluated at the lowest level where all the variables it mentions are
@@ -181,7 +214,7 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
  * a rel, thereby forcing them up the join tree to the right level.
  *
  * To ease the calculation of these values, distribute_quals_to_rels() returns
- * the list of Relids involved in its own level of join.  This is just an
+ * the list of base Relids involved in its own level of join.  This is just an
  * internal convenience; no outside callers pay attention to the result.
  */
 Relids
@@ -302,7 +335,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
        foreach(relid, rels)
        {
                int                     relno = lfirsti(relid);
-               RelOptInfo *rel = build_base_rel(root, relno);
+               RelOptInfo *rel = find_base_rel(root, relno);
 
                /*
                 * Since we do this bottom-up, any outer-rels previously marked
@@ -382,11 +415,41 @@ distribute_qual_to_rels(Query *root, Node *clause,
        clause_get_relids_vars(clause, &relids, &vars);
 
        /*
-        * Cross-check: clause should contain no relids not within its scope.
-        * Otherwise the parser messed up.
+        * The clause might contain some join alias vars; if so, we want to
+        * remove the join otherrelids from relids and add the referent joins'
+        * scope lists instead (thus ensuring that the clause can be evaluated
+        * no lower than that join node).  We rely here on the marking done
+        * earlier by add_base_rels_to_query.
+        *
+        * We can combine this step with a cross-check that the clause contains
+        * no relids not within its scope.  If the first crosscheck succeeds,
+        * the clause contains no aliases and we needn't look more closely.
         */
        if (!is_subseti(relids, qualscope))
-               elog(ERROR, "JOIN qualification may not refer to other relations");
+       {
+               Relids          newrelids = NIL;
+               List       *relid;
+
+               foreach(relid, relids)
+               {
+                       RelOptInfo *rel = find_other_rel(root, lfirsti(relid));
+
+                       if (rel && rel->outerjoinset)
+                       {
+                               /* this relid is for a join RTE */
+                               newrelids = set_unioni(newrelids, rel->outerjoinset);
+                       }
+                       else
+                       {
+                               /* this relid is for a true baserel */
+                               newrelids = lappendi(newrelids, lfirsti(relid));
+                       }
+               }
+               relids = newrelids;
+               /* Now repeat the crosscheck */
+               if (!is_subseti(relids, qualscope))
+                       elog(ERROR, "JOIN qualification may not refer to other relations");
+       }
 
        /*
         * If the clause is variable-free, we force it to be evaluated at its
@@ -439,7 +502,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
                can_be_equijoin = true;
                foreach(relid, relids)
                {
-                       RelOptInfo *rel = build_base_rel(root, lfirsti(relid));
+                       RelOptInfo *rel = find_base_rel(root, lfirsti(relid));
 
                        if (rel->outerjoinset &&
                                !is_subseti(rel->outerjoinset, relids))
@@ -475,7 +538,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
                 * There is only one relation participating in 'clause', so
                 * 'clause' is a restriction clause for that relation.
                 */
-               RelOptInfo *rel = build_base_rel(root, lfirsti(relids));
+               RelOptInfo *rel = find_base_rel(root, lfirsti(relids));
 
                /*
                 * Check for a "mergejoinable" clause even though it's not a join
@@ -595,7 +658,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
                 * Find or make the joininfo node for this combination of rels,
                 * and add the restrictinfo node to it.
                 */
-               joininfo = find_joininfo_node(build_base_rel(root, cur_relid),
+               joininfo = find_joininfo_node(find_base_rel(root, cur_relid),
                                                                          unjoined_relids);
                joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
                                                                                           restrictinfo);
@@ -640,9 +703,6 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
         * If both vars belong to same rel, we need to look at that rel's
         * baserestrictinfo list.  If different rels, each will have a
         * joininfo node for the other, and we can scan either list.
-        *
-        * All baserel entries should already exist at this point, so use
-        * find_base_rel not build_base_rel.
         */
        rel1 = find_base_rel(root, irel1);
        if (irel1 == irel2)
index 57c9b963c734f57c39762024a4ca4912ec2f6543..ee1d18907121634221d977516191fb752095c5ef 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.67 2001/10/25 05:49:33 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.68 2002/03/12 00:51:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -176,48 +176,32 @@ subplanner(Query *root,
                   List *flat_tlist,
                   double tuple_fraction)
 {
-       List       *joined_rels;
-       List       *brel;
        RelOptInfo *final_rel;
        Plan       *resultplan;
        Path       *cheapestpath;
        Path       *presortedpath;
 
-       /*
-        * Examine the targetlist and qualifications, adding entries to
-        * base_rel_list as relation references are found (e.g., in the
-        * qualification, the targetlist, etc.).  Restrict and join clauses
-        * are added to appropriate lists belonging to the mentioned
-        * relations.  We also build lists of equijoined keys for pathkey
-        * construction.
-        */
+       /* init lists to empty */
        root->base_rel_list = NIL;
        root->other_rel_list = NIL;
        root->join_rel_list = NIL;
        root->equi_key_list = NIL;
 
-       build_base_rel_tlists(root, flat_tlist);
-
-       (void) distribute_quals_to_rels(root, (Node *) root->jointree);
-
        /*
-        * Make sure we have RelOptInfo nodes for all relations to be joined.
+        * Construct RelOptInfo nodes for all base relations in query.
         */
-       joined_rels = add_missing_rels_to_query(root, (Node *) root->jointree);
+       (void) add_base_rels_to_query(root, (Node *) root->jointree);
 
        /*
-        * Check that the join tree includes all the base relations used in
-        * the query --- otherwise, the parser or rewriter messed up.
+        * Examine the targetlist and qualifications, adding entries to
+        * baserel targetlists for all referenced Vars.  Restrict and join
+        * clauses are added to appropriate lists belonging to the mentioned
+        * relations.  We also build lists of equijoined keys for pathkey
+        * construction.
         */
-       foreach(brel, root->base_rel_list)
-       {
-               RelOptInfo *baserel = (RelOptInfo *) lfirst(brel);
-               int                     relid = lfirsti(baserel->relids);
+       build_base_rel_tlists(root, flat_tlist);
 
-               if (!ptrMember(baserel, joined_rels))
-                       elog(ERROR, "Internal error: no jointree entry for rel %s (%d)",
-                                rt_fetch(relid, root->rtable)->eref->relname, relid);
-       }
+       (void) distribute_quals_to_rels(root, (Node *) root->jointree);
 
        /*
         * Use the completed lists of equijoined keys to deduce any implied
index eb92c3d3af95771bc110f9e6956ae7610352d642..1df2cd2940226741aab5967a758855b4e53dbe6a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.114 2001/12/10 22:54:12 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.115 2002/03/12 00:51:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -99,7 +99,7 @@ planner(Query *parse)
        result_plan->nParamExec = length(PlannerParamVar);
 
        /* final cleanup of the plan */
-       set_plan_references(result_plan);
+       set_plan_references(parse, result_plan);
 
        /* restore state for outer planner, if any */
        PlannerQueryLevel = save_PlannerQueryLevel;
@@ -616,6 +616,9 @@ preprocess_jointree(Query *parse, Node *jtnode)
 static Node *
 preprocess_expression(Query *parse, Node *expr, int kind)
 {
+       bool            has_join_rtes;
+       List       *rt;
+
        /*
         * Simplify constant expressions.
         *
@@ -651,6 +654,29 @@ preprocess_expression(Query *parse, Node *expr, int kind)
        if (PlannerQueryLevel > 1)
                expr = SS_replace_correlation_vars(expr);
 
+       /*
+        * If the query has any join RTEs, try to replace join alias variables
+        * with base-relation variables, to allow quals to be pushed down.
+        * We must do this after sublink processing, since it does not recurse
+        * into sublinks.
+        *
+        * The flattening pass is expensive enough that it seems worthwhile to
+        * scan the rangetable to see if we can avoid it.
+        */
+       has_join_rtes = false;
+       foreach(rt, parse->rtable)
+       {
+               RangeTblEntry *rte = lfirst(rt);
+
+               if (rte->rtekind == RTE_JOIN)
+               {
+                       has_join_rtes = true;
+                       break;
+               }
+       }
+       if (has_join_rtes)
+               expr = flatten_join_alias_vars(expr, parse, 0);
+
        return expr;
 }
 
index 167901acdf0ad3379a18c910d9797ccb12468314..2f48821ece6867d7275c2497d8647776c84b3431 100644 (file)
@@ -9,25 +9,29 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.73 2001/11/05 17:46:26 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.74 2002/03/12 00:51:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include <sys/types.h>
-
 #include "postgres.h"
 
+#include <sys/types.h>
+
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/planmain.h"
 #include "optimizer/tlist.h"
+#include "optimizer/var.h"
+
 
 typedef struct
 {
+       Query      *root;
        List       *outer_tlist;
        List       *inner_tlist;
        Index           acceptable_rel;
+       Index           join_rti;
 } join_references_context;
 
 typedef struct
@@ -38,7 +42,7 @@ typedef struct
 } replace_vars_with_subplan_refs_context;
 
 static void fix_expr_references(Plan *plan, Node *node);
-static void set_join_references(Join *join);
+static void set_join_references(Query *root, Join *join);
 static void set_uppernode_references(Plan *plan, Index subvarno);
 static Node *join_references_mutator(Node *node,
                                                join_references_context *context);
@@ -71,7 +75,7 @@ static bool fix_opids_walker(Node *node, void *context);
  * Returns nothing of interest, but modifies internal fields of nodes.
  */
 void
-set_plan_references(Plan *plan)
+set_plan_references(Query *root, Plan *plan)
 {
        List       *pl;
 
@@ -115,16 +119,16 @@ set_plan_references(Plan *plan)
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
                        /* Recurse into subplan too */
-                       set_plan_references(((SubqueryScan *) plan)->subplan);
+                       set_plan_references(root, ((SubqueryScan *) plan)->subplan);
                        break;
                case T_NestLoop:
-                       set_join_references((Join *) plan);
+                       set_join_references(root, (Join *) plan);
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
                        fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
                        break;
                case T_MergeJoin:
-                       set_join_references((Join *) plan);
+                       set_join_references(root, (Join *) plan);
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
                        fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
@@ -132,7 +136,7 @@ set_plan_references(Plan *plan)
                                                        (Node *) ((MergeJoin *) plan)->mergeclauses);
                        break;
                case T_HashJoin:
-                       set_join_references((Join *) plan);
+                       set_join_references(root, (Join *) plan);
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
                        fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
@@ -186,7 +190,7 @@ set_plan_references(Plan *plan)
                         * recurse into subplans.
                         */
                        foreach(pl, ((Append *) plan)->appendplans)
-                               set_plan_references((Plan *) lfirst(pl));
+                               set_plan_references(root, (Plan *) lfirst(pl));
                        break;
                default:
                        elog(ERROR, "set_plan_references: unknown plan type %d",
@@ -203,21 +207,21 @@ set_plan_references(Plan *plan)
         * plan's var nodes against the already-modified nodes of the
         * subplans.
         */
-       set_plan_references(plan->lefttree);
-       set_plan_references(plan->righttree);
+       set_plan_references(root, plan->lefttree);
+       set_plan_references(root, plan->righttree);
        foreach(pl, plan->initPlan)
        {
                SubPlan    *sp = (SubPlan *) lfirst(pl);
 
                Assert(IsA(sp, SubPlan));
-               set_plan_references(sp->plan);
+               set_plan_references(root, sp->plan);
        }
        foreach(pl, plan->subPlan)
        {
                SubPlan    *sp = (SubPlan *) lfirst(pl);
 
                Assert(IsA(sp, SubPlan));
-               set_plan_references(sp->plan);
+               set_plan_references(root, sp->plan);
        }
 }
 
@@ -256,7 +260,7 @@ fix_expr_references(Plan *plan, Node *node)
  * 'join' is a join plan node
  */
 static void
-set_join_references(Join *join)
+set_join_references(Query *root, Join *join)
 {
        Plan       *outer = join->plan.lefttree;
        Plan       *inner = join->plan.righttree;
@@ -264,9 +268,11 @@ set_join_references(Join *join)
        List       *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
 
        join->plan.targetlist = join_references(join->plan.targetlist,
+                                                                                       root,
                                                                                        outer_tlist,
                                                                                        inner_tlist,
-                                                                                       (Index) 0);
+                                                                                       (Index) 0,
+                                                                                       join->joinrti);
 }
 
 /*
@@ -343,7 +349,8 @@ set_uppernode_references(Plan *plan, Index subvarno)
  *        Creates a new set of targetlist entries or join qual clauses by
  *        changing the varno/varattno values of variables in the clauses
  *        to reference target list values from the outer and inner join
- *        relation target lists.
+ *        relation target lists.  Also, any join alias variables in the
+ *        clauses are expanded into references to their component variables.
  *
  * This is used in two different scenarios: a normal join clause, where
  * all the Vars in the clause *must* be replaced by OUTER or INNER references;
@@ -360,21 +367,27 @@ set_uppernode_references(Plan *plan, Index subvarno)
  * 'inner_tlist' is the target list of the inner join relation, or NIL
  * 'acceptable_rel' is either zero or the rangetable index of a relation
  *             whose Vars may appear in the clause without provoking an error.
+ * 'join_rti' is either zero or the join RTE index of join alias variables
+ *             that should be expanded.
  *
  * Returns the new expression tree.  The original clause structure is
  * not modified.
  */
 List *
 join_references(List *clauses,
+                               Query *root,
                                List *outer_tlist,
                                List *inner_tlist,
-                               Index acceptable_rel)
+                               Index acceptable_rel,
+                               Index join_rti)
 {
        join_references_context context;
 
+       context.root = root;
        context.outer_tlist = outer_tlist;
        context.inner_tlist = inner_tlist;
        context.acceptable_rel = acceptable_rel;
+       context.join_rti = join_rti;
        return (List *) join_references_mutator((Node *) clauses, &context);
 }
 
@@ -387,12 +400,14 @@ join_references_mutator(Node *node,
        if (IsA(node, Var))
        {
                Var                *var = (Var *) node;
-               Var                *newvar = (Var *) copyObject(var);
                Resdom     *resdom;
 
+               /* First look for the var in the input tlists */
                resdom = tlist_member((Node *) var, context->outer_tlist);
                if (resdom)
                {
+                       Var        *newvar = (Var *) copyObject(var);
+
                        newvar->varno = OUTER;
                        newvar->varattno = resdom->resno;
                        return (Node *) newvar;
@@ -400,18 +415,33 @@ join_references_mutator(Node *node,
                resdom = tlist_member((Node *) var, context->inner_tlist);
                if (resdom)
                {
+                       Var        *newvar = (Var *) copyObject(var);
+
                        newvar->varno = INNER;
                        newvar->varattno = resdom->resno;
                        return (Node *) newvar;
                }
 
+               /* Perhaps it's a join alias that can be resolved to input vars? */
+               if (var->varno == context->join_rti)
+               {
+                       Node   *newnode;
+
+                       newnode = flatten_join_alias_vars((Node *) var,
+                                                                                         context->root,
+                                                                                         context->join_rti);
+                       /* Must now resolve the input vars... */
+                       newnode = join_references_mutator(newnode, context);
+                       return newnode;
+               }
+
                /*
-                * Var not in either tlist --- either raise an error, or return
-                * the Var unmodified.
+                * No referent found for Var --- either raise an error, or return
+                * the Var unmodified if it's for acceptable_rel.
                 */
                if (var->varno != context->acceptable_rel)
                        elog(ERROR, "join_references: variable not in subplan target lists");
-               return (Node *) newvar;
+               return (Node *) copyObject(var);
        }
        return expression_tree_mutator(node,
                                                                   join_references_mutator,
index 64d8b78f066b5b9e89414bd321c933a14237eb00..b2f18780fcced782cb394930d14cfb31da1b8ab2 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.71 2002/03/05 05:10:24 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.72 2002/03/12 00:51:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -673,7 +673,7 @@ List *
 expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
 {
        RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
-       Oid                     parentOID = rte->relid;
+       Oid                     parentOID;
        List       *inhOIDs;
        List       *inhRTIs;
        List       *l;
@@ -681,10 +681,11 @@ expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
        /* Does RT entry allow inheritance? */
        if (!rte->inh)
                return NIL;
-       Assert(parentOID != InvalidOid && rte->subquery == NULL);
+       Assert(rte->rtekind == RTE_RELATION);
        /* Always clear the parent's inh flag, see above comments */
        rte->inh = false;
        /* Fast path for common case of childless table */
+       parentOID = rte->relid;
        if (!has_subclass(parentOID))
                return NIL;
        /* Scan for all members of inheritance set */
@@ -811,6 +812,19 @@ adjust_inherited_attrs_mutator(Node *node,
                        rtr->rtindex = context->new_rt_index;
                return (Node *) rtr;
        }
+       if (IsA(node, JoinExpr))
+       {
+               /* Copy the JoinExpr node with correct mutation of subnodes */
+               JoinExpr *j;
+
+               j = (JoinExpr *) expression_tree_mutator(node,
+                                                                                                adjust_inherited_attrs_mutator,
+                                                                                                (void *) context);
+               /* now fix JoinExpr's rtindex */
+               if (j->rtindex == context->old_rt_index)
+                       j->rtindex = context->new_rt_index;
+               return (Node *) j;
+       }
 
        /*
         * We have to process RestrictInfo nodes specially: we do NOT want to
index 8a4bcf4d9c450bf02c3c94bcc0b15d5075cdc360..6585fb1905da64b6395815c8ef3abc1a3a12a76a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.93 2002/01/03 18:01:59 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.94 2002/03/12 00:51:50 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node,
                                        return true;
                                if (walker(join->quals, context))
                                        return true;
-                               if (walker((Node *) join->colvars, context))
-                                       return true;
-
                                /*
-                                * alias clause, using list, colnames list are deemed
-                                * uninteresting.
+                                * alias clause, using list are deemed uninteresting.
                                 */
                        }
                        break;
@@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node,
                                MUTATE(newnode->larg, join->larg, Node *);
                                MUTATE(newnode->rarg, join->rarg, Node *);
                                MUTATE(newnode->quals, join->quals, Node *);
-                               MUTATE(newnode->colvars, join->colvars, List *);
-                               /* We do not mutate alias, using, or colnames by default */
+                               /* We do not mutate alias or using by default */
                                return (Node *) newnode;
                        }
                        break;
index 457826f5d316ff7484a096c3ffbf8b3d6fee03fd..0fad16fbdd082bbb4bef7a3fadf61bd27f5e8414 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.35 2001/10/25 05:49:34 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,26 +40,26 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
 
 /*
  * build_base_rel
- *       Returns relation entry corresponding to 'relid', creating a new one
- *       if necessary.  This is for base relations.
+ *       Construct a new base relation RelOptInfo, and put it in the query's
+ *       base_rel_list.
  */
-RelOptInfo *
+void
 build_base_rel(Query *root, int relid)
 {
        List       *rels;
        RelOptInfo *rel;
 
-       /* Already made? */
+       /* Rel should not exist already */
        foreach(rels, root->base_rel_list)
        {
                rel = (RelOptInfo *) lfirst(rels);
 
                /* length(rel->relids) == 1 for all members of base_rel_list */
                if (lfirsti(rel->relids) == relid)
-                       return rel;
+                       elog(ERROR, "build_base_rel: rel already exists");
        }
 
-       /* It should not exist as an "other" rel */
+       /* It should not exist as an "other" rel, either */
        foreach(rels, root->other_rel_list)
        {
                rel = (RelOptInfo *) lfirst(rels);
@@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid)
 
        /* and add it to the list */
        root->base_rel_list = lcons(rel, root->base_rel_list);
-
-       return rel;
 }
 
 /*
  * build_other_rel
  *       Returns relation entry corresponding to 'relid', creating a new one
- *       if necessary.  This is for 'other' relations, which are just like
+ *       if necessary.  This is for 'other' relations, which are much like
  *       base relations except that they live in a different list.
  */
 RelOptInfo *
@@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid)
        /* No existing RelOptInfo for this other rel, so make a new one */
        rel = make_base_rel(root, relid);
 
+       /* if it's not a join rel, must be a child rel */
+       if (rel->reloptkind == RELOPT_BASEREL)
+               rel->reloptkind = RELOPT_OTHER_CHILD_REL;
+
        /* and add it to the list */
        root->other_rel_list = lcons(rel, root->other_rel_list);
 
@@ -127,8 +129,9 @@ static RelOptInfo *
 make_base_rel(Query *root, int relid)
 {
        RelOptInfo *rel = makeNode(RelOptInfo);
-       Oid                     relationObjectId;
+       RangeTblEntry *rte = rt_fetch(relid, root->rtable);
 
+       rel->reloptkind = RELOPT_BASEREL;
        rel->relids = makeListi1(relid);
        rel->rows = 0;
        rel->width = 0;
@@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid)
        rel->pages = 0;
        rel->tuples = 0;
        rel->subplan = NULL;
+       rel->joinrti = 0;
+       rel->joinrteids = NIL;
        rel->baserestrictinfo = NIL;
        rel->baserestrictcost = 0;
        rel->outerjoinset = NIL;
        rel->joininfo = NIL;
        rel->innerjoin = NIL;
 
-       /* Check rtable to see if it's a plain relation or a subquery */
-       relationObjectId = getrelid(relid, root->rtable);
-
-       if (relationObjectId != InvalidOid)
-       {
-               /* Plain relation --- retrieve statistics from the system catalogs */
-               bool            indexed;
-
-               get_relation_info(relationObjectId,
-                                                 &indexed, &rel->pages, &rel->tuples);
-               if (indexed)
-                       rel->indexlist = find_secondary_indexes(relationObjectId);
-       }
-       else
+       /* Check type of rtable entry */
+       switch (rte->rtekind)
        {
-               /* subquery --- mark it as such for later processing */
-               rel->issubquery = true;
+               case RTE_RELATION:
+               {
+                       /* Table --- retrieve statistics from the system catalogs */
+                       bool            indexed;
+
+                       get_relation_info(rte->relid,
+                                                         &indexed, &rel->pages, &rel->tuples);
+                       if (indexed)
+                               rel->indexlist = find_secondary_indexes(rte->relid);
+                       break;
+               }
+               case RTE_SUBQUERY:
+                       /* Subquery --- mark it as such for later processing */
+                       rel->issubquery = true;
+                       break;
+               case RTE_JOIN:
+                       /* Join --- must be an otherrel */
+                       rel->reloptkind = RELOPT_OTHER_JOIN_REL;
+                       break;
+               default:
+                       elog(ERROR, "make_base_rel: unsupported RTE kind %d",
+                                (int) rte->rtekind);
+                       break;
        }
 
        return rel;
@@ -203,6 +217,47 @@ find_base_rel(Query *root, int relid)
        return NULL;                            /* keep compiler quiet */
 }
 
+/*
+ * find_other_rel
+ *       Find an otherrel entry, if one exists for the given relid.
+ *       Return NULL if no entry.
+ */
+RelOptInfo *
+find_other_rel(Query *root, int relid)
+{
+       List       *rels;
+
+       foreach(rels, root->other_rel_list)
+       {
+               RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
+
+               if (lfirsti(rel->relids) == relid)
+                       return rel;
+       }
+       return NULL;
+}
+
+/*
+ * find_other_rel_for_join
+ *       Look for an otherrel for a join RTE matching the given baserel set.
+ *       Return NULL if no entry.
+ */
+RelOptInfo *
+find_other_rel_for_join(Query *root, List *relids)
+{
+       List       *rels;
+
+       foreach(rels, root->other_rel_list)
+       {
+               RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
+
+               if (rel->reloptkind == RELOPT_OTHER_JOIN_REL
+                       && sameseti(relids, rel->outerjoinset))
+                       return rel;
+       }
+       return NULL;
+}
+
 /*
  * find_join_rel
  *       Returns relation entry corresponding to 'relids' (a list of RT indexes),
@@ -252,6 +307,7 @@ build_join_rel(Query *root,
 {
        List       *joinrelids;
        RelOptInfo *joinrel;
+       RelOptInfo *joinrterel;
        List       *restrictlist;
        List       *new_outer_tlist;
        List       *new_inner_tlist;
@@ -286,6 +342,7 @@ build_join_rel(Query *root,
         * Nope, so make one.
         */
        joinrel = makeNode(RelOptInfo);
+       joinrel->reloptkind = RELOPT_JOINREL;
        joinrel->relids = joinrelids;
        joinrel->rows = 0;
        joinrel->width = 0;
@@ -299,30 +356,61 @@ build_join_rel(Query *root,
        joinrel->pages = 0;
        joinrel->tuples = 0;
        joinrel->subplan = NULL;
+       joinrel->joinrti = 0;
+       joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids),
+                                                               inner_rel->joinrteids);
        joinrel->baserestrictinfo = NIL;
        joinrel->baserestrictcost = 0;
        joinrel->outerjoinset = NIL;
        joinrel->joininfo = NIL;
        joinrel->innerjoin = NIL;
 
+       /* Is there a join RTE matching this join? */
+       joinrterel = find_other_rel_for_join(root, joinrelids);
+       if (joinrterel)
+       {
+               /* Yes, remember its RT index */
+               joinrel->joinrti = lfirsti(joinrterel->relids);
+               joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids);
+       }
+
        /*
         * Create a new tlist by removing irrelevant elements from both tlists
         * of the outer and inner join relations and then merging the results
         * together.
         *
+        * XXX right now we don't remove any irrelevant elements, we just
+        * append the two tlists together.  Someday consider pruning vars from the
+        * join's targetlist if they are needed only to evaluate restriction
+        * clauses of this join, and will never be accessed at higher levels of
+        * the plantree.
+        *
         * NOTE: the tlist order for a join rel will depend on which pair of
         * outer and inner rels we first try to build it from.  But the
         * contents should be the same regardless.
-        *
-        * XXX someday: consider pruning vars from the join's targetlist if they
-        * are needed only to evaluate restriction clauses of this join, and
-        * will never be accessed at higher levels of the plantree.
         */
        new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1);
        new_inner_tlist = new_join_tlist(inner_rel->targetlist,
                                                                         length(new_outer_tlist) + 1);
        joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
 
+       /*
+        * If there are any alias variables attached to the matching join RTE,
+        * attach them to the tlist too, so that they will be evaluated for use
+        * at higher plan levels.
+        */
+       if (joinrterel)
+       {
+               List   *jrtetl;
+
+               foreach(jrtetl, joinrterel->targetlist)
+               {
+                       TargetEntry *jrtete = lfirst(jrtetl);
+
+                       add_var_to_tlist(joinrel, (Var *) jrtete->expr);
+               }
+       }
+
        /*
         * Construct restrict and join clause lists for the new joinrel. (The
         * caller might or might not need the restrictlist, but I need it
index 2521ffb2b6c7ed962332b9e411ead79e8d2aec17..568e024d20ba20add8bf7c44912870ee270adcda 100644 (file)
@@ -8,15 +8,18 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.33 2001/10/25 05:49:34 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.34 2002/03/12 00:51:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "nodes/makefuncs.h"
 #include "nodes/plannodes.h"
 #include "optimizer/clauses.h"
 #include "optimizer/var.h"
+#include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
 
 
 typedef struct
@@ -38,6 +41,12 @@ typedef struct
        bool            includeUpperVars;
 } pull_var_clause_context;
 
+typedef struct
+{
+       Query      *root;
+       int                     expandRTI;
+} flatten_join_alias_vars_context;
+
 static bool pull_varnos_walker(Node *node,
                                   pull_varnos_context *context);
 static bool contain_var_reference_walker(Node *node,
@@ -45,6 +54,10 @@ static bool contain_var_reference_walker(Node *node,
 static bool contain_var_clause_walker(Node *node, void *context);
 static bool pull_var_clause_walker(Node *node,
                                           pull_var_clause_context *context);
+static Node *flatten_join_alias_vars_mutator(Node *node,
+                                               flatten_join_alias_vars_context *context);
+static Node *flatten_join_alias_var(Var *var, Query *root, int expandRTI);
+static Node *find_jointree_item(Node *jtnode, int rtindex);
 
 
 /*
@@ -297,3 +310,363 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
        return expression_tree_walker(node, pull_var_clause_walker,
                                                                  (void *) context);
 }
+
+
+/*
+ * flatten_join_alias_vars
+ *       Whereever possible, replace Vars that reference JOIN outputs with
+ *       references to the original relation variables instead.  This allows
+ *       quals involving such vars to be pushed down.  Vars that cannot be
+ *       simplified to non-join Vars are replaced by COALESCE expressions
+ *       if they have varno = expandRTI, and are left as JOIN RTE references
+ *       otherwise.  (Pass expandRTI = 0 to prevent all COALESCE expansion.)
+ *
+ *       Upper-level vars (with varlevelsup > 0) are ignored; normally there
+ *       should not be any by the time this routine is called.
+ *
+ * Does not examine subqueries, therefore must only be used after reduction
+ * of sublinks to subplans!
+ */
+Node *
+flatten_join_alias_vars(Node *node, Query *root, int expandRTI)
+{
+       flatten_join_alias_vars_context context;
+
+       context.root = root;
+       context.expandRTI = expandRTI;
+
+       return flatten_join_alias_vars_mutator(node, &context);
+}
+
+static Node *
+flatten_join_alias_vars_mutator(Node *node,
+                                                               flatten_join_alias_vars_context *context)
+{
+       if (node == NULL)
+               return NULL;
+       if (IsA(node, Var))
+       {
+               Var        *var = (Var *) node;
+
+               if (var->varlevelsup != 0)
+                       return node;            /* no need to copy, really */
+               return flatten_join_alias_var(var, context->root, context->expandRTI);
+       }
+       return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
+                                                                  (void *) context);
+}
+
+static Node *
+flatten_join_alias_var(Var *var, Query *root, int expandRTI)
+{
+       Index           varno = var->varno;
+       AttrNumber      varattno = var->varattno;
+       Oid                     vartype = var->vartype;
+       int32           vartypmod = var->vartypmod;
+       JoinExpr   *jexpr = NULL;
+
+       /*
+        * Loop to cope with joins of joins
+        */
+       for (;;)
+       {
+               RangeTblEntry *rte = rt_fetch(varno, root->rtable);
+               Index           leftrti,
+                                       rightrti;
+               AttrNumber      leftattno,
+                                       rightattno;
+               RangeTblEntry *subrte;
+               Oid                     subtype;
+               int32           subtypmod;
+
+               if (rte->rtekind != RTE_JOIN)
+                       break;                          /* reached a non-join RTE */
+               /*
+                * Find the RT indexes of the left and right children of the
+                * join node.  We have to search the join tree to do this,
+                * which is a major pain in the neck --- but keeping RT indexes
+                * in other RT entries is worse, because it makes modifying
+                * querytrees difficult.  (Perhaps we can improve on the
+                * rangetable/jointree datastructure someday.)  One thing we
+                * can do is avoid repeated searches while tracing a single
+                * variable down to its baserel.
+                */
+               if (jexpr == NULL)
+                       jexpr = (JoinExpr *)
+                               find_jointree_item((Node *) root->jointree, varno);
+               if (jexpr == NULL ||
+                       !IsA(jexpr, JoinExpr) ||
+                       jexpr->rtindex != varno)
+                       elog(ERROR, "flatten_join_alias_var: failed to find JoinExpr");
+               if (IsA(jexpr->larg, RangeTblRef))
+                       leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
+               else if (IsA(jexpr->larg, JoinExpr))
+                       leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
+               else
+               {
+                       elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
+                       leftrti = 0;            /* keep compiler quiet */
+               }
+               if (IsA(jexpr->rarg, RangeTblRef))
+                       rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
+               else if (IsA(jexpr->rarg, JoinExpr))
+                       rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
+               else
+               {
+                       elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
+                       rightrti = 0;           /* keep compiler quiet */
+               }
+               /*
+                * See if the join var is from the left side, the right side,
+                * or both (ie, it is a USING/NATURAL JOIN merger column).
+                */
+               Assert(varattno > 0);
+               leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
+               rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
+               if (leftattno && rightattno)
+               {
+                       /*
+                        * Var is a merge var.  If a left or right join, we can replace
+                        * it by the left or right input var respectively; we only need
+                        * a COALESCE for a full join.  However, beware of the possibility
+                        * that there's been a type promotion to make the input vars
+                        * compatible; do not replace a var by one of a different type!
+                        */
+                       if (rte->jointype == JOIN_INNER ||
+                               rte->jointype == JOIN_LEFT)
+                       {
+                               subrte = rt_fetch(leftrti, root->rtable);
+                               get_rte_attribute_type(subrte, leftattno,
+                                                                          &subtype, &subtypmod);
+                               if (vartype == subtype && vartypmod == subtypmod)
+                               {
+                                       varno = leftrti;
+                                       varattno = leftattno;
+                                       jexpr = (JoinExpr *) jexpr->larg;
+                                       continue;
+                               }
+                       }
+                       if (rte->jointype == JOIN_INNER ||
+                               rte->jointype == JOIN_RIGHT)
+                       {
+                               subrte = rt_fetch(rightrti, root->rtable);
+                               get_rte_attribute_type(subrte, rightattno,
+                                                                          &subtype, &subtypmod);
+                               if (vartype == subtype && vartypmod == subtypmod)
+                               {
+                                       varno = rightrti;
+                                       varattno = rightattno;
+                                       jexpr = (JoinExpr *) jexpr->rarg;
+                                       continue;
+                               }
+                       }
+                       /*
+                        * This var cannot be substituted directly, only with a COALESCE.
+                        * Do so only if it belongs to the particular join indicated by
+                        * the caller.
+                        */
+                       if (varno != expandRTI)
+                               break;
+                       {
+                               Node   *l_var,
+                                          *r_var;
+                               CaseExpr   *c = makeNode(CaseExpr);
+                               CaseWhen   *w = makeNode(CaseWhen);
+                               NullTest   *n = makeNode(NullTest);
+
+                               subrte = rt_fetch(leftrti, root->rtable);
+                               get_rte_attribute_type(subrte, leftattno,
+                                                                          &subtype, &subtypmod);
+                               l_var = (Node *) makeVar(leftrti,
+                                                                                leftattno,
+                                                                                subtype,
+                                                                                subtypmod,
+                                                                                0);
+                               if (subtype != vartype)
+                               {
+                                       l_var = coerce_type(NULL, l_var, subtype,
+                                                                               vartype, vartypmod);
+                                       l_var = coerce_type_typmod(NULL, l_var,
+                                                                                          vartype, vartypmod);
+                               }
+                               else if (subtypmod != vartypmod)
+                                       l_var = coerce_type_typmod(NULL, l_var,
+                                                                                          vartype, vartypmod);
+
+                               subrte = rt_fetch(rightrti, root->rtable);
+                               get_rte_attribute_type(subrte, rightattno,
+                                                                          &subtype, &subtypmod);
+                               r_var = (Node *) makeVar(rightrti,
+                                                                                rightattno,
+                                                                                subtype,
+                                                                                subtypmod,
+                                                                                0);
+                               if (subtype != vartype)
+                               {
+                                       r_var = coerce_type(NULL, r_var, subtype,
+                                                                               vartype, vartypmod);
+                                       r_var = coerce_type_typmod(NULL, r_var,
+                                                                                          vartype, vartypmod);
+                               }
+                               else if (subtypmod != vartypmod)
+                                       r_var = coerce_type_typmod(NULL, r_var,
+                                                                                          vartype, vartypmod);
+
+                               n->arg = l_var;
+                               n->nulltesttype = IS_NOT_NULL;
+                               w->expr = (Node *) n;
+                               w->result = l_var;
+                               c->casetype = vartype;
+                               c->args = makeList1(w);
+                               c->defresult = r_var;
+                               return (Node *) c;
+                       }
+               }
+               else if (leftattno)
+               {
+                       /* Here we do not need to check the type */
+                       varno = leftrti;
+                       varattno = leftattno;
+                       jexpr = (JoinExpr *) jexpr->larg;
+               }
+               else
+               {
+                       Assert(rightattno);
+                       /* Here we do not need to check the type */
+                       varno = rightrti;
+                       varattno = rightattno;
+                       jexpr = (JoinExpr *) jexpr->rarg;
+               }
+       }
+
+       /*
+        * When we fall out of the loop, we've reached the base Var.
+        */
+       return (Node *) makeVar(varno,
+                                                       varattno,
+                                                       vartype,
+                                                       vartypmod,
+                                                       0);
+}
+
+/*
+ * Given a join alias Var, construct Vars for the two input vars it directly
+ * depends on.  Note that this should *only* be called for merger alias Vars.
+ * In practice it is only used for Vars that got past flatten_join_alias_vars.
+ */
+void
+build_join_alias_subvars(Query *root, Var *aliasvar,
+                                                Var **leftsubvar, Var **rightsubvar)
+{
+       Index           varno = aliasvar->varno;
+       AttrNumber      varattno = aliasvar->varattno;
+       RangeTblEntry *rte;
+       JoinExpr   *jexpr;
+       Index           leftrti,
+                               rightrti;
+       AttrNumber      leftattno,
+                               rightattno;
+       RangeTblEntry *subrte;
+       Oid                     subtype;
+       int32           subtypmod;
+
+       Assert(aliasvar->varlevelsup == 0);
+       rte = rt_fetch(varno, root->rtable);
+       Assert(rte->rtekind == RTE_JOIN);
+
+       /*
+        * Find the RT indexes of the left and right children of the
+        * join node.
+        */
+       jexpr = (JoinExpr *) find_jointree_item((Node *) root->jointree, varno);
+       if (jexpr == NULL ||
+               !IsA(jexpr, JoinExpr) ||
+               jexpr->rtindex != varno)
+               elog(ERROR, "build_join_alias_subvars: failed to find JoinExpr");
+       if (IsA(jexpr->larg, RangeTblRef))
+               leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
+       else if (IsA(jexpr->larg, JoinExpr))
+               leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
+       else
+       {
+               elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
+               leftrti = 0;                    /* keep compiler quiet */
+       }
+       if (IsA(jexpr->rarg, RangeTblRef))
+               rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
+       else if (IsA(jexpr->rarg, JoinExpr))
+               rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
+       else
+       {
+               elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
+               rightrti = 0;                   /* keep compiler quiet */
+       }
+
+       Assert(varattno > 0);
+       leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
+       rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
+       if (!(leftattno && rightattno))
+               elog(ERROR, "build_join_alias_subvars: non-merger variable");
+
+       subrte = rt_fetch(leftrti, root->rtable);
+       get_rte_attribute_type(subrte, leftattno,
+                                                  &subtype, &subtypmod);
+       *leftsubvar = makeVar(leftrti,
+                                                 leftattno,
+                                                 subtype,
+                                                 subtypmod,
+                                                 0);
+
+       subrte = rt_fetch(rightrti, root->rtable);
+       get_rte_attribute_type(subrte, rightattno,
+                                                  &subtype, &subtypmod);
+       *rightsubvar = makeVar(rightrti,
+                                                  rightattno,
+                                                  subtype,
+                                                  subtypmod,
+                                                  0);
+}
+
+/*
+ * Find jointree item matching the specified RT index
+ */
+static Node *
+find_jointree_item(Node *jtnode, int rtindex)
+{
+       if (jtnode == NULL)
+               return NULL;
+       if (IsA(jtnode, RangeTblRef))
+       {
+               if (((RangeTblRef *) jtnode)->rtindex == rtindex)
+                       return jtnode;
+       }
+       else if (IsA(jtnode, FromExpr))
+       {
+               FromExpr   *f = (FromExpr *) jtnode;
+               List       *l;
+
+               foreach(l, f->fromlist)
+               {
+                       jtnode = find_jointree_item(lfirst(l), rtindex);
+                       if (jtnode)
+                               return jtnode;
+               }
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
+
+               if (j->rtindex == rtindex)
+                       return jtnode;
+               jtnode = find_jointree_item(j->larg, rtindex);
+               if (jtnode)
+                       return jtnode;
+               jtnode = find_jointree_item(j->rarg, rtindex);
+               if (jtnode)
+                       return jtnode;
+       }
+       else
+               elog(ERROR, "find_jointree_item: unexpected node type %d",
+                        nodeTag(jtnode));
+       return NULL;
+}
index 5fb298e1c27a64bac5c22ba084cedb1c70db6f92..98f5030f78b756706dfd3a66d1afb21b33573fc6 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.219 2002/03/10 06:02:23 momjian Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.220 2002/03/12 00:51:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2045,10 +2045,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        Node       *node;
        List       *lefttl,
                           *dtlist,
-                          *targetvars,
+                          *colMods,
                           *targetnames,
-                          *sv_namespace;
-       JoinExpr   *jnode;
+                          *sv_namespace,
+                          *sv_rtable;
+       RangeTblEntry *jrte;
+       RangeTblRef *jrtr;
        int                     tllen;
 
        qry->commandType = CMD_SELECT;
@@ -2115,12 +2117,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        /*
         * Generate dummy targetlist for outer query using column names of
         * leftmost select and common datatypes of topmost set operation. Also
-        * make lists of the dummy vars and their names for use in parsing
-        * ORDER BY.
+        * make a list of the column names for use in parsing ORDER BY.
+        *
+        * XXX colMods is a hack to provide a dummy typmod list below.  We
+        * should probably keep track of common typmod instead.
         */
        qry->targetList = NIL;
-       targetvars = NIL;
        targetnames = NIL;
+       colMods = NIL;
        lefttl = leftmostQuery->targetList;
        foreach(dtlist, sostmt->colTypes)
        {
@@ -2135,15 +2139,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                                                        -1,
                                                        colName,
                                                        false);
-               expr = (Node *) makeVar(leftmostRTI,
+               expr = (Node *) makeVar(1,
                                                                leftResdom->resno,
                                                                colType,
                                                                -1,
                                                                0);
                qry->targetList = lappend(qry->targetList,
                                                                  makeTargetEntry(resdom, expr));
-               targetvars = lappend(targetvars, expr);
                targetnames = lappend(targetnames, makeString(colName));
+               colMods = lappendi(colMods, -1);
                lefttl = lnext(lefttl);
        }
 
@@ -2190,7 +2194,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        /*
         * As a first step towards supporting sort clauses that are
         * expressions using the output columns, generate a namespace entry
-        * that makes the output columns visible.  A JoinExpr node is handy
+        * that makes the output columns visible.  A Join RTE node is handy
         * for this, since we can easily control the Vars generated upon
         * matches.
         *
@@ -2198,12 +2202,23 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
         * "ORDER BY upper(foo)" will draw the right error message rather than
         * "foo not found".
         */
-       jnode = makeNode(JoinExpr);
-       jnode->colnames = targetnames;
-       jnode->colvars = targetvars;
+       jrte = addRangeTableEntryForJoin(NULL,
+                                                                        targetnames,
+                                                                        JOIN_INNER,
+                                                                        sostmt->colTypes,
+                                                                        colMods,
+                                                                        NIL,
+                                                                        NIL,
+                                                                        NULL,
+                                                                        true);
+       jrtr = makeNode(RangeTblRef);
+       jrtr->rtindex = 1;
+
+       sv_rtable = pstate->p_rtable;
+       pstate->p_rtable = makeList1(jrte);
 
        sv_namespace = pstate->p_namespace;
-       pstate->p_namespace = makeList1(jnode);
+       pstate->p_namespace = makeList1(jrtr);
 
        /*
         * For now, we don't support resjunk sort clauses on the output of a
@@ -2218,6 +2233,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                                                                                  qry->targetList);
 
        pstate->p_namespace = sv_namespace;
+       pstate->p_rtable = sv_rtable;
 
        if (tllen != length(qry->targetList))
                elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns");
index 61904439e7d5c3273f17798e1727aea73956f367..2f1eda4a9bf1502bdaf1b331f5fd754295792209 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.83 2001/10/25 05:49:37 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.84 2002/03/12 00:51:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -503,7 +503,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                                   *res_colnames,
                                   *l_colvars,
                                   *r_colvars,
-                                  *res_colvars;
+                                  *coltypes,
+                                  *coltypmods,
+                                  *leftcolnos,
+                                  *rightcolnos;
+               Index           leftrti,
+                                       rightrti;
+               RangeTblEntry *rte;
 
                /*
                 * Recursively process the left and right subtrees
@@ -525,39 +531,32 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
 
                /*
                 * Extract column name and var lists from both subtrees
+                *
+                * Note: expandRTE returns new lists, safe for me to modify
                 */
-               if (IsA(j->larg, JoinExpr))
-               {
-                       /* Make a copy of the subtree's lists so we can modify! */
-                       l_colnames = copyObject(((JoinExpr *) j->larg)->colnames);
-                       l_colvars = copyObject(((JoinExpr *) j->larg)->colvars);
-               }
+               if (IsA(j->larg, RangeTblRef))
+                       leftrti = ((RangeTblRef *) j->larg)->rtindex;
+               else if (IsA(j->larg, JoinExpr))
+                       leftrti = ((JoinExpr *) j->larg)->rtindex;
                else
                {
-                       RangeTblEntry *rte;
-
-                       Assert(IsA(j->larg, RangeTblRef));
-                       rte = rt_fetch(((RangeTblRef *) j->larg)->rtindex,
-                                                  pstate->p_rtable);
-                       expandRTE(pstate, rte, &l_colnames, &l_colvars);
-                       /* expandRTE returns new lists, so no need for copyObject */
-               }
-               if (IsA(j->rarg, JoinExpr))
-               {
-                       /* Make a copy of the subtree's lists so we can modify! */
-                       r_colnames = copyObject(((JoinExpr *) j->rarg)->colnames);
-                       r_colvars = copyObject(((JoinExpr *) j->rarg)->colvars);
+                       elog(ERROR, "transformFromClauseItem: unexpected subtree type");
+                       leftrti = 0;            /* keep compiler quiet */
                }
+               rte = rt_fetch(leftrti, pstate->p_rtable);
+               expandRTE(pstate, rte, &l_colnames, &l_colvars);
+
+               if (IsA(j->rarg, RangeTblRef))
+                       rightrti = ((RangeTblRef *) j->rarg)->rtindex;
+               else if (IsA(j->rarg, JoinExpr))
+                       rightrti = ((JoinExpr *) j->rarg)->rtindex;
                else
                {
-                       RangeTblEntry *rte;
-
-                       Assert(IsA(j->rarg, RangeTblRef));
-                       rte = rt_fetch(((RangeTblRef *) j->rarg)->rtindex,
-                                                  pstate->p_rtable);
-                       expandRTE(pstate, rte, &r_colnames, &r_colvars);
-                       /* expandRTE returns new lists, so no need for copyObject */
+                       elog(ERROR, "transformFromClauseItem: unexpected subtree type");
+                       rightrti = 0;           /* keep compiler quiet */
                }
+               rte = rt_fetch(rightrti, pstate->p_rtable);
+               expandRTE(pstate, rte, &r_colnames, &r_colvars);
 
                /*
                 * Natural join does not explicitly specify columns; must generate
@@ -604,7 +603,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                 * Now transform the join qualifications, if any.
                 */
                res_colnames = NIL;
-               res_colvars = NIL;
+               coltypes = NIL;
+               coltypmods = NIL;
+               leftcolnos = NIL;
+               rightcolnos = NIL;
 
                if (j->using)
                {
@@ -624,9 +626,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                        {
                                char       *u_colname = strVal(lfirst(ucol));
                                List       *col;
-                               Node       *l_colvar,
-                                                  *r_colvar,
-                                                  *colvar;
+                               Var                *l_colvar,
+                                                  *r_colvar;
+                               Oid                     outcoltype;
+                               int32           outcoltypmod;
                                int                     ndx;
                                int                     l_index = -1;
                                int                     r_index = -1;
@@ -672,34 +675,28 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
 
                                res_colnames = lappend(res_colnames,
                                                                           nth(l_index, l_colnames));
-                               switch (j->jointype)
+                               /*
+                                * Choose output type if input types are dissimilar.
+                                */
+                               outcoltype = l_colvar->vartype;
+                               outcoltypmod = l_colvar->vartypmod;
+                               if (outcoltype != r_colvar->vartype)
                                {
-                                       case JOIN_INNER:
-                                       case JOIN_LEFT:
-                                               colvar = l_colvar;
-                                               break;
-                                       case JOIN_RIGHT:
-                                               colvar = r_colvar;
-                                               break;
-                                       default:
-                                               {
-                                                       /* Need COALESCE(l_colvar, r_colvar) */
-                                                       CaseExpr   *c = makeNode(CaseExpr);
-                                                       CaseWhen   *w = makeNode(CaseWhen);
-                                                       NullTest   *n = makeNode(NullTest);
-
-                                                       n->arg = l_colvar;
-                                                       n->nulltesttype = IS_NOT_NULL;
-                                                       w->expr = (Node *) n;
-                                                       w->result = l_colvar;
-                                                       c->args = makeList1(w);
-                                                       c->defresult = r_colvar;
-                                                       colvar = transformExpr(pstate, (Node *) c,
-                                                                                                  EXPR_COLUMN_FIRST);
-                                                       break;
-                                               }
+                                       outcoltype =
+                                               select_common_type(makeListi2(l_colvar->vartype,
+                                                                                                         r_colvar->vartype),
+                                                                                  "JOIN/USING");
+                                       outcoltypmod = -1; /* ie, unknown */
                                }
-                               res_colvars = lappend(res_colvars, colvar);
+                               else if (outcoltypmod != r_colvar->vartypmod)
+                               {
+                                       /* same type, but not same typmod */
+                                       outcoltypmod = -1; /* ie, unknown */
+                               }
+                               coltypes = lappendi(coltypes, outcoltype);
+                               coltypmods = lappendi(coltypmods, outcoltypmod);
+                               leftcolnos = lappendi(leftcolnos, l_index+1);
+                               rightcolnos = lappendi(rightcolnos, r_index+1);
                        }
 
                        j->quals = transformJoinUsingClause(pstate,
@@ -724,30 +721,53 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                                                         r_colnames, r_colvars,
                                                         &r_colnames, &r_colvars);
                res_colnames = nconc(res_colnames, l_colnames);
-               res_colvars = nconc(res_colvars, l_colvars);
+               while (l_colvars)
+               {
+                       Var        *l_var = (Var *) lfirst(l_colvars);
+
+                       coltypes = lappendi(coltypes, l_var->vartype);
+                       coltypmods = lappendi(coltypmods, l_var->vartypmod);
+                       leftcolnos = lappendi(leftcolnos, l_var->varattno);
+                       rightcolnos = lappendi(rightcolnos, 0);
+                       l_colvars = lnext(l_colvars);
+               }
                res_colnames = nconc(res_colnames, r_colnames);
-               res_colvars = nconc(res_colvars, r_colvars);
+               while (r_colvars)
+               {
+                       Var        *r_var = (Var *) lfirst(r_colvars);
+
+                       coltypes = lappendi(coltypes, r_var->vartype);
+                       coltypmods = lappendi(coltypmods, r_var->vartypmod);
+                       leftcolnos = lappendi(leftcolnos, 0);
+                       rightcolnos = lappendi(rightcolnos, r_var->varattno);
+                       r_colvars = lnext(r_colvars);
+               }
 
                /*
-                * Process alias (AS clause), if any.
+                * Check alias (AS clause), if any.
                 */
                if (j->alias)
                {
-                       /*
-                        * If a column alias list is specified, substitute the alias
-                        * names into my output-column list
-                        */
                        if (j->alias->attrs != NIL)
                        {
-                               if (length(j->alias->attrs) != length(res_colnames))
-                                       elog(ERROR, "Column alias list for \"%s\" has wrong number of entries (need %d)",
-                                                j->alias->relname, length(res_colnames));
-                               res_colnames = j->alias->attrs;
+                               if (length(j->alias->attrs) > length(res_colnames))
+                                       elog(ERROR, "Column alias list for \"%s\" has too many entries",
+                                                j->alias->relname);
                        }
                }
 
-               j->colnames = res_colnames;
-               j->colvars = res_colvars;
+               /*
+                * Now build an RTE for the result of the join
+                */
+               rte = addRangeTableEntryForJoin(pstate, res_colnames,
+                                                                               j->jointype,
+                                                                               coltypes, coltypmods,
+                                                                               leftcolnos, rightcolnos,
+                                                                               j->alias, true);
+
+               /* assume new rte is at end */
+               j->rtindex = length(pstate->p_rtable);
+               Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable));
 
                return (Node *) j;
        }
index f740d632cc1e3ed5b2dd839aad9204e02a69be16..9c32fac23147507055320efb015e0be91acf2315 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.107 2002/03/07 16:35:36 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.108 2002/03/12 00:51:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -646,7 +646,7 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
         * appear
         */
        if (ident->indirection == NIL &&
-        refnameRangeOrJoinEntry(pstate, ident->name, &sublevels_up) != NULL)
+               refnameRangeTblEntry(pstate, ident->name, &sublevels_up) != NULL)
        {
                ident->isRel = TRUE;
                result = (Node *) ident;
index 766a5daad55b8fcaa5780896c20324535ec984f9..ed39d6c1036c8e02034e81311994ba690779fd8d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.116 2002/02/19 20:11:15 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.117 2002/03/12 00:51:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -316,7 +316,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                {
                        RangeTblEntry *rte;
                        int                     vnum;
-                       Node       *rteorjoin;
                        int                     sublevels_up;
 
                        /*
@@ -324,49 +323,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                         */
                        refname = ((Ident *) arg)->name;
 
-                       rteorjoin = refnameRangeOrJoinEntry(pstate, refname,
-                                                                                               &sublevels_up);
+                       rte = refnameRangeTblEntry(pstate, refname,
+                                                                          &sublevels_up);
 
-                       if (rteorjoin == NULL)
+                       if (rte == NULL)
                                rte = addImplicitRTE(pstate, refname);
-                       else if (IsA(rteorjoin, RangeTblEntry))
-                               rte = (RangeTblEntry *) rteorjoin;
-                       else if (IsA(rteorjoin, JoinExpr))
-                       {
-                               /*
-                                * The relation name refers to a join.  We can't support
-                                * functions on join tuples (since we don't have a named
-                                * type for the join tuples), so error out.
-                                */
-                               if (nargs == 1)
-                               {
-                                       /*
-                                        * We have f(x) or more likely x.f where x is a join
-                                        * and f is not one of the attribute names of the join
-                                        * (else we'd have recognized it above).  Give an
-                                        * appropriately vague error message.  Would be nicer
-                                        * to know which syntax was used...
-                                        */
-                                       elog(ERROR, "No such attribute or function %s.%s",
-                                                refname, funcname);
-                               }
-                               else
-                               {
-                                       /*
-                                        * There are multiple arguments, so it must be a
-                                        * function call.
-                                        */
-                                       elog(ERROR, "Cannot pass result of join %s to a function",
-                                                refname);
-                               }
-                               rte = NULL;             /* keep compiler quiet */
-                       }
-                       else
-                       {
-                               elog(ERROR, "ParseFuncOrColumn: unexpected node type %d",
-                                        nodeTag(rteorjoin));
-                               rte = NULL;             /* keep compiler quiet */
-                       }
 
                        vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
@@ -379,11 +340,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                         * sizeof(Pointer) to signal that the runtime representation
                         * will be a pointer not an Oid.
                         */
-                       if (rte->relname == NULL)
+                       if (rte->rtekind != RTE_RELATION)
                        {
                                /*
-                                * RTE is a subselect; must fail for lack of a specific
-                                * type
+                                * RTE is a join or subselect; must fail for lack of a
+                                * named tuple type
                                 */
                                if (nargs == 1)
                                {
@@ -397,7 +358,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                                }
                                else
                                {
-                                       elog(ERROR, "Cannot pass result of sub-select %s to a function",
+                                       elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
                                                 refname);
                                }
                        }
index a43dcb13af223a3afae05f91b5f607e2f6ad72b5..be825c26f9ec6667c708bbdea5e9ac7aee9ad5e9 100644 (file)
@@ -8,21 +8,22 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.58 2002/03/06 06:09:54 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.59 2002/03/12 00:51:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
+#include "postgres.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <float.h>
 
-#include "postgres.h"
-
 #include "access/heapam.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
 #include "fmgr.h"
 #include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_node.h"
@@ -165,51 +166,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
 {
        int                     vnum,
                                sublevels_up;
-       Oid                     vartypeid = 0;
-       int32           type_mod = 0;
+       Oid                     vartypeid;
+       int32           type_mod;
 
        vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
-
-       if (rte->relid != InvalidOid)
-       {
-               /* Plain relation RTE --- get the attribute's type info */
-               HeapTuple       tp;
-               Form_pg_attribute att_tup;
-
-               tp = SearchSysCache(ATTNUM,
-                                                       ObjectIdGetDatum(rte->relid),
-                                                       Int16GetDatum(attrno),
-                                                       0, 0);
-               /* this shouldn't happen... */
-               if (!HeapTupleIsValid(tp))
-                       elog(ERROR, "Relation %s does not have attribute %d",
-                                rte->relname, attrno);
-               att_tup = (Form_pg_attribute) GETSTRUCT(tp);
-               vartypeid = att_tup->atttypid;
-               type_mod = att_tup->atttypmod;
-               ReleaseSysCache(tp);
-       }
-       else
-       {
-               /* Subselect RTE --- get type info from subselect's tlist */
-               List       *tlistitem;
-
-               foreach(tlistitem, rte->subquery->targetList)
-               {
-                       TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
-
-                       if (te->resdom->resjunk || te->resdom->resno != attrno)
-                               continue;
-                       vartypeid = te->resdom->restype;
-                       type_mod = te->resdom->restypmod;
-                       break;
-               }
-               /* falling off end of list shouldn't happen... */
-               if (tlistitem == NIL)
-                       elog(ERROR, "Subquery %s does not have attribute %d",
-                                rte->eref->relname, attrno);
-       }
-
+       get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod);
        return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
 }
 
index 9440914a776cd43336ff928b3e1d90d55ac5db9d..1609c89ce07b796a78b41e5d7e3bd19a4643b56b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.62 2002/03/06 06:09:55 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.63 2002/03/12 00:51:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,38 +36,41 @@ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
                                                char *refname);
 static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
                                 char *colname);
-static Node *scanJoinForColumn(JoinExpr *join, char *colname,
-                                 int sublevels_up);
 static bool isForUpdate(ParseState *pstate, char *relname);
-static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
 static int     specialAttNum(char *a);
 static void warnAutoRange(ParseState *pstate, char *refname);
 
 
 /*
- * refnameRangeOrJoinEntry
- *       Given a refname, look to see if it matches any RTE or join table.
- *       If so, return a pointer to the RangeTblEntry or JoinExpr.
+ * refnameRangeTblEntry
+ *       Given a refname, look to see if it matches any RTE.
+ *       If so, return a pointer to the RangeTblEntry.
  *       Optionally get its nesting depth (0 = current).       If sublevels_up
  *       is NULL, only consider items at the current nesting level.
  */
-Node *
-refnameRangeOrJoinEntry(ParseState *pstate,
-                                               char *refname,
-                                               int *sublevels_up)
+RangeTblEntry *
+refnameRangeTblEntry(ParseState *pstate,
+                                        char *refname,
+                                        int *sublevels_up)
 {
        if (sublevels_up)
                *sublevels_up = 0;
 
        while (pstate != NULL)
        {
-               Node       *rte;
+               Node       *nsnode;
 
-               rte = scanNameSpaceForRefname(pstate,
-                                                                         (Node *) pstate->p_namespace,
-                                                                         refname);
-               if (rte)
-                       return rte;
+               nsnode = scanNameSpaceForRefname(pstate,
+                                                                                (Node *) pstate->p_namespace,
+                                                                                refname);
+               if (nsnode)
+               {
+                       /* should get an RTE or JoinExpr */
+                       if (IsA(nsnode, RangeTblEntry))
+                               return (RangeTblEntry *) nsnode;
+                       Assert(IsA(nsnode, JoinExpr));
+                       return rt_fetch(((JoinExpr *) nsnode)->rtindex, pstate->p_rtable);
+               }
 
                pstate = pstate->parentParseState;
                if (sublevels_up)
@@ -247,6 +250,12 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
  *
  * Side effect: if we find a match, mark the RTE as requiring read access.
  * See comments in setTargetTable().
+ *
+ * NOTE: if the RTE is for a join, marking it as requiring read access does
+ * nothing.  It might seem that we need to propagate the mark to all the
+ * contained RTEs, but that is not necessary.  This is so because a join
+ * expression can only appear in a FROM clause, and any table named in
+ * FROM will be marked checkForRead from the beginning.
  */
 static Node *
 scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
@@ -279,10 +288,9 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
                return result;
 
        /*
-        * If the RTE represents a table (not a sub-select), consider system
-        * column names.
+        * If the RTE represents a real table, consider system column names.
         */
-       if (rte->relid != InvalidOid)
+       if (rte->rtekind == RTE_RELATION)
        {
                /* quick check to see if name could be a system column */
                attnum = specialAttNum(colname);
@@ -303,44 +311,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
        return result;
 }
 
-/*
- * scanJoinForColumn
- *       Search the column names of a single join table for the given name.
- *       If found, return an appropriate Var node or expression, else return NULL.
- *       If the name proves ambiguous within this jointable, raise error.
- *
- * NOTE: unlike scanRTEForColumn, there's no need to worry about forcing
- * checkForRead true for the referenced tables.  This is so because a join
- * expression can only appear in a FROM clause, and any table named in
- * FROM will be marked checkForRead from the beginning.
- */
-static Node *
-scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
-{
-       Node       *result = NULL;
-       int                     attnum = 0;
-       List       *c;
-
-       foreach(c, join->colnames)
-       {
-               attnum++;
-               if (strcmp(strVal(lfirst(c)), colname) == 0)
-               {
-                       if (result)
-                               elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
-                       result = copyObject(nth(attnum - 1, join->colvars));
-
-                       /*
-                        * If referencing an uplevel join item, we must adjust
-                        * sublevels settings in the copied expression.
-                        */
-                       if (sublevels_up > 0)
-                               IncrementVarSublevelsUp(result, sublevels_up, 0);
-               }
-       }
-       return result;
-}
-
 /*
  * colnameToVar
  *       Search for an unqualified column name.
@@ -382,9 +352,13 @@ colnameToVar(ParseState *pstate, char *colname)
                        }
                        else if (IsA(nsnode, JoinExpr))
                        {
-                               JoinExpr   *j = (JoinExpr *) nsnode;
+                               int                     varno = ((JoinExpr *) nsnode)->rtindex;
+                               RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
 
-                               newresult = scanJoinForColumn(j, colname, levels_up);
+                               /* joins are always inFromCl, so no need to check */
+
+                               /* use orig_pstate here to get the right sublevels_up */
+                               newresult = scanRTEForColumn(orig_pstate, rte, colname);
                        }
                        else
                                elog(ERROR, "colnameToVar: unexpected node type %d",
@@ -412,41 +386,26 @@ colnameToVar(ParseState *pstate, char *colname)
 /*
  * qualifiedNameToVar
  *       Search for a qualified column name (refname + column name).
- *       If found, return the appropriate Var node (or expression).
+ *       If found, return the appropriate Var node.
  *       If not found, return NULL.  If the name proves ambiguous, raise error.
  */
 Node *
 qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
                                   bool implicitRTEOK)
 {
-       Node       *result;
-       Node       *rteorjoin;
+       RangeTblEntry *rte;
        int                     sublevels_up;
 
-       rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up);
+       rte = refnameRangeTblEntry(pstate, refname, &sublevels_up);
 
-       if (rteorjoin == NULL)
+       if (rte == NULL)
        {
                if (!implicitRTEOK)
                        return NULL;
-               rteorjoin = (Node *) addImplicitRTE(pstate, refname);
-               sublevels_up = 0;
+               rte = addImplicitRTE(pstate, refname);
        }
 
-       if (IsA(rteorjoin, RangeTblEntry))
-               result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
-                                                                 colname);
-       else if (IsA(rteorjoin, JoinExpr))
-               result = scanJoinForColumn((JoinExpr *) rteorjoin,
-                                                                  colname, sublevels_up);
-       else
-       {
-               elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
-                        nodeTag(rteorjoin));
-               result = NULL;                  /* keep compiler quiet */
-       }
-
-       return result;
+       return scanRTEForColumn(pstate, rte, colname);
 }
 
 /*
@@ -474,9 +433,9 @@ addRangeTableEntry(ParseState *pstate,
        int                     numaliases;
        int                     varattno;
 
+       rte->rtekind = RTE_RELATION;
        rte->relname = relname;
        rte->alias = alias;
-       rte->subquery = NULL;
 
        /*
         * Get the rel's OID.  This access also ensures that we have an
@@ -563,6 +522,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
        int                     varattno;
        List       *tlistitem;
 
+       rte->rtekind = RTE_SUBQUERY;
        rte->relname = NULL;
        rte->relid = InvalidOid;
        rte->subquery = subquery;
@@ -621,6 +581,76 @@ addRangeTableEntryForSubquery(ParseState *pstate,
        return rte;
 }
 
+/*
+ * Add an entry for a join to the pstate's range table (p_rtable).
+ *
+ * This is much like addRangeTableEntry() except that it makes a join RTE.
+ */
+RangeTblEntry *
+addRangeTableEntryForJoin(ParseState *pstate,
+                                                 List *colnames,
+                                                 JoinType jointype,
+                                                 List *coltypes,
+                                                 List *coltypmods,
+                                                 List *leftcols,
+                                                 List *rightcols,
+                                                 Attr *alias,
+                                                 bool inFromCl)
+{
+       RangeTblEntry *rte = makeNode(RangeTblEntry);
+       Attr       *eref;
+       int                     numaliases;
+
+       rte->rtekind = RTE_JOIN;
+       rte->relname = NULL;
+       rte->relid = InvalidOid;
+       rte->subquery = NULL;
+       rte->jointype = jointype;
+       rte->joincoltypes = coltypes;
+       rte->joincoltypmods = coltypmods;
+       rte->joinleftcols = leftcols;
+       rte->joinrightcols = rightcols;
+       rte->alias = alias;
+
+       eref = alias ? (Attr *) copyObject(alias) : makeAttr("unnamed_join", NULL);
+       numaliases = length(eref->attrs);
+
+       /* fill in any unspecified alias columns */
+       if (numaliases < length(colnames))
+       {
+               while (numaliases-- > 0)
+                       colnames = lnext(colnames);
+               eref->attrs = nconc(eref->attrs, colnames);
+       }
+
+       rte->eref = eref;
+
+       /*----------
+        * Flags:
+        * - this RTE should be expanded to include descendant tables,
+        * - this RTE is in the FROM clause,
+        * - this RTE should be checked for read/write access rights.
+        *
+        * Joins are never checked for access rights.
+        *----------
+        */
+       rte->inh = false;                       /* never true for joins */
+       rte->inFromCl = inFromCl;
+       rte->checkForRead = false;
+       rte->checkForWrite = false;
+
+       rte->checkAsUser = InvalidOid;
+
+       /*
+        * Add completed RTE to pstate's range table list, but not to join
+        * list nor namespace --- caller must do that if appropriate.
+        */
+       if (pstate != NULL)
+               pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+       return rte;
+}
+
 /*
  * Has the specified relname been selected FOR UPDATE?
  */
@@ -720,15 +750,16 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
        /* Need the RT index of the entry for creating Vars */
        rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
-       if (rte->relname)
+       if (rte->rtekind == RTE_RELATION)
        {
                /* Ordinary relation RTE */
                Relation        rel;
                int                     maxattrs;
+               int                     numaliases;
 
                rel = heap_openr(rte->relname, AccessShareLock);
-
                maxattrs = RelationGetNumberOfAttributes(rel);
+               numaliases = length(rte->eref->attrs);
 
                for (varattno = 0; varattno < maxattrs; varattno++)
                {
@@ -743,7 +774,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
                        {
                                char       *label;
 
-                               if (varattno < length(rte->eref->attrs))
+                               if (varattno < numaliases)
                                        label = strVal(nth(varattno, rte->eref->attrs));
                                else
                                        label = NameStr(attr->attname);
@@ -764,7 +795,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
 
                heap_close(rel, AccessShareLock);
        }
-       else
+       else if (rte->rtekind == RTE_SUBQUERY)
        {
                /* Subquery RTE */
                List       *aliasp = rte->eref->attrs;
@@ -802,56 +833,63 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
                        }
                }
        }
-}
+       else if (rte->rtekind == RTE_JOIN)
+       {
+               /* Join RTE */
+               List       *aliasp = rte->eref->attrs;
+               List       *coltypes = rte->joincoltypes;
+               List       *coltypmods = rte->joincoltypmods;
 
-/*
- * expandRelAttrs -
- *       makes a list of TargetEntry nodes for the attributes of the rel
- */
-List *
-expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
-{
-       List       *name_list,
-                          *var_list;
+               varattno = 0;
+               while (aliasp)
+               {
+                       Assert(coltypes && coltypmods);
+                       varattno++;
 
-       expandRTE(pstate, rte, &name_list, &var_list);
+                       if (colnames)
+                       {
+                               char       *label = strVal(lfirst(aliasp));
 
-       return expandNamesVars(pstate, name_list, var_list);
-}
+                               *colnames = lappend(*colnames, makeString(pstrdup(label)));
+                       }
 
-/*
- * expandJoinAttrs -
- *       makes a list of TargetEntry nodes for the attributes of the join
- */
-List *
-expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
-{
-       List       *vars;
+                       if (colvars)
+                       {
+                               Var                *varnode;
 
-       vars = copyObject(join->colvars);
+                               varnode = makeVar(rtindex, varattno,
+                                                                 (Oid) lfirsti(coltypes),
+                                                                 (int32) lfirsti(coltypmods),
+                                                                 sublevels_up);
 
-       /*
-        * If referencing an uplevel join item, we must adjust sublevels
-        * settings in the copied expression.
-        */
-       if (sublevels_up > 0)
-               IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0);
+                               *colvars = lappend(*colvars, varnode);
+                       }
 
-       return expandNamesVars(pstate,
-                                                  copyObject(join->colnames),
-                                                  vars);
+                       aliasp = lnext(aliasp);
+                       coltypes = lnext(coltypes);
+                       coltypmods = lnext(coltypmods);
+               }
+               Assert(coltypes == NIL && coltypmods == NIL);
+       }
+       else
+               elog(ERROR, "expandRTE: unsupported RTE kind %d",
+                        (int) rte->rtekind);
 }
 
 /*
- * expandNamesVars -
- *             Workhorse for "*" expansion: produce a list of targetentries
- *             given lists of column names (as String nodes) and var references.
+ * expandRelAttrs -
+ *       Workhorse for "*" expansion: produce a list of targetentries
+ *       for the attributes of the rte
  */
-static List *
-expandNamesVars(ParseState *pstate, List *names, List *vars)
+List *
+expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
 {
+       List       *names,
+                          *vars;
        List       *te_list = NIL;
 
+       expandRTE(pstate, rte, &names, &vars);
+
        while (names)
        {
                char       *label = strVal(lfirst(names));
@@ -875,22 +913,16 @@ expandNamesVars(ParseState *pstate, List *names, List *vars)
        return te_list;
 }
 
-/* ----------
+/*
  * get_rte_attribute_name
  *             Get an attribute name from a RangeTblEntry
  *
  * This is unlike get_attname() because we use aliases if available.
- * In particular, it will work on an RTE for a subselect, whereas
+ * In particular, it will work on an RTE for a subselect or join, whereas
  * get_attname() only works on real relations.
  *
  * "*" is returned if the given attnum is InvalidAttrNumber --- this case
  * occurs when a Var represents a whole tuple of a relation.
- *
- * XXX Actually, this is completely bogus, because refnames of RTEs are
- * not guaranteed unique, and may not even have scope across the whole
- * query.  Cleanest fix would be to add refname/attname to Var nodes and
- * just print those, rather than indulging in this hack.
- * ----------
  */
 char *
 get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
@@ -901,7 +933,8 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
                return "*";
 
        /*
-        * If there is an alias, use it
+        * If there is an alias, use it.  (This path should always be taken
+        * for non-relation RTEs.)
         */
        if (attnum > 0 && attnum <= length(rte->eref->attrs))
                return strVal(nth(attnum - 1, rte->eref->attrs));
@@ -909,9 +942,9 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
        /*
         * Can get here for a system attribute (which never has an alias), or
         * if alias name list is too short (which probably can't happen
-        * anymore).  Neither of these cases is valid for a subselect RTE.
+        * anymore).  Neither of these cases is valid for a non-relation RTE.
         */
-       if (rte->relid == InvalidOid)
+       if (rte->rtekind != RTE_RELATION)
                elog(ERROR, "Invalid attnum %d for rangetable entry %s",
                         attnum, rte->eref->relname);
 
@@ -925,6 +958,64 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
        return attname;
 }
 
+/*
+ * get_rte_attribute_type
+ *             Get attribute type information from a RangeTblEntry
+ */
+void
+get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
+                                          Oid *vartype, int32 *vartypmod)
+{
+       if (rte->rtekind == RTE_RELATION)
+       {
+               /* Plain relation RTE --- get the attribute's type info */
+               HeapTuple       tp;
+               Form_pg_attribute att_tup;
+
+               tp = SearchSysCache(ATTNUM,
+                                                       ObjectIdGetDatum(rte->relid),
+                                                       Int16GetDatum(attnum),
+                                                       0, 0);
+               /* this shouldn't happen... */
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "Relation %s does not have attribute %d",
+                                rte->relname, attnum);
+               att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+               *vartype = att_tup->atttypid;
+               *vartypmod = att_tup->atttypmod;
+               ReleaseSysCache(tp);
+       }
+       else if (rte->rtekind == RTE_SUBQUERY)
+       {
+               /* Subselect RTE --- get type info from subselect's tlist */
+               List       *tlistitem;
+
+               foreach(tlistitem, rte->subquery->targetList)
+               {
+                       TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+
+                       if (te->resdom->resjunk || te->resdom->resno != attnum)
+                               continue;
+                       *vartype = te->resdom->restype;
+                       *vartypmod = te->resdom->restypmod;
+                       return;
+               }
+               /* falling off end of list shouldn't happen... */
+               elog(ERROR, "Subquery %s does not have attribute %d",
+                        rte->eref->relname, attnum);
+       }
+       else if (rte->rtekind == RTE_JOIN)
+       {
+               /* Join RTE --- get type info directly from join RTE */
+               Assert(attnum > 0 && attnum <= length(rte->joincoltypes));
+               *vartype = (Oid) nthi(attnum-1, rte->joincoltypes);
+               *vartypmod = nthi(attnum-1, rte->joincoltypmods);
+       }
+       else
+               elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
+                        (int) rte->rtekind);
+}
+
 /*
  *     given relation and att name, return id of variable
  *
index bb398a7068f48fbd058d0a7bb580af0b91ac55ae..f5791298f310c6d9197fe032fa6e702b01f9816d 100644 (file)
@@ -8,12 +8,13 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.76 2001/11/05 17:46:26 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.77 2002/03/12 00:51:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
+
 #include "nodes/makefuncs.h"
 #include "parser/parsetree.h"
 #include "parser/parse_coerce.h"
@@ -118,30 +119,16 @@ transformTargetList(ParseState *pstate, List *targetlist)
                                 * Target item is relation.*, expand that table (eg.
                                 * SELECT emp.*, dname FROM emp, dept)
                                 */
-                               Node       *rteorjoin;
+                               RangeTblEntry *rte;
                                int                     sublevels_up;
 
-                               rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname,
-                                                                                                       &sublevels_up);
-
-                               if (rteorjoin == NULL)
-                               {
-                                       rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
-                                       sublevels_up = 0;
-                               }
+                               rte = refnameRangeTblEntry(pstate, att->relname,
+                                                                                  &sublevels_up);
+                               if (rte == NULL)
+                                       rte = addImplicitRTE(pstate, att->relname);
 
-                               if (IsA(rteorjoin, RangeTblEntry))
-                                       p_target = nconc(p_target,
-                                                                        expandRelAttrs(pstate,
-                                                                                  (RangeTblEntry *) rteorjoin));
-                               else if (IsA(rteorjoin, JoinExpr))
-                                       p_target = nconc(p_target,
-                                                                        expandJoinAttrs(pstate,
-                                                                                                 (JoinExpr *) rteorjoin,
-                                                                                                        sublevels_up));
-                               else
-                                       elog(ERROR, "transformTargetList: unexpected node type %d",
-                                                nodeTag(rteorjoin));
+                               p_target = nconc(p_target,
+                                                                expandRelAttrs(pstate, rte));
                        }
                        else
                        {
@@ -405,34 +392,29 @@ ExpandAllTables(ParseState *pstate)
        foreach(ns, pstate->p_namespace)
        {
                Node       *n = (Node *) lfirst(ns);
+               RangeTblEntry *rte;
 
                if (IsA(n, RangeTblRef))
-               {
-                       RangeTblEntry *rte;
-
                        rte = rt_fetch(((RangeTblRef *) n)->rtindex,
                                                   pstate->p_rtable);
-
-                       /*
-                        * Ignore added-on relations that were not listed in the FROM
-                        * clause.
-                        */
-                       if (!rte->inFromCl)
-                               continue;
-
-                       target = nconc(target, expandRelAttrs(pstate, rte));
-               }
                else if (IsA(n, JoinExpr))
-               {
-                       /* A newfangled join expression */
-                       JoinExpr   *j = (JoinExpr *) n;
-
-                       /* Currently, a join expr could only have come from FROM. */
-                       target = nconc(target, expandJoinAttrs(pstate, j, 0));
-               }
+                       rte = rt_fetch(((JoinExpr *) n)->rtindex,
+                                                  pstate->p_rtable);
                else
+               {
                        elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
                                 "\n\t%s", nodeToString(n));
+                       rte = NULL;                     /* keep compiler quiet */
+               }
+
+               /*
+                * Ignore added-on relations that were not listed in the FROM
+                * clause.
+                */
+               if (!rte->inFromCl)
+                       continue;
+
+               target = nconc(target, expandRelAttrs(pstate, rte));
        }
 
        /* Check for SELECT *; */
index f3e2306a00bb7d96e965af29ed17a65037e08869..e118654e4040c8933df27d57ff7af1600ba189a2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.98 2001/10/25 05:49:41 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.99 2002/03/12 00:51:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -289,6 +289,7 @@ ApplyRetrieveRule(Query *parsetree,
         */
        rte = rt_fetch(rt_index, parsetree->rtable);
 
+       rte->rtekind = RTE_SUBQUERY;
        rte->relname = NULL;
        rte->relid = InvalidOid;
        rte->subquery = rule_action;
@@ -354,17 +355,17 @@ markQueryForUpdate(Query *qry, bool skipOldNew)
                        (rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
                        continue;
 
-               if (rte->subquery)
-               {
-                       /* FOR UPDATE of subquery is propagated to subquery's rels */
-                       markQueryForUpdate(rte->subquery, false);
-               }
-               else
+               if (rte->rtekind == RTE_RELATION)
                {
                        if (!intMember(rti, qry->rowMarks))
                                qry->rowMarks = lappendi(qry->rowMarks, rti);
                        rte->checkForWrite = true;
                }
+               else if (rte->rtekind == RTE_SUBQUERY)
+               {
+                       /* FOR UPDATE of subquery is propagated to subquery's rels */
+                       markQueryForUpdate(rte->subquery, false);
+               }
        }
 }
 
@@ -440,12 +441,18 @@ fireRIRrules(Query *parsetree)
                 * to do to this level of the query, but we must recurse into the
                 * subquery to expand any rule references in it.
                 */
-               if (rte->subquery)
+               if (rte->rtekind == RTE_SUBQUERY)
                {
                        rte->subquery = fireRIRrules(rte->subquery);
                        continue;
                }
 
+               /*
+                * Joins and other non-relation RTEs can be ignored completely.
+                */
+               if (rte->rtekind != RTE_RELATION)
+                       continue;
+
                /*
                 * If the table is not referenced in the query, then we ignore it.
                 * This prevents infinite expansion loop due to new rtable entries
@@ -756,6 +763,7 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
        result_relation = parsetree->resultRelation;
        Assert(result_relation != 0);
        rt_entry = rt_fetch(result_relation, parsetree->rtable);
+       Assert(rt_entry->rtekind == RTE_RELATION);
 
        /*
         * This may well be the first access to the result relation during the
@@ -945,7 +953,7 @@ QueryRewrite(Query *parsetree)
                        RangeTblEntry *rte = rt_fetch(query->resultRelation,
                                                                                  query->rtable);
 
-                       if (rte->subquery)
+                       if (rte->rtekind == RTE_SUBQUERY)
                        {
                                switch (query->commandType)
                                {
index 49f67c91c7bcfea105016861217ee91fed09870d..af413cab938d701500e1b496707800738adbab81 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.61 2001/11/05 17:46:27 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.62 2002/03/12 00:51:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,8 +101,8 @@ checkExprHasSubLink_walker(Node *node, void *context)
  *
  * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
  * and increment their varno fields (rangetable indexes) by 'offset'.
- * The varnoold fields are adjusted similarly. Also, RangeTblRef nodes
- * in join trees and setOp trees are adjusted.
+ * The varnoold fields are adjusted similarly. Also, RangeTblRef and
+ * JoinExpr nodes in join trees and setOp trees are adjusted.
  *
  * NOTE: although this has the form of a walker, we cheat and modify the
  * nodes in-place.     The given expression tree should have been copied
@@ -140,6 +140,14 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
                /* the subquery itself is visited separately */
                return false;
        }
+       if (IsA(node, JoinExpr))
+       {
+               JoinExpr *j = (JoinExpr *) node;
+
+               if (context->sublevels_up == 0)
+                       j->rtindex += context->offset;
+               /* fall through to examine children */
+       }
        if (IsA(node, Query))
        {
                /* Recurse into subselects */
@@ -200,7 +208,7 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
  * Find all Var nodes in the given tree belonging to a specific relation
  * (identified by sublevels_up and rt_index), and change their varno fields
  * to 'new_index'.     The varnoold fields are changed too.  Also, RangeTblRef
- * nodes in join trees and setOp trees are adjusted.
+ * and JoinExpr nodes in join trees and setOp trees are adjusted.
  *
  * NOTE: although this has the form of a walker, we cheat and modify the
  * nodes in-place.     The given expression tree should have been copied
@@ -241,6 +249,15 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
                /* the subquery itself is visited separately */
                return false;
        }
+       if (IsA(node, JoinExpr))
+       {
+               JoinExpr *j = (JoinExpr *) node;
+
+               if (context->sublevels_up == 0 &&
+                       j->rtindex == context->rt_index)
+                       j->rtindex = context->new_index;
+               /* fall through to examine children */
+       }
        if (IsA(node, Query))
        {
                /* Recurse into subselects */
@@ -410,6 +427,15 @@ rangeTableEntry_used_walker(Node *node,
                /* the subquery itself is visited separately */
                return false;
        }
+       if (IsA(node, JoinExpr))
+       {
+               JoinExpr *j = (JoinExpr *) node;
+
+               if (j->rtindex == context->rt_index &&
+                       context->sublevels_up == 0)
+                       return true;
+               /* fall through to examine children */
+       }
        if (IsA(node, Query))
        {
                /* Recurse into subselects */
index 169bcd23e29be6d605c8b8c635b90aa2d5c5e473..d47bf3bb945c9f658c7e4ff0022ffade1b4abd41 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.92 2002/03/06 19:58:26 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.93 2002/03/12 00:51:59 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -73,17 +73,22 @@ typedef struct
 
 /*
  * Each level of query context around a subtree needs a level of Var namespace.
- * The rangetable is the list of actual RTEs, and the namespace indicates
- * which parts of the rangetable are accessible (and under what aliases)
- * in the expression currently being looked at.  A Var having varlevelsup=N
- * refers to the N'th item (counting from 0) in the current context's
- * namespaces list.
+ * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
+ * the current context's namespaces list.
+ *
+ * The rangetable is the list of actual RTEs from the query tree.
+ *
+ * For deparsing plan trees, we allow two special RTE entries that are not
+ * part of the rtable list (mainly because they don't have consecutively
+ * allocated varnos).
  */
 typedef struct
 {
        List       *rtable;                     /* List of RangeTblEntry nodes */
-       List       *namespace;          /* List of joinlist items (RangeTblRef and
-                                                                * JoinExpr nodes) */
+       int                     outer_varno;    /* varno for outer_rte */
+       RangeTblEntry *outer_rte;       /* special RangeTblEntry, or NULL */
+       int                     inner_varno;    /* varno for inner_rte */
+       RangeTblEntry *inner_rte;       /* special RangeTblEntry, or NULL */
 } deparse_namespace;
 
 
@@ -122,12 +127,6 @@ static void get_rule_sortgroupclause(SortClause *srt, List *tlist,
                                                 deparse_context *context);
 static void get_names_for_var(Var *var, deparse_context *context,
                                  char **refname, char **attname);
-static bool get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
-                                  char **refname, char **attname);
-static bool find_alias_in_namespace(Node *nsnode, Node *expr,
-                                               List *rangetable, int levelsup,
-                                               char **refname, char **attname);
-static bool phony_equal(Node *expr1, Node *expr2, int levelsup);
 static void get_rule_expr(Node *node, deparse_context *context);
 static void get_func_expr(Expr *expr, deparse_context *context);
 static Node *strip_type_coercion(Node *expr, Oid resultType);
@@ -644,7 +643,7 @@ deparse_expression(Node *expr, List *dpcontext, bool forceprefix)
  *
  * Given the name and OID of a relation, build deparsing context for an
  * expression referencing only that relation (as varno 1, varlevelsup 0).
- * This is presently sufficient for the external uses of deparse_expression.
+ * This is sufficient for many uses of deparse_expression.
  * ----------
  */
 List *
@@ -652,30 +651,119 @@ deparse_context_for(char *relname, Oid relid)
 {
        deparse_namespace *dpns;
        RangeTblEntry *rte;
-       RangeTblRef *rtr;
 
        dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
 
        /* Build a minimal RTE for the rel */
        rte = makeNode(RangeTblEntry);
+       rte->rtekind = RTE_RELATION;
        rte->relname = relname;
        rte->relid = relid;
        rte->eref = makeNode(Attr);
        rte->eref->relname = relname;
        rte->inh = false;
        rte->inFromCl = true;
+
        /* Build one-element rtable */
        dpns->rtable = makeList1(rte);
+       dpns->outer_varno = dpns->inner_varno = 0;
+       dpns->outer_rte = dpns->inner_rte = NULL;
 
-       /* Build a namespace list referencing this RTE only */
-       rtr = makeNode(RangeTblRef);
-       rtr->rtindex = 1;
-       dpns->namespace = makeList1(rtr);
+       /* Return a one-deep namespace stack */
+       return makeList1(dpns);
+}
+
+/*
+ * deparse_context_for_plan            - Build deparse context for a plan node
+ *
+ * We assume we are dealing with an upper-level plan node having either
+ * one or two referenceable children (pass innercontext = NULL if only one).
+ * The passed-in Nodes should be made using deparse_context_for_subplan.
+ * The resulting context will work for deparsing quals, tlists, etc of the
+ * plan node.
+ */
+List *
+deparse_context_for_plan(int outer_varno, Node *outercontext,
+                                                int inner_varno, Node *innercontext)
+{
+       deparse_namespace *dpns;
+
+       dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+
+       dpns->rtable = NIL;
+       dpns->outer_varno = outer_varno;
+       dpns->outer_rte = (RangeTblEntry *) outercontext;
+       dpns->inner_varno = inner_varno;
+       dpns->inner_rte = (RangeTblEntry *) innercontext;
 
        /* Return a one-deep namespace stack */
        return makeList1(dpns);
 }
 
+/*
+ * deparse_context_for_subplan - Build deparse context for a plan node
+ *
+ * Helper routine to build one of the inputs for deparse_context_for_plan.
+ * Pass the tlist of the subplan node, plus the query rangetable.
+ *
+ * The returned node is actually a RangeTblEntry, but we declare it as just
+ * Node to discourage callers from assuming anything.
+ */
+Node *
+deparse_context_for_subplan(const char *name, List *tlist,
+                                                       List *rtable)
+{
+       RangeTblEntry *rte = makeNode(RangeTblEntry);
+       List       *attrs = NIL;
+       int                     nattrs = 0;
+       int                     rtablelength = length(rtable);
+       List       *tl;
+       char            buf[32];
+
+       foreach(tl, tlist)
+       {
+               TargetEntry *tle = lfirst(tl);
+               Resdom *resdom = tle->resdom;
+
+               nattrs++;
+               Assert(resdom->resno == nattrs);
+               if (resdom->resname)
+               {
+                       attrs = lappend(attrs, makeString(resdom->resname));
+                       continue;
+               }
+               if (tle->expr && IsA(tle->expr, Var))
+               {
+                       Var        *var = (Var *) tle->expr;
+
+                       /* varno/varattno won't be any good, but varnoold might be */
+                       if (var->varnoold > 0 && var->varnoold <= rtablelength)
+                       {
+                               RangeTblEntry *varrte = rt_fetch(var->varnoold, rtable);
+                               char *varname;
+
+                               varname = get_rte_attribute_name(varrte, var->varoattno);
+                               attrs = lappend(attrs, makeString(varname));
+                               continue;
+                       }
+               }
+               /* Fallback if can't get name */
+               snprintf(buf, sizeof(buf), "?column%d?", resdom->resno);
+               attrs = lappend(attrs, makeString(pstrdup(buf)));
+       }
+
+       rte->rtekind = RTE_SPECIAL;     /* XXX */
+       rte->relname = pstrdup(name);
+       rte->relid = InvalidOid;
+       rte->eref = makeNode(Attr);
+       rte->eref->relname = rte->relname;
+       rte->eref->attrs = attrs;
+       rte->inh = false;
+       rte->inFromCl = true;
+
+       return (Node *) rte;
+}
+
 /* ----------
  * make_ruledef                        - reconstruct the CREATE RULE command
  *                               for a given pg_rewrite tuple
@@ -789,7 +877,8 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
                context.namespaces = makeList1(&dpns);
                context.varprefix = (length(query->rtable) != 1);
                dpns.rtable = query->rtable;
-               dpns.namespace = query->jointree ? query->jointree->fromlist : NIL;
+               dpns.outer_varno = dpns.inner_varno = 0;
+               dpns.outer_rte = dpns.inner_rte = NULL;
 
                get_rule_expr(qual, &context);
        }
@@ -912,7 +1001,8 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace)
        context.varprefix = (parentnamespace != NIL ||
                                                 length(query->rtable) != 1);
        dpns.rtable = query->rtable;
-       dpns.namespace = query->jointree ? query->jointree->fromlist : NIL;
+       dpns.outer_varno = dpns.inner_varno = 0;
+       dpns.outer_rte = dpns.inner_rte = NULL;
 
        switch (query->commandType)
        {
@@ -1382,12 +1472,11 @@ get_utility_query_def(Query *query, deparse_context *context)
 /*
  * Get the relation refname and attname for a (possibly nonlocal) Var.
  *
+ * refname will be returned as NULL if the Var references an unnamed join.
+ * In this case the Var *must* be displayed without any qualification.
+ *
  * attname will be returned as NULL if the Var represents a whole tuple
  * of the relation.
- *
- * This is trickier than it ought to be because of the possibility of aliases
- * and limited scope of refnames.  We have to try to return the correct alias
- * with respect to the current namespace given by the context.
  */
 static void
 get_names_for_var(Var *var, deparse_context *context,
@@ -1406,262 +1495,31 @@ get_names_for_var(Var *var, deparse_context *context,
                         var->varlevelsup);
        dpns = (deparse_namespace *) lfirst(nslist);
 
-       /* Scan namespace to see if we can find an alias for the var */
-       if (find_alias_in_namespace((Node *) dpns->namespace, (Node *) var,
-                                                               dpns->rtable, var->varlevelsup,
-                                                               refname, attname))
-               return;
+       /* Find the relevant RTE */
+       if (var->varno >= 1 && var->varno <= length(dpns->rtable))
+               rte = rt_fetch(var->varno, dpns->rtable);
+       else if (var->varno == dpns->outer_varno)
+               rte = dpns->outer_rte;
+       else if (var->varno == dpns->inner_varno)
+               rte = dpns->inner_rte;
+       else
+               rte = NULL;
+       if (rte == NULL)
+               elog(ERROR, "get_names_for_var: bogus varno %d",
+                        var->varno);
+
+       /* Emit results */
+       if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
+               *refname = NULL;
+       else
+               *refname = rte->eref->relname;
 
-       /*
-        * Otherwise, fall back on the rangetable entry.  This should happen
-        * only for uses of special RTEs like *NEW* and *OLD*, which won't get
-        * placed in our namespace.
-        */
-       rte = rt_fetch(var->varno, dpns->rtable);
-       *refname = rte->eref->relname;
        if (var->varattno == InvalidAttrNumber)
                *attname = NULL;
        else
                *attname = get_rte_attribute_name(rte, var->varattno);
 }
 
-/*
- * Check to see if a CASE expression matches a FULL JOIN's output expression.
- * If so, return the refname and alias it should be expressed as.
- */
-static bool
-get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
-                                  char **refname, char **attname)
-{
-       List       *nslist;
-       int                     sup;
-
-       /*
-        * This could be done more efficiently if we first groveled through
-        * the CASE to find varlevelsup values, but it's probably not worth
-        * the trouble.  All this code will go away someday anyway ...
-        */
-
-       sup = 0;
-       foreach(nslist, context->namespaces)
-       {
-               deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
-
-               if (find_alias_in_namespace((Node *) dpns->namespace,
-                                                                       (Node *) caseexpr,
-                                                                       dpns->rtable, sup,
-                                                                       refname, attname))
-                       return true;
-               sup++;
-       }
-       return false;
-}
-
-/*
- * Recursively scan a namespace (same representation as a jointree) to see
- * if we can find an alias for the given expression.  If so, return the
- * correct alias refname and attname.  The expression may be either a plain
- * Var or a CASE expression (which may be a FULL JOIN reference).
- */
-static bool
-find_alias_in_namespace(Node *nsnode, Node *expr,
-                                               List *rangetable, int levelsup,
-                                               char **refname, char **attname)
-{
-       if (nsnode == NULL)
-               return false;
-       if (IsA(nsnode, RangeTblRef))
-       {
-               if (IsA(expr, Var))
-               {
-                       Var                *var = (Var *) expr;
-                       int                     rtindex = ((RangeTblRef *) nsnode)->rtindex;
-
-                       if (var->varno == rtindex && var->varlevelsup == levelsup)
-                       {
-                               RangeTblEntry *rte = rt_fetch(rtindex, rangetable);
-
-                               *refname = rte->eref->relname;
-                               if (var->varattno == InvalidAttrNumber)
-                                       *attname = NULL;
-                               else
-                                       *attname = get_rte_attribute_name(rte, var->varattno);
-                               return true;
-                       }
-               }
-       }
-       else if (IsA(nsnode, JoinExpr))
-       {
-               JoinExpr   *j = (JoinExpr *) nsnode;
-
-               if (j->alias)
-               {
-                       List       *vlist;
-                       List       *nlist;
-
-                       /*
-                        * Does the expr match any of the output columns of the join?
-                        *
-                        * We can't just use equal() here, because the given expr may
-                        * have nonzero levelsup, whereas the saved expression in the
-                        * JoinExpr should have zero levelsup.
-                        */
-                       nlist = j->colnames;
-                       foreach(vlist, j->colvars)
-                       {
-                               if (phony_equal(lfirst(vlist), expr, levelsup))
-                               {
-                                       *refname = j->alias->relname;
-                                       *attname = strVal(lfirst(nlist));
-                                       return true;
-                               }
-                               nlist = lnext(nlist);
-                       }
-
-                       /*
-                        * Tables within an aliased join are invisible from outside
-                        * the join, according to the scope rules of SQL92 (the join
-                        * is considered a subquery).  So, stop here.
-                        */
-                       return false;
-               }
-               if (find_alias_in_namespace(j->larg, expr,
-                                                                       rangetable, levelsup,
-                                                                       refname, attname))
-                       return true;
-               if (find_alias_in_namespace(j->rarg, expr,
-                                                                       rangetable, levelsup,
-                                                                       refname, attname))
-                       return true;
-       }
-       else if (IsA(nsnode, List))
-       {
-               List       *l;
-
-               foreach(l, (List *) nsnode)
-               {
-                       if (find_alias_in_namespace(lfirst(l), expr,
-                                                                               rangetable, levelsup,
-                                                                               refname, attname))
-                               return true;
-               }
-       }
-       else
-               elog(ERROR, "find_alias_in_namespace: unexpected node type %d",
-                        nodeTag(nsnode));
-       return false;
-}
-
-/*
- * Check for equality of two expressions, with the proviso that all Vars in
- * expr1 should have varlevelsup = 0, while all Vars in expr2 should have
- * varlevelsup = levelsup.
- *
- * In reality we only need to support equality checks on Vars and the type
- * of CASE expression that is used for FULL JOIN outputs, so not all node
- * types need be handled here.
- *
- * Otherwise, this code is a straight ripoff from equalfuncs.c.
- */
-static bool
-phony_equal(Node *expr1, Node *expr2, int levelsup)
-{
-       if (expr1 == NULL || expr2 == NULL)
-               return (expr1 == expr2);
-       if (nodeTag(expr1) != nodeTag(expr2))
-               return false;
-       if (IsA(expr1, Var))
-       {
-               Var                *a = (Var *) expr1;
-               Var                *b = (Var *) expr2;
-
-               if (a->varno != b->varno)
-                       return false;
-               if (a->varattno != b->varattno)
-                       return false;
-               if (a->vartype != b->vartype)
-                       return false;
-               if (a->vartypmod != b->vartypmod)
-                       return false;
-               if (a->varlevelsup != 0 || b->varlevelsup != levelsup)
-                       return false;
-               if (a->varnoold != b->varnoold)
-                       return false;
-               if (a->varoattno != b->varoattno)
-                       return false;
-               return true;
-       }
-       if (IsA(expr1, CaseExpr))
-       {
-               CaseExpr   *a = (CaseExpr *) expr1;
-               CaseExpr   *b = (CaseExpr *) expr2;
-
-               if (a->casetype != b->casetype)
-                       return false;
-               if (!phony_equal(a->arg, b->arg, levelsup))
-                       return false;
-               if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
-                       return false;
-               if (!phony_equal(a->defresult, b->defresult, levelsup))
-                       return false;
-               return true;
-       }
-       if (IsA(expr1, CaseWhen))
-       {
-               CaseWhen   *a = (CaseWhen *) expr1;
-               CaseWhen   *b = (CaseWhen *) expr2;
-
-               if (!phony_equal(a->expr, b->expr, levelsup))
-                       return false;
-               if (!phony_equal(a->result, b->result, levelsup))
-                       return false;
-               return true;
-       }
-       if (IsA(expr1, Expr))
-       {
-               Expr       *a = (Expr *) expr1;
-               Expr       *b = (Expr *) expr2;
-
-               if (a->opType != b->opType)
-                       return false;
-               if (!phony_equal(a->oper, b->oper, levelsup))
-                       return false;
-               if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
-                       return false;
-               return true;
-       }
-       if (IsA(expr1, Func))
-       {
-               Func       *a = (Func *) expr1;
-               Func       *b = (Func *) expr2;
-
-               if (a->funcid != b->funcid)
-                       return false;
-               if (a->functype != b->functype)
-                       return false;
-               return true;
-       }
-       if (IsA(expr1, List))
-       {
-               List       *la = (List *) expr1;
-               List       *lb = (List *) expr2;
-               List       *l;
-
-               if (length(la) != length(lb))
-                       return false;
-               foreach(l, la)
-               {
-                       if (!phony_equal(lfirst(l), lfirst(lb), levelsup))
-                               return false;
-                       lb = lnext(lb);
-               }
-               return true;
-       }
-       /* If we get here, there was something weird in a JOIN's colvars list */
-       elog(ERROR, "phony_equal: unexpected node type %d", nodeTag(expr1));
-       return false;
-}
-
 /* ----------
  * get_rule_expr                       - Parse back an expression
  * ----------
@@ -1695,7 +1553,7 @@ get_rule_expr(Node *node, deparse_context *context)
                                char       *attname;
 
                                get_names_for_var(var, context, &refname, &attname);
-                               if (context->varprefix || attname == NULL)
+                               if (refname && (context->varprefix || attname == NULL))
                                {
                                        if (strcmp(refname, "*NEW*") == 0)
                                                appendStringInfo(buf, "new");
@@ -1767,6 +1625,10 @@ get_rule_expr(Node *node, deparse_context *context)
                                                appendStringInfoChar(buf, ')');
                                                break;
 
+                                       case FUNC_EXPR:
+                                               get_func_expr((Expr *) node, context);
+                                               break;
+
                                        case OR_EXPR:
                                                appendStringInfoChar(buf, '(');
                                                get_rule_expr((Node *) lfirst(args), context);
@@ -1795,8 +1657,13 @@ get_rule_expr(Node *node, deparse_context *context)
                                                appendStringInfoChar(buf, ')');
                                                break;
 
-                                       case FUNC_EXPR:
-                                               get_func_expr((Expr *) node, context);
+                                       case SUBPLAN_EXPR:
+                                               /*
+                                                * We cannot see an already-planned subplan in rule
+                                                * deparsing, only while EXPLAINing a query plan.
+                                                * For now, just punt.
+                                                */
+                                               appendStringInfo(buf, "(subplan)");
                                                break;
 
                                        default:
@@ -1927,19 +1794,6 @@ get_rule_expr(Node *node, deparse_context *context)
                        {
                                CaseExpr   *caseexpr = (CaseExpr *) node;
                                List       *temp;
-                               char       *refname;
-                               char       *attname;
-
-                               /* Hack for providing aliases for FULL JOIN outputs */
-                               if (get_alias_for_case(caseexpr, context,
-                                                                          &refname, &attname))
-                               {
-                                       if (context->varprefix)
-                                               appendStringInfo(buf, "%s.",
-                                                                                quote_identifier(refname));
-                                       appendStringInfo(buf, "%s", quote_identifier(attname));
-                                       break;
-                               }
 
                                appendStringInfo(buf, "CASE");
                                foreach(temp, caseexpr->args)
@@ -2015,6 +1869,28 @@ get_rule_expr(Node *node, deparse_context *context)
                        get_sublink_expr(node, context);
                        break;
 
+               case T_Param:
+                       {
+                               Param      *param = (Param *) node;
+
+                               switch (param->paramkind)
+                               {
+                                       case PARAM_NAMED:
+                                       case PARAM_NEW:
+                                       case PARAM_OLD:
+                                               appendStringInfo(buf, "$%s", param->paramname);
+                                               break;
+                                       case PARAM_NUM:
+                                       case PARAM_EXEC:
+                                               appendStringInfo(buf, "$%d", param->paramid);
+                                               break;
+                                       default:
+                                               appendStringInfo(buf, "(param)");
+                                               break;
+                               }
+                       }
+                       break;
+
                default:
                        printf("\n%s\n", nodeToString(node));
                        elog(ERROR, "get_ruledef of %s: unknown node type %d in get_rule_expr()",
@@ -2442,16 +2318,6 @@ static void
 get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
-       deparse_namespace *dpns;
-       List       *sv_namespace;
-
-       /*
-        * FROM-clause items have limited visibility of query's namespace.
-        * Save and restore the outer namespace setting while we munge it.
-        */
-       dpns = (deparse_namespace *) lfirst(context->namespaces);
-       sv_namespace = dpns->namespace;
-       dpns->namespace = NIL;
 
        if (IsA(jtnode, RangeTblRef))
        {
@@ -2544,7 +2410,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                        }
                        else if (j->quals)
                        {
-                               dpns->namespace = makeList2(j->larg, j->rarg);
                                appendStringInfo(buf, " ON (");
                                get_rule_expr(j->quals, context);
                                appendStringInfoChar(buf, ')');
@@ -2575,8 +2440,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
        else
                elog(ERROR, "get_from_clause_item: unexpected node type %d",
                         nodeTag(jtnode));
-
-       dpns->namespace = sv_namespace;
 }
 
 /* ----------
index f579d0267c3bef555a69f367c60e1620cce39cb4..882a29246fe45b207a3276b0d0f72c4b0cf7a828 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.105 2002/03/01 22:45:16 petere Exp $
+ * $Id: catversion.h,v 1.106 2002/03/12 00:51:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200203011
+#define CATALOG_VERSION_NO     200203111
 
 #endif
index 996c2b9c738942fd57cc3495fc62fe64f4747147..9ae9feb739dfbdff1b78dbf21e3cf6b95900dfa6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.159 2002/03/08 04:37:18 tgl Exp $
+ * $Id: parsenodes.h,v 1.160 2002/03/12 00:52:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -430,9 +430,12 @@ typedef struct TargetEntry
  * RangeTblEntry -
  *       A range table is a List of RangeTblEntry nodes.
  *
- *       Currently we use the same node type for both plain relation references
- *       and sub-selects in the FROM clause.  It might be cleaner to abstract
- *       the common fields into a "superclass" nodetype.
+ *       A range table entry may represent a plain relation, a sub-select in
+ *       FROM, or the result of a JOIN clause.  (Only explicit JOIN syntax
+ *       produces an RTE, not the implicit join resulting from multiple FROM
+ *       items.  This is because we only need the RTE to deal with SQL features
+ *       like outer joins and join-output-column aliasing.)  Other special
+ *       RTE types also exist, as indicated by RTEKind.
  *
  *       alias is an Attr node representing the AS alias-clause attached to the
  *       FROM expression, or NULL if no clause.
@@ -445,7 +448,7 @@ typedef struct TargetEntry
  *
  *       inh is TRUE for relation references that should be expanded to include
  *       inheritance children, if the rel has any.  This *must* be FALSE for
- *       subquery RTEs.
+ *       RTEs other than RTE_RELATION entries.
  *
  *       inFromCl marks those range variables that are listed in the FROM clause.
  *       In SQL, the query can only refer to range variables listed in the
@@ -465,12 +468,28 @@ typedef struct TargetEntry
  *       (This allows rules to act as setuid gateways.)
  *--------------------
  */
+typedef enum RTEKind
+{
+       RTE_RELATION,                           /* ordinary relation reference */
+       RTE_SUBQUERY,                           /* subquery in FROM */
+       RTE_JOIN,                                       /* join */
+       RTE_SPECIAL                                     /* special rule relation (NEW or OLD) */
+} RTEKind;
+
 typedef struct RangeTblEntry
 {
        NodeTag         type;
 
+       RTEKind         rtekind;                /* see above */
+
        /*
-        * Fields valid for a plain relation RTE (else NULL/zero):
+        * XXX the fields applicable to only some rte kinds should be merged
+        * into a union.  I didn't do this yet because the diffs would impact
+        * a lot of code that is being actively worked on.  FIXME later.
+        */
+
+       /*
+        * Fields valid for a plain relation or inh_relation RTE (else NULL/zero):
         */
        char       *relname;            /* real name of the relation */
        Oid                     relid;                  /* OID of the relation */
@@ -480,6 +499,21 @@ typedef struct RangeTblEntry
         */
        Query      *subquery;           /* the sub-query */
 
+       /*
+        * Fields valid for a join RTE (else NULL):
+        *
+        * joincoltypes/joincoltypmods identify the column datatypes of the
+        * join result.  joinleftcols and joinrightcols identify the source
+        * columns from the join's inputs: each entry is either a source column
+        * AttrNumber or zero.  For normal columns exactly one is nonzero,
+        * but both are nonzero for a column "merged" by USING or NATURAL.
+        */
+       JoinType        jointype;               /* type of join */
+       List       *joincoltypes;       /* integer list of column type OIDs */
+       List       *joincoltypmods;     /* integer list of column typmods */
+       List       *joinleftcols;       /* integer list of left-side column #s */
+       List       *joinrightcols;      /* integer list of right-side column #s */
+
        /*
         * Fields valid in all RTEs:
         */
index 111f87c24d360ee25c6e9ec04fda9d81fa62a9cf..66db9cf36404ca4a1f4585c3d1ea3c734e6f493d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.53 2001/11/05 17:46:34 momjian Exp $
+ * $Id: plannodes.h,v 1.54 2002/03/12 00:52:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -254,6 +254,7 @@ typedef struct SubqueryScan
  * jointype:   rule for joining tuples from left and right subtrees
  * joinqual:   qual conditions that came from JOIN/ON or JOIN/USING
  *                             (plan.qual contains conditions that came from WHERE)
+ * joinrti:            rtable index of corresponding JOIN RTE, if any (0 if none)
  *
  * When jointype is INNER, joinqual and plan.qual are semantically
  * interchangeable.  For OUTER jointypes, the two are *not* interchangeable;
@@ -262,6 +263,8 @@ typedef struct SubqueryScan
  * (But plan.qual is still applied before actually returning a tuple.)
  * For an outer join, only joinquals are allowed to be used as the merge
  * or hash condition of a merge or hash join.
+ *
+ * joinrti is for the convenience of setrefs.c; it's not used in execution.
  * ----------------
  */
 typedef struct Join
@@ -269,6 +272,7 @@ typedef struct Join
        Plan            plan;
        JoinType        jointype;
        List       *joinqual;           /* JOIN quals (in addition to plan.qual) */
+       Index           joinrti;                /* JOIN RTE, if any */
 } Join;
 
 /* ----------------
index c2aabaffe6aa2648605c0cc685a6c46c5193712d..43e4d5a41e20673cc5ee9c4a7705ab060708abf6 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.57 2001/11/05 17:46:34 momjian Exp $
+ * $Id: primnodes.h,v 1.58 2002/03/12 00:52:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -513,10 +513,9 @@ typedef struct RangeTblRef
  * alias has a critical impact on semantics, because a join with an alias
  * restricts visibility of the tables/columns inside it.
  *
- * During parse analysis, colnames is filled with a list of String nodes
- * giving the column names (real or alias) of the output of the join,
- * and colvars is filled with a list of expressions that can be copied to
- * reference the output columns.
+ * During parse analysis, an RTE is created for the Join, and its index
+ * is filled into rtindex.  This RTE is present mainly so that Vars can
+ * be created that refer to the outputs of the join.
  *----------
  */
 typedef struct JoinExpr
@@ -529,9 +528,7 @@ typedef struct JoinExpr
        List       *using;                      /* USING clause, if any (list of String) */
        Node       *quals;                      /* qualifiers on join, if any */
        struct Attr *alias;                     /* user-written alias clause, if any */
-       List       *colnames;           /* output column names (list of String) */
-       List       *colvars;            /* output column nodes (list of
-                                                                * expressions) */
+       int                     rtindex;                /* RT index assigned for join */
 } JoinExpr;
 
 /*----------
index e14f1ea4e83d672278b59b05f9634bc07e0d3f45..d26d60c71be1b20b491ba443708474e070c6d6d5 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.62 2002/03/01 06:01:20 tgl Exp $
+ * $Id: relation.h,v 1.63 2002/03/12 00:52:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,18 +39,39 @@ typedef enum CostSelector
  * RelOptInfo
  *             Per-relation information for planning/optimization
  *
- *             For planning purposes, a "base rel" is either a plain relation (a
- *             table) or the output of a sub-SELECT that appears in the range table.
- *             In either case it is uniquely identified by an RT index.  A "joinrel"
- *             is the joining of two or more base rels.  A joinrel is identified by
- *             the set of RT indexes for its component baserels.
- *
- *             Note that there is only one joinrel for any given set of component
- *             baserels, no matter what order we assemble them in; so an unordered
- *             set is the right datatype to identify it with.
- *
- *             Parts of this data structure are specific to various scan and join
- *             mechanisms.  It didn't seem worth creating new node types for them.
+ * For planning purposes, a "base rel" is either a plain relation (a table)
+ * or the output of a sub-SELECT that appears in the range table.
+ * In either case it is uniquely identified by an RT index.  A "joinrel"
+ * is the joining of two or more base rels.  A joinrel is identified by
+ * the set of RT indexes for its component baserels.  We create RelOptInfo
+ * nodes for each baserel and joinrel, and store them in the Query's
+ * base_rel_list and join_rel_list respectively.
+ *
+ * Note that there is only one joinrel for any given set of component
+ * baserels, no matter what order we assemble them in; so an unordered
+ * set is the right datatype to identify it with.
+ *
+ * We also have "other rels", which are like base rels in that they refer to
+ * single RT indexes; but they are not part of the join tree, and are stored
+ * in other_rel_list not base_rel_list.  An otherrel is created for each
+ * join RTE as an aid in processing Vars that refer to the join's outputs,
+ * but it serves no other purpose in planning.  It is important not to
+ * confuse this otherrel with the joinrel that represents the matching set
+ * of base relations.
+ *
+ * A second category of otherrels are those made for child relations of an
+ * inheritance scan (SELECT FROM foo*).  The parent table's RTE and
+ * corresponding baserel represent the whole result of the inheritance scan.
+ * The planner creates separate RTEs and associated RelOptInfos for each child
+ * table (including the parent table, in its capacity as a member of the
+ * inheritance set).  These RelOptInfos are physically identical to baserels,
+ * but are otherrels because they are not in the main join tree.  These added
+ * RTEs and otherrels are used to plan the scans of the individual tables in
+ * the inheritance set; then the parent baserel is given an Append plan
+ * comprising the best plans for the individual child tables.
+ *
+ * Parts of this data structure are specific to various scan and join
+ * mechanisms.  It didn't seem worth creating new node types for them.
  *
  *             relids - List of base-relation identifiers; it is a base relation
  *                             if there is just one, a join relation if more than one
@@ -69,7 +90,7 @@ typedef enum CostSelector
  *             pruneable - flag to let the planner know whether it can prune the
  *                                     pathlist of this RelOptInfo or not.
  *
- *      * If the relation is a base relation it will have these fields set:
+ * If the relation is a base relation it will have these fields set:
  *
  *             issubquery - true if baserel is a subquery RTE rather than a table
  *             indexlist - list of IndexOptInfo nodes for relation's indexes
@@ -82,25 +103,30 @@ typedef enum CostSelector
  *             upon creation of the RelOptInfo object; they are filled in when
  *             set_base_rel_pathlist processes the object.
  *
- *             Note: if a base relation is the root of an inheritance tree
- *             (SELECT FROM foo*) it is still considered a base rel.  We will
- *             generate a list of candidate Paths for accessing that table itself,
- *             and also generate baserel RelOptInfo nodes for each child table,
- *             with their own candidate Path lists.  Then, an AppendPath is built
- *             from the cheapest Path for each of these tables, and set to be the
- *             only available Path for the inheritance baserel.
+ *             For otherrels that are inheritance children, these fields are filled
+ *             in just as for a baserel.  In otherrels for join RTEs, these fields
+ *             are empty --- the only useful field of a join otherrel is its
+ *             outerjoinset.
+ *
+ * If the relation is a join relation it will have these fields set:
  *
- *      * The presence of the remaining fields depends on the restrictions
- *             and joins that the relation participates in:
+ *             joinrti - RT index of corresponding JOIN RTE, if any; 0 if none
+ *             joinrteids - List of RT indexes of JOIN RTEs included in this join
+ *                                      (including joinrti)
+ *
+ * The presence of the remaining fields depends on the restrictions
+ * and joins that the relation participates in:
  *
  *             baserestrictinfo - List of RestrictInfo nodes, containing info about
  *                                     each qualification clause in which this relation
  *                                     participates (only used for base rels)
  *             baserestrictcost - Estimated cost of evaluating the baserestrictinfo
  *                                     clauses at a single tuple (only used for base rels)
- *             outerjoinset - If the rel appears within the nullable side of an outer
- *                                     join, the list of all relids participating in the highest
- *                                     such outer join; else NIL (only used for base rels)
+ *             outerjoinset - For a base rel: if the rel appears within the nullable
+ *                                     side of an outer join, the list of all relids
+ *                                     participating in the highest such outer join; else NIL.
+ *                                     For a join otherrel: the list of all baserel relids
+ *                                     syntactically within the join.  Otherwise, unused.
  *             joininfo  - List of JoinInfo nodes, containing info about each join
  *                                     clause in which this relation participates
  *             innerjoin - List of Path nodes that represent indices that may be used
@@ -128,11 +154,20 @@ typedef enum CostSelector
  * until after the outer join is performed.
  *----------
  */
+typedef enum RelOptKind
+{
+       RELOPT_BASEREL,
+       RELOPT_JOINREL,
+       RELOPT_OTHER_JOIN_REL,
+       RELOPT_OTHER_CHILD_REL
+} RelOptKind;
 
 typedef struct RelOptInfo
 {
        NodeTag         type;
 
+       RelOptKind      reloptkind;
+
        /* all relations included in this RelOptInfo */
        Relids          relids;                 /* integer list of base relids (RT
                                                                 * indexes) */
@@ -155,6 +190,10 @@ typedef struct RelOptInfo
        double          tuples;
        struct Plan *subplan;
 
+       /* information about a join rel (not set for base rels!) */
+       Index           joinrti;
+       List       *joinrteids;
+
        /* used by various scans and joins: */
        List       *baserestrictinfo;           /* RestrictInfo structures (if
                                                                                 * base rel) */
@@ -228,6 +267,16 @@ typedef struct IndexOptInfo
        bool            unique;                 /* if a unique index */
 } IndexOptInfo;
 
+
+/*
+ * A Var is considered to belong to a relation if it's either from one
+ * of the actual base rels making up the relation, or it's a join alias
+ * var that is included in the relation.
+ */
+#define VARISRELMEMBER(varno,rel) (intMember((varno), (rel)->relids) || \
+                                                                  intMember((varno), (rel)->joinrteids))
+
+
 /*
  * PathKeys
  *
index 9be3cfde0102e8e176a398f4bdaa995b125315be..d9419df47d3dedabc4b3d456e1e0199b9b4ab599 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pathnode.h,v 1.41 2001/11/05 17:46:34 momjian Exp $
+ * $Id: pathnode.h,v 1.42 2002/03/12 00:52:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -67,9 +67,11 @@ extern HashPath *create_hashjoin_path(Query *root,
 /*
  * prototypes for relnode.c
  */
-extern RelOptInfo *build_base_rel(Query *root, int relid);
+extern void build_base_rel(Query *root, int relid);
 extern RelOptInfo *build_other_rel(Query *root, int relid);
 extern RelOptInfo *find_base_rel(Query *root, int relid);
+extern RelOptInfo *find_other_rel(Query *root, int relid);
+extern RelOptInfo *find_other_rel_for_join(Query *root, List *relids);
 extern RelOptInfo *build_join_rel(Query *root,
                           RelOptInfo *outer_rel,
                           RelOptInfo *inner_rel,
index 37209dfd5c37a2a2857fda28d6101b2855f0be65..919f3d23de4b10c7b7101edeb6fcda52ade8881a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.54 2001/11/05 17:46:34 momjian Exp $
+ * $Id: planmain.h,v 1.55 2002/03/12 00:52:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,18 +47,19 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
 /*
  * prototypes for plan/initsplan.c
  */
+extern List *add_base_rels_to_query(Query *root, Node *jtnode);
 extern void build_base_rel_tlists(Query *root, List *tlist);
 extern Relids distribute_quals_to_rels(Query *root, Node *jtnode);
-extern List *add_missing_rels_to_query(Query *root, Node *jtnode);
 extern void process_implied_equality(Query *root, Node *item1, Node *item2,
                                                 Oid sortop1, Oid sortop2);
 
 /*
  * prototypes for plan/setrefs.c
  */
-extern void set_plan_references(Plan *plan);
-extern List *join_references(List *clauses, List *outer_tlist,
-                               List *inner_tlist, Index acceptable_rel);
+extern void set_plan_references(Query *root, Plan *plan);
+extern List *join_references(List *clauses, Query *root,
+                                                        List *outer_tlist, List *inner_tlist,
+                                                        Index acceptable_rel, Index join_rti);
 extern void fix_opids(Node *node);
 
 /*
index c88da074a5826a33e5bff9351caf768a3028a7df..1153604e48a1ed930b24f699058871be347f12c7 100644 (file)
@@ -7,14 +7,15 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: var.h,v 1.17 2001/11/05 17:46:34 momjian Exp $
+ * $Id: var.h,v 1.18 2002/03/12 00:52:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef VAR_H
 #define VAR_H
 
-#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+
 
 extern List *pull_varnos(Node *node);
 extern bool contain_var_reference(Node *node, int varno, int varattno,
@@ -22,5 +23,8 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
 extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
 extern bool contain_var_clause(Node *node);
 extern List *pull_var_clause(Node *node, bool includeUpperVars);
+extern Node *flatten_join_alias_vars(Node *node, Query *root, int expandRTI);
+extern void build_join_alias_subvars(Query *root, Var *aliasvar,
+                                                                        Var **leftsubvar, Var **rightsubvar);
 
 #endif   /* VAR_H */
index 3bc575f6acecf73a7ac7ec6a08bb8cf1b7bdc2b7..1b579850f4eeac7ce4014c06a5fded3ddc4cc35a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_relation.h,v 1.28 2001/11/05 17:46:35 momjian Exp $
+ * $Id: parse_relation.h,v 1.29 2002/03/12 00:52:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,7 @@
 
 #include "parser/parse_node.h"
 
-extern Node *refnameRangeOrJoinEntry(ParseState *pstate,
+extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
                                                char *refname,
                                                int *sublevels_up);
 extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
@@ -36,14 +36,21 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
                                                          Query *subquery,
                                                          Attr *alias,
                                                          bool inFromCl);
+extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
+                                                 List *colnames,
+                                                 JoinType jointype,
+                                                 List *coltypes,
+                                                 List *coltypmods,
+                                                 List *leftcols,
+                                                 List *rightcols,
+                                                 Attr *alias,
+                                                 bool inFromCl);
 extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
                          bool addToJoinList, bool addToNameSpace);
 extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
 extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
                  List **colnames, List **colvars);
 extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte);
-extern List *expandJoinAttrs(ParseState *pstate, JoinExpr *join,
-                               int sublevels_up);
 extern int     attnameAttNum(Relation rd, char *a);
 extern Name attnumAttName(Relation rd, int attid);
 extern Oid     attnumTypeId(Relation rd, int attid);
index 2c19c9cc45bb0fcce628d907e8e6a20f549c9110..f0dc6627f43a0b229f6426109f085de4c545dc1b 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsetree.h,v 1.16 2001/11/05 17:46:35 momjian Exp $
+ * $Id: parsetree.h,v 1.17 2002/03/12 00:52:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,7 +41,7 @@
  *
  *             Given the range index of a relation, return the corresponding
  *             relation OID.  Note that InvalidOid will be returned if the
- *             RTE is for a sub-select rather than a relation.
+ *             RTE is for a non-relation-type RTE.
  */
 #define getrelid(rangeindex,rangetable) \
        (rt_fetch(rangeindex, rangetable)->relid)
  */
 extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum);
 
+/*
+ * Given an RTE and an attribute number, return the appropriate
+ * type and typemod info for that attribute of that RTE.
+ */
+extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
+                                                                  Oid *vartype, int32 *vartypmod);
+
 #endif   /* PARSETREE_H */
index e3bc6217841ee07075966bd37434f5509c025e7a..4c2dec5d106f0f24fd1cb8340402752ab2d9cdd8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.171 2001/11/05 17:46:36 momjian Exp $
+ * $Id: builtins.h,v 1.172 2002/03/12 00:52:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,7 +15,7 @@
 #define BUILTINS_H
 
 #include "fmgr.h"
-#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
 #include "storage/itemptr.h"   /* for setLastTid() */
 
 /*
@@ -343,6 +343,10 @@ extern Datum pg_get_expr(PG_FUNCTION_ARGS);
 extern char *deparse_expression(Node *expr, List *dpcontext,
                                   bool forceprefix);
 extern List *deparse_context_for(char *relname, Oid relid);
+extern List *deparse_context_for_plan(int outer_varno, Node *outercontext,
+                                                                         int inner_varno, Node *innercontext);
+extern Node *deparse_context_for_subplan(const char *name, List *tlist,
+                                                                                List *rtable);
 
 /* tid.c */
 extern void setLastTid(const ItemPointer tid);
index 6774390046e30b394914fdbeaad0bbd4d0ba9804..cfe7ded08d9022b893d2f69bda91fb679648bcbe 100644 (file)
@@ -1846,8 +1846,33 @@ SELECT '' AS "xxx", *
 SELECT '' AS "xxx", *
   FROM J1_TBL UNION JOIN J2_TBL;
 ERROR:  UNION JOIN is not implemented yet
+--
+-- Multiway full join
+--
+CREATE TABLE t1 (name TEXT, n INTEGER);
+CREATE TABLE t2 (name TEXT, n INTEGER);
+CREATE TABLE t3 (name TEXT, n INTEGER);
+INSERT INTO t1 VALUES ( 'aa', 11 );
+INSERT INTO t2 VALUES ( 'aa', 12 );
+INSERT INTO t2 VALUES ( 'bb', 22 );
+INSERT INTO t2 VALUES ( 'dd', 42 );
+INSERT INTO t3 VALUES ( 'aa', 13 );
+INSERT INTO t3 VALUES ( 'bb', 23 );
+INSERT INTO t3 VALUES ( 'cc', 33 );
+SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name);
+ name | n  | n  | n  
+------+----+----+----
+ aa   | 11 | 12 | 13
+ bb   |    | 22 | 23
+ cc   |    |    | 33
+ dd   |    | 42 |   
+(4 rows)
+
 --
 -- Clean up
 --
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
 DROP TABLE J1_TBL;
 DROP TABLE J2_TBL;
index ae63a61c01fbb8741ea131e6bd49c18c754cdc97..91e64adfc9437d11fb1af311d29033ea22979695 100644 (file)
@@ -198,10 +198,31 @@ SELECT '' AS "xxx", *
 SELECT '' AS "xxx", *
   FROM J1_TBL UNION JOIN J2_TBL;
 
+--
+-- Multiway full join
+--
+
+CREATE TABLE t1 (name TEXT, n INTEGER);
+CREATE TABLE t2 (name TEXT, n INTEGER);
+CREATE TABLE t3 (name TEXT, n INTEGER);
+
+INSERT INTO t1 VALUES ( 'aa', 11 );
+INSERT INTO t2 VALUES ( 'aa', 12 );
+INSERT INTO t2 VALUES ( 'bb', 22 );
+INSERT INTO t2 VALUES ( 'dd', 42 );
+INSERT INTO t3 VALUES ( 'aa', 13 );
+INSERT INTO t3 VALUES ( 'bb', 23 );
+INSERT INTO t3 VALUES ( 'cc', 33 );
+
+SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name);
+
 --
 -- Clean up
 --
 
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+
 DROP TABLE J1_TBL;
 DROP TABLE J2_TBL;
-