OSDN Git Service

Teach planner to convert simple UNION ALL subqueries into append relations,
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 3 Feb 2006 21:08:49 +0000 (21:08 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 3 Feb 2006 21:08:49 +0000 (21:08 +0000)
thereby sharing code with the inheritance case.  This puts the UNION-ALL-view
approach to partitioned tables on par with inheritance, so far as constraint
exclusion is concerned: it works either way.  (Still need to update the docs
to say so.)  The definition of "simple UNION ALL" is a little simpler than
I would like --- basically the union arms can only be SELECT * FROM foo
--- but it's good enough for partitioned-table cases.

src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/relnode.c
src/backend/optimizer/util/tlist.c
src/include/optimizer/pathnode.h
src/include/optimizer/prep.h
src/include/optimizer/tlist.h

index 90add90..890bb59 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.140 2006/01/31 21:39:23 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.141 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,6 +42,8 @@ int                   geqo_threshold;
 
 
 static void set_base_rel_pathlists(PlannerInfo *root);
+static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
+                                Index rti, RangeTblEntry *rte);
 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                           RangeTblEntry *rte);
 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -133,7 +135,6 @@ set_base_rel_pathlists(PlannerInfo *root)
        for (rti = 1; rti < root->simple_rel_array_size; rti++)
        {
                RelOptInfo *rel = root->simple_rel_array[rti];
-               RangeTblEntry *rte;
 
                /* there may be empty slots corresponding to non-baserel RTEs */
                if (rel == NULL)
@@ -145,33 +146,44 @@ set_base_rel_pathlists(PlannerInfo *root)
                if (rel->reloptkind != RELOPT_BASEREL)
                        continue;
 
-               rte = rt_fetch(rti, root->parse->rtable);
+               set_rel_pathlist(root, rel, rti,
+                                                rt_fetch(rti, root->parse->rtable));
+       }
+}
 
-               if (rte->inh)
-               {
-                       /* It's an "append relation", process accordingly */
-                       set_append_rel_pathlist(root, rel, rti, rte);
-               }
-               else if (rel->rtekind == RTE_SUBQUERY)
-               {
-                       /* Subquery --- generate a separate plan for it */
-                       set_subquery_pathlist(root, rel, rti, rte);
-               }
-               else if (rel->rtekind == RTE_FUNCTION)
-               {
-                       /* RangeFunction --- generate a separate plan for it */
-                       set_function_pathlist(root, rel, rte);
-               }
-               else
-               {
-                       /* Plain relation */
-                       set_plain_rel_pathlist(root, rel, rte);
-               }
+/*
+ * set_rel_pathlist
+ *       Build access paths for a base relation
+ */
+static void
+set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
+                                Index rti, RangeTblEntry *rte)
+{
+       if (rte->inh)
+       {
+               /* It's an "append relation", process accordingly */
+               set_append_rel_pathlist(root, rel, rti, rte);
+       }
+       else if (rel->rtekind == RTE_SUBQUERY)
+       {
+               /* Subquery --- generate a separate plan for it */
+               set_subquery_pathlist(root, rel, rti, rte);
+       }
+       else if (rel->rtekind == RTE_FUNCTION)
+       {
+               /* RangeFunction --- generate a separate plan for it */
+               set_function_pathlist(root, rel, rte);
+       }
+       else
+       {
+               /* Plain relation */
+               Assert(rel->rtekind == RTE_RELATION);
+               set_plain_rel_pathlist(root, rel, rte);
+       }
 
 #ifdef OPTIMIZER_DEBUG
-               debug_print_rel(root, rel);
+       debug_print_rel(root, rel);
 #endif
-       }
 }
 
 /*
@@ -181,9 +193,6 @@ set_base_rel_pathlists(PlannerInfo *root)
 static void
 set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 {
-       Assert(rel->rtekind == RTE_RELATION);
-       Assert(!rte->inh);
-
        /* Mark rel with estimated output rows, width, etc */
        set_baserel_size_estimates(root, rel);
 
@@ -265,6 +274,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                int                     childRTindex;
                RelOptInfo *childrel;
                RangeTblEntry *childrte;
+               Path       *childpath;
                ListCell   *parentvars;
                ListCell   *childvars;
 
@@ -346,10 +356,20 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 
                /*
                 * Compute the child's access paths, and save the cheapest.
+                *
+                * It's possible that the child is itself an appendrel, in which
+                * case we can "cut out the middleman" and just add its child
+                * paths to our own list.  (We don't try to do this earlier because
+                * we need to apply both levels of transformation to the quals.)
                 */
-               set_plain_rel_pathlist(root, childrel, childrte);
+               set_rel_pathlist(root, childrel, childRTindex, childrte);
 
-               subpaths = lappend(subpaths, childrel->cheapest_total_path);
+               childpath = childrel->cheapest_total_path;
+               if (IsA(childpath, AppendPath))
+                       subpaths = list_concat(subpaths,
+                                                                  ((AppendPath *) childpath)->subpaths);
+               else
+                       subpaths = lappend(subpaths, childpath);
 
                /*
                 * Propagate size information from the child back to the parent. For
index 137be08..df3bd12 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.99 2006/01/31 21:39:23 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.100 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
-#include "optimizer/prep.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 
@@ -856,8 +855,6 @@ join_before_append(PlannerInfo *root,
                int                     childRTindex;
                RelOptInfo *childrel;
                Path       *bestinnerjoin;
-               Relids          joinrelids;
-               Relids     *save_attr_needed;
                RelOptInfo *this_joinrel;
                List       *this_restrictlist;
 
@@ -899,27 +896,9 @@ join_before_append(PlannerInfo *root,
                 * in joinrels.c, it provides necessary context for the Path,
                 * such as properly-translated target and quals lists.
                 */
-               joinrelids = bms_copy(joinrel->relids);
-               joinrelids = bms_del_member(joinrelids, parentRTindex);
-               joinrelids = bms_add_member(joinrelids, childRTindex);
-
-               /*
-                * Kluge: temporarily adjust the outer rel's attr_needed info so
-                * that it references the member rel instead of the appendrel.
-                * This is needed to build the correct targetlist for the joinrel.
-                */
-               save_attr_needed = outerrel->attr_needed;
-               outerrel->attr_needed =
-                       adjust_other_rel_attr_needed(outerrel, appinfo,
-                                                                                outerrel->min_attr,
-                                                                                outerrel->max_attr);
-
-               this_joinrel = build_join_rel(root, joinrelids, outerrel, childrel,
-                                                                         jointype, &this_restrictlist);
-
-               /* Now we can undo the hack on attr_needed */
-               pfree(outerrel->attr_needed);
-               outerrel->attr_needed = save_attr_needed;
+               this_joinrel = translate_join_rel(root, joinrel, appinfo,
+                                                                                 outerrel, childrel, jointype,
+                                                                                 &this_restrictlist);
 
                /* Build Path for join and add to result list */
                append_paths = lappend(append_paths,
index e007e4e..e530a1a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.197 2006/01/31 21:39:24 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.198 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -222,7 +222,7 @@ subquery_planner(Query *parse, double tuple_fraction,
         * this query.
         */
        parse->jointree = (FromExpr *)
-               pull_up_subqueries(root, (Node *) parse->jointree, false);
+               pull_up_subqueries(root, (Node *) parse->jointree, false, false);
 
        /*
         * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
index 546c4bd..a955c73 100644 (file)
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.34 2006/01/31 21:39:24 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.35 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/prep.h"
 #include "optimizer/subselect.h"
+#include "optimizer/tlist.h"
 #include "optimizer/var.h"
+#include "parser/parse_expr.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
@@ -37,8 +40,23 @@ typedef struct reduce_outer_joins_state
        List       *sub_states;         /* List of states for subtree components */
 } reduce_outer_joins_state;
 
+static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
+                                                                        RangeTblEntry *rte,
+                                                                        bool below_outer_join,
+                                                                        bool append_rel_member);
+static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode,
+                                                                         RangeTblEntry *rte);
+static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root,
+                                                                          int parentRTindex, Query *setOpQuery);
+static void make_setop_translation_lists(Query *query,
+                                                        Index newvarno,
+                                                        List **col_mappings, List **translated_vars);
 static bool is_simple_subquery(Query *subquery);
+static bool is_simple_union_all(Query *subquery);
+static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery,
+                                                                               List *colTypes);
 static bool has_nullable_targetlist(Query *subquery);
+static bool is_safe_append_member(Query *subquery);
 static void resolvenew_in_jointree(Node *jtnode, int varno,
                                           RangeTblEntry *rte, List *subtlist);
 static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
@@ -48,6 +66,8 @@ static void reduce_outer_joins_pass2(Node *jtnode,
                                                 Relids nonnullable_rels);
 static void fix_in_clause_relids(List *in_info_list, int varno,
                                         Relids subrelids);
+static void fix_append_rel_relids(List *append_rel_list, int varno,
+                                                                 Relids subrelids);
 static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
 
 
@@ -110,10 +130,16 @@ pull_up_IN_clauses(PlannerInfo *root, Node *node)
  *             Look for subqueries in the rangetable that can be pulled up into
  *             the parent query.  If the subquery has no special features like
  *             grouping/aggregation then we can merge it into the parent's jointree.
+ *             Also, subqueries that are simple UNION ALL structures can be
+ *             converted into "append relations".
  *
  * below_outer_join is true if this jointree node is within the nullable
  * side of an outer join.  This restricts what we can do.
  *
+ * append_rel_member is true if we are looking at a member subquery of
+ * an append relation.  This puts some different restrictions on what
+ * we can do.
+ *
  * A tricky aspect of this code is that if we pull up a subquery we have
  * to replace Vars that reference the subquery's outputs throughout the
  * parent query, including quals attached to jointree nodes above the one
@@ -124,16 +150,15 @@ pull_up_IN_clauses(PlannerInfo *root, Node *node)
  * copy of the tree; we have to invoke it just on the quals, instead.
  */
 Node *
-pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
+pull_up_subqueries(PlannerInfo *root, Node *jtnode,
+                                  bool below_outer_join, bool append_rel_member)
 {
        if (jtnode == NULL)
                return NULL;
        if (IsA(jtnode, RangeTblRef))
        {
                int                     varno = ((RangeTblRef *) jtnode)->rtindex;
-               Query      *parse = root->parse;
-               RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
-               Query      *subquery = rte->subquery;
+               RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable);
 
                /*
                 * Is this a subquery RTE, and if so, is the subquery simple enough to
@@ -148,260 +173,74 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
                 * expressions; we'd have to figure out how to get the pseudo-
                 * variables evaluated at the right place in the modified plan tree.
                 * Fix it someday.
+                *
+                * If we are looking at an append-relation member, we can't pull
+                * it up unless is_safe_append_member says so.
                 */
                if (rte->rtekind == RTE_SUBQUERY &&
-                       is_simple_subquery(subquery) &&
-                       (!below_outer_join || has_nullable_targetlist(subquery)))
-               {
-                       PlannerInfo *subroot;
-                       int                     rtoffset;
-                       List       *subtlist;
-                       ListCell   *rt;
-
-                       /*
-                        * Need a modifiable copy of the subquery to hack on.  Even if we
-                        * didn't sometimes choose not to pull up below, we must do this
-                        * to avoid problems if the same subquery is referenced from
-                        * multiple jointree items (which can't happen normally, but might
-                        * after rule rewriting).
-                        */
-                       subquery = copyObject(subquery);
-
-                       /*
-                        * Create a PlannerInfo data structure for this subquery.
-                        *
-                        * NOTE: the next few steps should match the first processing in
-                        * subquery_planner().  Can we refactor to avoid code duplication,
-                        * or would that just make things uglier?
-                        */
-                       subroot = makeNode(PlannerInfo);
-                       subroot->parse = subquery;
-                       subroot->in_info_list = NIL;
-                       subroot->append_rel_list = NIL;
-
-                       /*
-                        * Pull up any IN clauses within the subquery's WHERE, so that we
-                        * don't leave unoptimized INs behind.
-                        */
-                       if (subquery->hasSubLinks)
-                               subquery->jointree->quals = pull_up_IN_clauses(subroot,
-                                                                                                 subquery->jointree->quals);
-
-                       /*
-                        * Recursively pull up the subquery's subqueries, so that this
-                        * routine's processing is complete for its jointree and
-                        * rangetable.
-                        *
-                        * Note: 'false' is correct here even if we are within an outer
-                        * join in the upper query; the lower query starts with a clean
-                        * slate for outer-join semantics.
-                        */
-                       subquery->jointree = (FromExpr *)
-                               pull_up_subqueries(subroot, (Node *) subquery->jointree,
-                                                                  false);
-
-                       /*
-                        * Now we must recheck whether the subquery is still simple enough
-                        * to pull up.  If not, abandon processing it.
-                        *
-                        * We don't really need to recheck all the conditions involved,
-                        * but it's easier just to keep this "if" looking the same as the
-                        * one above.
-                        */
-                       if (is_simple_subquery(subquery) &&
-                               (!below_outer_join || has_nullable_targetlist(subquery)))
-                       {
-                               /* good to go */
-                       }
-                       else
-                       {
-                               /*
-                                * Give up, return unmodified RangeTblRef.
-                                *
-                                * Note: The work we just did will be redone when the subquery
-                                * gets planned on its own.  Perhaps we could avoid that by
-                                * storing the modified subquery back into the rangetable, but
-                                * I'm not gonna risk it now.
-                                */
-                               return jtnode;
-                       }
-
-                       /*
-                        * Adjust level-0 varnos in subquery so that we can append its
-                        * rangetable to upper query's.  We have to fix the subquery's
-                        * in_info_list and append_rel_list, as well.
-                        */
-                       rtoffset = list_length(parse->rtable);
-                       OffsetVarNodes((Node *) subquery, rtoffset, 0);
-                       OffsetVarNodes((Node *) subroot->in_info_list, rtoffset, 0);
-                       OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
-
-                       /*
-                        * Upper-level vars in subquery are now one level closer to their
-                        * parent than before.
-                        */
-                       IncrementVarSublevelsUp((Node *) subquery, -1, 1);
-                       IncrementVarSublevelsUp((Node *) subroot->in_info_list, -1, 1);
-                       IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
-
-                       /*
-                        * Replace all of the top query's references to the subquery's
-                        * outputs with copies of the adjusted subtlist items, being
-                        * careful not to replace any of the jointree structure. (This'd
-                        * be a lot cleaner if we could use query_tree_mutator.)
-                        */
-                       subtlist = subquery->targetList;
-                       parse->targetList = (List *)
-                               ResolveNew((Node *) parse->targetList,
-                                                  varno, 0, rte,
-                                                  subtlist, CMD_SELECT, 0);
-                       resolvenew_in_jointree((Node *) parse->jointree, varno,
-                                                                  rte, subtlist);
-                       Assert(parse->setOperations == NULL);
-                       parse->havingQual =
-                               ResolveNew(parse->havingQual,
-                                                  varno, 0, rte,
-                                                  subtlist, CMD_SELECT, 0);
-                       root->in_info_list = (List *)
-                               ResolveNew((Node *) root->in_info_list,
-                                                  varno, 0, rte,
-                                                  subtlist, CMD_SELECT, 0);
-                       root->append_rel_list = (List *)
-                               ResolveNew((Node *) root->append_rel_list,
-                                                  varno, 0, rte,
-                                                  subtlist, CMD_SELECT, 0);
-
-                       foreach(rt, parse->rtable)
-                       {
-                               RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt);
-
-                               if (otherrte->rtekind == RTE_JOIN)
-                                       otherrte->joinaliasvars = (List *)
-                                               ResolveNew((Node *) otherrte->joinaliasvars,
-                                                                  varno, 0, rte,
-                                                                  subtlist, CMD_SELECT, 0);
-                       }
-
-                       /*
-                        * Now append the adjusted rtable entries to upper query. (We hold
-                        * off until after fixing the upper rtable entries; no point in
-                        * running that code on the subquery ones too.)
-                        */
-                       parse->rtable = list_concat(parse->rtable, subquery->rtable);
-
-                       /*
-                        * Pull up any FOR UPDATE/SHARE markers, too.  (OffsetVarNodes
-                        * already adjusted the marker values, so just list_concat the
-                        * list.)
-                        *
-                        * Executor can't handle multiple FOR UPDATE/SHARE/NOWAIT flags,
-                        * so complain if they are valid but different
-                        */
-                       if (parse->rowMarks && subquery->rowMarks)
-                       {
-                               if (parse->forUpdate != subquery->forUpdate)
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                        errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
-                               if (parse->rowNoWait != subquery->rowNoWait)
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                       errmsg("cannot use both wait and NOWAIT in one query")));
-                       }
-                       parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
-                       if (subquery->rowMarks)
-                       {
-                               parse->forUpdate = subquery->forUpdate;
-                               parse->rowNoWait = subquery->rowNoWait;
-                       }
-
-                       /*
-                        * We also have to fix the relid sets of any parent InClauseInfo
-                        * nodes.  (This could perhaps be done by ResolveNew, but it would
-                        * clutter that routine's API unreasonably.)
-                        */
-                       if (root->in_info_list)
-                       {
-                               Relids          subrelids;
-
-                               subrelids = get_relids_in_jointree((Node *) subquery->jointree);
-                               fix_in_clause_relids(root->in_info_list, varno, subrelids);
-                       }
-
-                       /*
-                        * And now append any subquery InClauseInfos to our list.
-                        */
-                       root->in_info_list = list_concat(root->in_info_list,
-                                                                                        subroot->in_info_list);
-
-                       /*
-                        * XXX need to do something about adjusting AppendRelInfos too
-                        */
-                       Assert(root->append_rel_list == NIL);
-
-                       /* Also pull up any subquery AppendRelInfos */
-                       root->append_rel_list = list_concat(root->append_rel_list,
-                                                                                               subroot->append_rel_list);
-
-                       /*
-                        * We don't have to do the equivalent bookkeeping for outer-join
-                        * info, because that hasn't been set up yet.
-                        */
-                       Assert(root->oj_info_list == NIL);
-                       Assert(subroot->oj_info_list == NIL);
-
-                       /*
-                        * Miscellaneous housekeeping.
-                        */
-                       parse->hasSubLinks |= subquery->hasSubLinks;
-                       /* subquery won't be pulled up if it hasAggs, so no work there */
+                       is_simple_subquery(rte->subquery) &&
+                       (!below_outer_join || has_nullable_targetlist(rte->subquery)) &&
+                       (!append_rel_member || is_safe_append_member(rte->subquery)))
+                       return pull_up_simple_subquery(root, jtnode, rte,
+                                                                                  below_outer_join,
+                                                                                  append_rel_member);
 
-                       /*
-                        * Return the adjusted subquery jointree to replace the
-                        * RangeTblRef entry in my jointree.
-                        */
-                       return (Node *) subquery->jointree;
-               }
+               /*
+                * Alternatively, is it a simple UNION ALL subquery?  If so, flatten
+                * into an "append relation".  We can do this regardless of nullability
+                * considerations since this transformation does not result in
+                * propagating non-Var expressions into upper levels of the query.
+                *
+                * It's also safe to do this regardless of whether this query is
+                * itself an appendrel member.  (If you're thinking we should try
+                * to flatten the two levels of appendrel together, you're right;
+                * but we handle that in set_append_rel_pathlist, not here.)
+                */
+               if (rte->rtekind == RTE_SUBQUERY &&
+                       is_simple_union_all(rte->subquery))
+                       return pull_up_simple_union_all(root, jtnode, rte);
        }
        else if (IsA(jtnode, FromExpr))
        {
                FromExpr   *f = (FromExpr *) jtnode;
                ListCell   *l;
 
+               Assert(!append_rel_member);
                foreach(l, f->fromlist)
                        lfirst(l) = pull_up_subqueries(root, lfirst(l),
-                                                                                  below_outer_join);
+                                                                                  below_outer_join, false);
        }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
 
+               Assert(!append_rel_member);
                /* Recurse, being careful to tell myself when inside outer join */
                switch (j->jointype)
                {
                        case JOIN_INNER:
                                j->larg = pull_up_subqueries(root, j->larg,
-                                                                                        below_outer_join);
+                                                                                        below_outer_join, false);
                                j->rarg = pull_up_subqueries(root, j->rarg,
-                                                                                        below_outer_join);
+                                                                                        below_outer_join, false);
                                break;
                        case JOIN_LEFT:
                                j->larg = pull_up_subqueries(root, j->larg,
-                                                                                        below_outer_join);
+                                                                                        below_outer_join, false);
                                j->rarg = pull_up_subqueries(root, j->rarg,
-                                                                                        true);
+                                                                                        true, false);
                                break;
                        case JOIN_FULL:
                                j->larg = pull_up_subqueries(root, j->larg,
-                                                                                        true);
+                                                                                        true, false);
                                j->rarg = pull_up_subqueries(root, j->rarg,
-                                                                                        true);
+                                                                                        true, false);
                                break;
                        case JOIN_RIGHT:
                                j->larg = pull_up_subqueries(root, j->larg,
-                                                                                        true);
+                                                                                        true, false);
                                j->rarg = pull_up_subqueries(root, j->rarg,
-                                                                                        below_outer_join);
+                                                                                        below_outer_join, false);
                                break;
                        case JOIN_UNION:
 
@@ -426,6 +265,379 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
 }
 
 /*
+ * pull_up_simple_subquery
+ *             Attempt to pull up a single simple subquery.
+ *
+ * jtnode is a RangeTblRef that has been tentatively identified as a simple
+ * subquery by pull_up_subqueries.  We return the replacement jointree node,
+ * or jtnode itself if we determine that the subquery can't be pulled up after
+ * all.
+ */
+static Node *
+pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
+                                               bool below_outer_join, bool append_rel_member)
+{
+       Query      *parse = root->parse;
+       int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+       Query      *subquery;
+       PlannerInfo *subroot;
+       int                     rtoffset;
+       List       *subtlist;
+       ListCell   *rt;
+
+       /*
+        * Need a modifiable copy of the subquery to hack on.  Even if we
+        * didn't sometimes choose not to pull up below, we must do this
+        * to avoid problems if the same subquery is referenced from
+        * multiple jointree items (which can't happen normally, but might
+        * after rule rewriting).
+        */
+       subquery = copyObject(rte->subquery);
+
+       /*
+        * Create a PlannerInfo data structure for this subquery.
+        *
+        * NOTE: the next few steps should match the first processing in
+        * subquery_planner().  Can we refactor to avoid code duplication,
+        * or would that just make things uglier?
+        */
+       subroot = makeNode(PlannerInfo);
+       subroot->parse = subquery;
+       subroot->in_info_list = NIL;
+       subroot->append_rel_list = NIL;
+
+       /*
+        * Pull up any IN clauses within the subquery's WHERE, so that we
+        * don't leave unoptimized INs behind.
+        */
+       if (subquery->hasSubLinks)
+               subquery->jointree->quals = pull_up_IN_clauses(subroot,
+                                                                                               subquery->jointree->quals);
+
+       /*
+        * Recursively pull up the subquery's subqueries, so that
+        * pull_up_subqueries' processing is complete for its jointree and
+        * rangetable.
+        *
+        * Note: below_outer_join = false is correct here even if we are within an
+        * outer join in the upper query; the lower query starts with a clean
+        * slate for outer-join semantics.  Likewise, we say we aren't handling
+        * an appendrel member.
+        */
+       subquery->jointree = (FromExpr *)
+               pull_up_subqueries(subroot, (Node *) subquery->jointree, false, false);
+
+       /*
+        * Now we must recheck whether the subquery is still simple enough
+        * to pull up.  If not, abandon processing it.
+        *
+        * We don't really need to recheck all the conditions involved,
+        * but it's easier just to keep this "if" looking the same as the
+        * one in pull_up_subqueries.
+        */
+       if (is_simple_subquery(subquery) &&
+               (!below_outer_join || has_nullable_targetlist(subquery)) &&
+               (!append_rel_member || is_safe_append_member(subquery)))
+       {
+               /* good to go */
+       }
+       else
+       {
+               /*
+                * Give up, return unmodified RangeTblRef.
+                *
+                * Note: The work we just did will be redone when the subquery
+                * gets planned on its own.  Perhaps we could avoid that by
+                * storing the modified subquery back into the rangetable, but
+                * I'm not gonna risk it now.
+                */
+               return jtnode;
+       }
+
+       /*
+        * Adjust level-0 varnos in subquery so that we can append its
+        * rangetable to upper query's.  We have to fix the subquery's
+        * in_info_list and append_rel_list, as well.
+        */
+       rtoffset = list_length(parse->rtable);
+       OffsetVarNodes((Node *) subquery, rtoffset, 0);
+       OffsetVarNodes((Node *) subroot->in_info_list, rtoffset, 0);
+       OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
+
+       /*
+        * Upper-level vars in subquery are now one level closer to their
+        * parent than before.
+        */
+       IncrementVarSublevelsUp((Node *) subquery, -1, 1);
+       IncrementVarSublevelsUp((Node *) subroot->in_info_list, -1, 1);
+       IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
+
+       /*
+        * Replace all of the top query's references to the subquery's
+        * outputs with copies of the adjusted subtlist items, being
+        * careful not to replace any of the jointree structure. (This'd
+        * be a lot cleaner if we could use query_tree_mutator.)
+        */
+       subtlist = subquery->targetList;
+       parse->targetList = (List *)
+               ResolveNew((Node *) parse->targetList,
+                                  varno, 0, rte,
+                                  subtlist, CMD_SELECT, 0);
+       resolvenew_in_jointree((Node *) parse->jointree, varno,
+                                                  rte, subtlist);
+       Assert(parse->setOperations == NULL);
+       parse->havingQual =
+               ResolveNew(parse->havingQual,
+                                  varno, 0, rte,
+                                  subtlist, CMD_SELECT, 0);
+       root->in_info_list = (List *)
+               ResolveNew((Node *) root->in_info_list,
+                                  varno, 0, rte,
+                                  subtlist, CMD_SELECT, 0);
+       root->append_rel_list = (List *)
+               ResolveNew((Node *) root->append_rel_list,
+                                  varno, 0, rte,
+                                  subtlist, CMD_SELECT, 0);
+
+       foreach(rt, parse->rtable)
+       {
+               RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt);
+
+               if (otherrte->rtekind == RTE_JOIN)
+                       otherrte->joinaliasvars = (List *)
+                               ResolveNew((Node *) otherrte->joinaliasvars,
+                                                  varno, 0, rte,
+                                                  subtlist, CMD_SELECT, 0);
+       }
+
+       /*
+        * Now append the adjusted rtable entries to upper query. (We hold
+        * off until after fixing the upper rtable entries; no point in
+        * running that code on the subquery ones too.)
+        */
+       parse->rtable = list_concat(parse->rtable, subquery->rtable);
+
+       /*
+        * Pull up any FOR UPDATE/SHARE markers, too.  (OffsetVarNodes
+        * already adjusted the marker values, so just list_concat the
+        * list.)
+        *
+        * Executor can't handle multiple FOR UPDATE/SHARE/NOWAIT flags,
+        * so complain if they are valid but different
+        */
+       if (parse->rowMarks && subquery->rowMarks)
+       {
+               if (parse->forUpdate != subquery->forUpdate)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
+               if (parse->rowNoWait != subquery->rowNoWait)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("cannot use both wait and NOWAIT in one query")));
+       }
+       parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
+       if (subquery->rowMarks)
+       {
+               parse->forUpdate = subquery->forUpdate;
+               parse->rowNoWait = subquery->rowNoWait;
+       }
+
+       /*
+        * We also have to fix the relid sets of any parent InClauseInfo
+        * nodes.  (This could perhaps be done by ResolveNew, but it would
+        * clutter that routine's API unreasonably.)
+        *
+        * Likewise, relids appearing in AppendRelInfo nodes have to be fixed
+        * (but we took care of their translated_vars lists above).  We already
+        * checked that this won't require introducing multiple subrelids into
+        * the single-slot AppendRelInfo structs.
+        */
+       if (root->in_info_list || root->append_rel_list)
+       {
+               Relids          subrelids;
+
+               subrelids = get_relids_in_jointree((Node *) subquery->jointree);
+               fix_in_clause_relids(root->in_info_list, varno, subrelids);
+               fix_append_rel_relids(root->append_rel_list, varno, subrelids);
+       }
+
+       /*
+        * And now add any subquery InClauseInfos and AppendRelInfos to our lists.
+        */
+       root->in_info_list = list_concat(root->in_info_list,
+                                                                        subroot->in_info_list);
+       root->append_rel_list = list_concat(root->append_rel_list,
+                                                                               subroot->append_rel_list);
+
+       /*
+        * We don't have to do the equivalent bookkeeping for outer-join
+        * info, because that hasn't been set up yet.
+        */
+       Assert(root->oj_info_list == NIL);
+       Assert(subroot->oj_info_list == NIL);
+
+       /*
+        * Miscellaneous housekeeping.
+        */
+       parse->hasSubLinks |= subquery->hasSubLinks;
+       /* subquery won't be pulled up if it hasAggs, so no work there */
+
+       /*
+        * Return the adjusted subquery jointree to replace the
+        * RangeTblRef entry in parent's jointree.
+        */
+       return (Node *) subquery->jointree;
+}
+
+/*
+ * pull_up_simple_union_all
+ *             Pull up a single simple UNION ALL subquery.
+ *
+ * jtnode is a RangeTblRef that has been identified as a simple UNION ALL
+ * subquery by pull_up_subqueries.  We pull up the leaf subqueries and
+ * build an "append relation" for the union set.  The result value is just
+ * jtnode, since we don't actually need to change the query jointree.
+ */
+static Node *
+pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
+{
+       int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+       Query      *subquery = rte->subquery;
+
+       /*
+        * Recursively scan the subquery's setOperations tree and copy the leaf
+        * subqueries into the parent rangetable.  Add AppendRelInfo nodes for
+        * them to the parent's append_rel_list, too.
+        */
+       Assert(subquery->setOperations);
+       pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery);
+
+       /*
+        * Mark the parent as an append relation.
+        */
+       rte->inh = true;
+
+       return jtnode;
+}
+
+/*
+ * pull_up_union_leaf_queries -- recursive guts of pull_up_simple_union_all
+ *
+ * Note that setOpQuery is the Query containing the setOp node, whose rtable
+ * is where to look up the RTE if setOp is a RangeTblRef.  This is *not* the
+ * same as root->parse, which is the top-level Query we are pulling up into.
+ * parentRTindex is the appendrel parent's index in root->parse->rtable.
+ */
+static void
+pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
+                                                  Query *setOpQuery)
+{
+       if (IsA(setOp, RangeTblRef))
+       {
+               RangeTblRef *rtr = (RangeTblRef *) setOp;
+               RangeTblEntry *rte = rt_fetch(rtr->rtindex, setOpQuery->rtable);
+               Query      *subquery;
+               int                     childRTindex;
+               AppendRelInfo *appinfo;
+               Query      *parse = root->parse;
+
+               /*
+                * Make a modifiable copy of the child RTE and contained query.
+                */
+               rte = copyObject(rte);
+               subquery = rte->subquery;
+               Assert(subquery != NULL);
+
+               /*
+                * Upper-level vars in subquery are now one level closer to their
+                * parent than before.  We don't have to worry about offsetting
+                * varnos, though, because any such vars must refer to stuff above
+                * the level of the query we are pulling into.
+                */
+               IncrementVarSublevelsUp((Node *) subquery, -1, 1);
+
+               /*
+                * Attach child RTE to parent rtable.
+                */
+               parse->rtable = lappend(parse->rtable, rte);
+               childRTindex = list_length(parse->rtable);
+
+               /*
+                * Build a suitable AppendRelInfo, and attach to parent's list.
+                */
+               appinfo = makeNode(AppendRelInfo);
+               appinfo->parent_relid = parentRTindex;
+               appinfo->child_relid = childRTindex;
+               appinfo->parent_reltype = InvalidOid;
+               appinfo->child_reltype = InvalidOid;
+               make_setop_translation_lists(setOpQuery, childRTindex,
+                                                                        &appinfo->col_mappings,
+                                                                        &appinfo->translated_vars);
+               appinfo->parent_reloid = InvalidOid;
+               root->append_rel_list = lappend(root->append_rel_list, appinfo);
+
+               /*
+                * Recursively apply pull_up_subqueries to the new child RTE.  (We
+                * must build the AppendRelInfo first, because this will modify it.)
+                * Note that we can pass below_outer_join = false even if we're
+                * actually under an outer join, because the child's expressions
+                * aren't going to propagate up above the join.
+                */
+               rtr = makeNode(RangeTblRef);
+               rtr->rtindex = childRTindex;
+               (void) pull_up_subqueries(root, (Node *) rtr, false, true);
+       }
+       else if (IsA(setOp, SetOperationStmt))
+       {
+               SetOperationStmt *op = (SetOperationStmt *) setOp;
+
+               /* Recurse to reach leaf queries */
+               pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery);
+               pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery);
+       }
+       else
+       {
+               elog(ERROR, "unrecognized node type: %d",
+                        (int) nodeTag(setOp));
+       }
+}
+
+/*
+ * make_setop_translation_lists
+ *       Build the lists of translations from parent Vars to child Vars for
+ *       a UNION ALL member.  We need both a column number mapping list
+ *       and a list of Vars representing the child columns.
+ */
+static void
+make_setop_translation_lists(Query *query,
+                                                        Index newvarno,
+                                                        List **col_mappings, List **translated_vars)
+{
+       List       *numbers = NIL;
+       List       *vars = NIL;
+       ListCell   *l;
+
+       foreach(l, query->targetList)
+       {
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+               if (tle->resjunk)
+                       continue;
+
+               numbers = lappend_int(numbers, tle->resno);
+               vars = lappend(vars, makeVar(newvarno,
+                                                                        tle->resno,
+                                                                        exprType((Node *) tle->expr),
+                                                                        exprTypmod((Node *) tle->expr),
+                                                                        0));
+       }
+
+       *col_mappings = numbers;
+       *translated_vars = vars;
+}
+
+/*
  * is_simple_subquery
  *       Check a subquery in the range table to see if it's simple enough
  *       to pull up into the parent query.
@@ -443,7 +655,8 @@ is_simple_subquery(Query *subquery)
                elog(ERROR, "subquery is bogus");
 
        /*
-        * Can't currently pull up a query with setops. Maybe after querytree
+        * Can't currently pull up a query with setops (unless it's simple UNION
+        * ALL, which is handled by a different code path). Maybe after querytree
         * redesign...
         */
        if (subquery->setOperations)
@@ -485,6 +698,78 @@ is_simple_subquery(Query *subquery)
 }
 
 /*
+ * is_simple_union_all
+ *       Check a subquery to see if it's a simple UNION ALL.
+ *
+ * We require all the setops to be UNION ALL (no mixing) and there can't be
+ * any datatype coercions involved, ie, all the leaf queries must emit the
+ * same datatypes.
+ */
+static bool
+is_simple_union_all(Query *subquery)
+{
+       SetOperationStmt *topop;
+
+       /* Let's just make sure it's a valid subselect ... */
+       if (!IsA(subquery, Query) ||
+               subquery->commandType != CMD_SELECT ||
+               subquery->resultRelation != 0 ||
+               subquery->into != NULL)
+               elog(ERROR, "subquery is bogus");
+
+       /* Is it a set-operation query at all? */
+       topop = (SetOperationStmt *) subquery->setOperations;
+       if (!topop)
+               return false;
+       Assert(IsA(topop, SetOperationStmt));
+
+       /* Can't handle ORDER BY, LIMIT/OFFSET, or locking */
+       if (subquery->sortClause ||
+               subquery->limitOffset ||
+               subquery->limitCount ||
+               subquery->rowMarks)
+               return false;
+
+       /* Recursively check the tree of set operations */
+       return is_simple_union_all_recurse((Node *) topop, subquery,
+                                                                          topop->colTypes);
+}
+
+static bool
+is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
+{
+       if (IsA(setOp, RangeTblRef))
+       {
+               RangeTblRef *rtr = (RangeTblRef *) setOp;
+               RangeTblEntry *rte = rt_fetch(rtr->rtindex, setOpQuery->rtable);
+               Query      *subquery = rte->subquery;
+
+               Assert(subquery != NULL);
+
+               /* Leaf nodes are OK if they match the toplevel column types */
+               return tlist_same_datatypes(subquery->targetList, colTypes, true);
+       }
+       else if (IsA(setOp, SetOperationStmt))
+       {
+               SetOperationStmt *op = (SetOperationStmt *) setOp;
+
+               /* Must be UNION ALL */
+               if (op->op != SETOP_UNION || !op->all)
+                       return false;
+
+               /* Recurse to check inputs */
+               return is_simple_union_all_recurse(op->larg, setOpQuery, colTypes) &&
+                       is_simple_union_all_recurse(op->rarg, setOpQuery, colTypes);
+       }
+       else
+       {
+               elog(ERROR, "unrecognized node type: %d",
+                        (int) nodeTag(setOp));
+               return false;                   /* keep compiler quiet */
+       }
+}
+
+/*
  * has_nullable_targetlist
  *       Check a subquery in the range table to see if all the non-junk
  *       targetlist items are simple variables or strict functions of simple
@@ -522,6 +807,62 @@ has_nullable_targetlist(Query *subquery)
 }
 
 /*
+ * is_safe_append_member
+ *       Check a subquery that is a leaf of a UNION ALL appendrel to see if it's
+ *       safe to pull up.
+ */
+static bool
+is_safe_append_member(Query *subquery)
+{
+       FromExpr   *jtnode;
+       ListCell   *l;
+
+       /*
+        * It's only safe to pull up the child if its jointree contains
+        * exactly one RTE, else the AppendRelInfo data structure breaks.
+        * The one base RTE could be buried in several levels of FromExpr,
+        * however.
+        *
+        * Also, the child can't have any WHERE quals because there's no
+        * place to put them in an appendrel.  (This is a bit annoying...)
+        * If we didn't need to check this, we'd just test whether
+        * get_relids_in_jointree() yields a singleton set, to be more
+        * consistent with the coding of fix_append_rel_relids().
+        */
+       jtnode = subquery->jointree;
+       while (IsA(jtnode, FromExpr))
+       {
+               if (jtnode->quals != NULL)
+                       return false;
+               if (list_length(jtnode->fromlist) != 1)
+                       return false;
+               jtnode = linitial(jtnode->fromlist);
+       }
+       if (!IsA(jtnode, RangeTblRef))
+               return false;
+
+       /*
+        * XXX For the moment we also have to insist that the subquery's tlist
+        * includes only simple Vars.  This is pretty annoying, but fixing it
+        * seems to require nontrivial changes --- mainly because joinrel
+        * tlists are presently assumed to contain only Vars.  Perhaps a
+        * pseudo-variable mechanism similar to the one speculated about
+        * in pull_up_subqueries' comments would help?  FIXME someday.
+        */
+       foreach(l, subquery->targetList)
+       {
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+               if (tle->resjunk)
+                       continue;
+               if (!(tle->expr && IsA(tle->expr, Var)))
+                       return false;
+       }
+
+       return true;
+}
+
+/*
  * Helper routine for pull_up_subqueries: do ResolveNew on every expression
  * in the jointree, without changing the jointree structure itself.  Ugly,
  * but there's no other way...
@@ -852,6 +1193,43 @@ fix_in_clause_relids(List *in_info_list, int varno, Relids subrelids)
 }
 
 /*
+ * fix_append_rel_relids: update RT-index fields of AppendRelInfo nodes
+ *
+ * When we pull up a subquery, any AppendRelInfo references to the subquery's
+ * RT index have to be replaced by the substituted relid (and there had better
+ * be only one).
+ *
+ * We assume we may modify the AppendRelInfo nodes in-place.
+ */
+static void
+fix_append_rel_relids(List *append_rel_list, int varno, Relids subrelids)
+{
+       ListCell   *l;
+       int                     subvarno = -1;
+
+       /*
+        * We only want to extract the member relid once, but we mustn't fail
+        * immediately if there are multiple members; it could be that none of
+        * the AppendRelInfo nodes refer to it.  So compute it on first use.
+        * Note that bms_singleton_member will complain if set is not singleton.
+        */
+       foreach(l, append_rel_list)
+       {
+               AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+
+               /* The parent_relid shouldn't ever be a pullup target */
+               Assert(appinfo->parent_relid != varno);
+
+               if (appinfo->child_relid == varno)
+               {
+                       if (subvarno < 0)
+                               subvarno = bms_singleton_member(subrelids);
+                       appinfo->child_relid = subvarno;
+               }
+       }
+}
+
+/*
  * get_relids_in_jointree: get set of base RT indexes present in a jointree
  */
 Relids
index a33213e..cc44825 100644 (file)
@@ -4,13 +4,17 @@
  *       Routines to plan set-operation queries.  The filename is a leftover
  *       from a time when only UNIONs were implemented.
  *
+ * There are two code paths in the planner for set-operation queries.
+ * If a subquery consists entirely of simple UNION ALL operations, it
+ * is converted into an "append relation".  Otherwise, it is handled
+ * by the general code in this module (plan_set_operations and its
+ * subroutines).  There is some support code here for the append-relation
+ * case, but most of the heavy lifting for that is done elsewhere,
+ * notably in prepjointree.c and allpaths.c.
+ *
  * There is also some code here to support planning of queries that use
- * inheritance (SELECT FROM foo*).  Although inheritance is radically
- * different from set operations as far as the parser representation of
- * a query is concerned, we try to handle it identically to the UNION ALL
- * case during planning: both are converted to "append rels".  (Note that
- * UNION ALL is special-cased: other kinds of set operations go through
- * a completely different code path.)
+ * inheritance (SELECT FROM foo*).  Inheritance trees are converted into
+ * append relations, and thenceforth share code with the UNION ALL case.
  *
  *
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
@@ -18,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.129 2006/01/31 21:39:24 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.130 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,12 +68,13 @@ static List *generate_setop_tlist(List *colTypes, int flag,
 static List *generate_append_tlist(List *colTypes, bool flag,
                                          List *input_plans,
                                          List *refnames_tlist);
-static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
 static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
                                                                         Index rti);
-static void make_translation_lists(Relation oldrelation, Relation newrelation,
-                                                                  Index newvarno,
-                                                                  List **col_mappings, List **translated_vars);
+static void make_inh_translation_lists(Relation oldrelation,
+                                                                          Relation newrelation,
+                                                                          Index newvarno,
+                                                                          List **col_mappings,
+                                                                          List **translated_vars);
 static Node *adjust_appendrel_attrs_mutator(Node *node,
                                                                                        AppendRelInfo *context);
 static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
@@ -659,41 +664,6 @@ generate_append_tlist(List *colTypes, bool flag,
        return tlist;
 }
 
-/*
- * Does tlist have same datatypes as requested colTypes?
- *
- * Resjunk columns are ignored if junkOK is true; otherwise presence of
- * a resjunk column will always cause a 'false' result.
- */
-static bool
-tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
-{
-       ListCell   *l;
-       ListCell   *curColType = list_head(colTypes);
-
-       foreach(l, tlist)
-       {
-               TargetEntry *tle = (TargetEntry *) lfirst(l);
-
-               if (tle->resjunk)
-               {
-                       if (!junkOK)
-                               return false;
-               }
-               else
-               {
-                       if (curColType == NULL)
-                               return false;
-                       if (exprType((Node *) tle->expr) != lfirst_oid(curColType))
-                               return false;
-                       curColType = lnext(curColType);
-               }
-       }
-       if (curColType != NULL)
-               return false;
-       return true;
-}
-
 
 /*
  * find_all_inheritors -
@@ -896,9 +866,9 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
                appinfo->child_relid = childRTindex;
                appinfo->parent_reltype = oldrelation->rd_rel->reltype;
                appinfo->child_reltype = newrelation->rd_rel->reltype;
-               make_translation_lists(oldrelation, newrelation, childRTindex,
-                                                          &appinfo->col_mappings,
-                                                          &appinfo->translated_vars);
+               make_inh_translation_lists(oldrelation, newrelation, childRTindex,
+                                                                  &appinfo->col_mappings,
+                                                                  &appinfo->translated_vars);
                appinfo->parent_reloid = parentOID;
                appinfos = lappend(appinfos, appinfo);
 
@@ -933,7 +903,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 }
 
 /*
- * make_translation_lists
+ * make_inh_translation_lists
  *       Build the lists of translations from parent Vars to child Vars for
  *       an inheritance child.  We need both a column number mapping list
  *       and a list of Vars representing the child columns.
@@ -941,9 +911,9 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
  * For paranoia's sake, we match type as well as attribute name.
  */
 static void
-make_translation_lists(Relation oldrelation, Relation newrelation,
-                                          Index newvarno,
-                                          List **col_mappings, List **translated_vars)
+make_inh_translation_lists(Relation oldrelation, Relation newrelation,
+                                                  Index newvarno,
+                                                  List **col_mappings, List **translated_vars)
 {
        List       *numbers = NIL;
        List       *vars = NIL;
@@ -1123,8 +1093,18 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
                                }
                                else
                                {
-                                       /* XXX copy some code from ResolveNew */
-                                       Assert(false);/* not done yet */
+                                       /*
+                                        * Build a RowExpr containing the translated variables.
+                                        */
+                                       RowExpr    *rowexpr;
+                                       List       *fields;
+
+                                       fields = (List *) copyObject(context->translated_vars);
+                                       rowexpr = makeNode(RowExpr);
+                                       rowexpr->args = fields;
+                                       rowexpr->row_typeid = var->vartype;
+                                       rowexpr->row_format = COERCE_IMPLICIT_CAST;
+                                       return (Node *) rowexpr;
                                }
                        }
                        /* system attributes don't need any other translation */
@@ -1338,45 +1318,6 @@ adjust_appendrel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
 }
 
 /*
- * adjust_other_rel_attr_needed
- *             Adjust an attr_needed[] array to reference a member rel instead of
- *             the original appendrel
- *
- * This is exactly like adjust_appendrel_attr_needed except that we disregard
- * appinfo->col_mappings and instead assume that the mapping of user
- * attributes is one-to-one.  This is appropriate for generating an attr_needed
- * array that describes another relation to be joined with a member rel.
- */
-Relids *
-adjust_other_rel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
-                                                        AttrNumber new_min_attr, AttrNumber new_max_attr)
-{
-       Relids     *new_attr_needed;
-       Index           parent_relid = appinfo->parent_relid;
-       Index           child_relid = appinfo->child_relid;
-       int                     parent_attr;
-
-       /* Create empty result array */
-       Assert(new_min_attr <= oldrel->min_attr);
-       Assert(new_max_attr >= oldrel->max_attr);
-       new_attr_needed = (Relids *)
-               palloc0((new_max_attr - new_min_attr + 1) * sizeof(Relids));
-       /* Process user attributes and system attributes */
-       for (parent_attr = oldrel->min_attr; parent_attr <= oldrel->max_attr;
-                parent_attr++)
-       {
-               Relids          attrneeded;
-
-               attrneeded = oldrel->attr_needed[parent_attr - oldrel->min_attr];
-               attrneeded = adjust_relid_set(attrneeded,
-                                                                         parent_relid, child_relid);
-               new_attr_needed[parent_attr - new_min_attr] = attrneeded;
-       }
-
-       return new_attr_needed;
-}
-
-/*
  * Adjust the targetlist entries of an inherited UPDATE operation
  *
  * The expressions have already been fixed, but we have to make sure that
index cedb808..ca258d5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.75 2006/01/31 21:39:24 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.76 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/plancat.h"
+#include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
 #include "parser/parsetree.h"
@@ -570,3 +571,144 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
                }
        }
 }
+
+/*
+ * translate_join_rel
+ *       Returns relation entry corresponding to the union of two given rels,
+ *       creating a new relation entry if none already exists.  This is used
+ *       when one of the inputs is an append child relation.  In addition to
+ *       data about the input rels themselves, the corresponding joinrel for
+ *       the append parent relation must be provided, plus the AppendRelInfo
+ *       showing the parent-to-child translation.
+ *
+ * The reason for having this code, instead of just applying build_join_rel,
+ * is that we must have corresponding tlist orderings for all joinrels that
+ * are involved in an Append plan.  So we generate the tlist for joinrels
+ * involving append child relations by translating the parent joinrel's tlist,
+ * rather than examining the input relations directly.  (Another reason for
+ * doing it this way is that the base relation attr_needed info in relations
+ * being joined to the appendrel doesn't refer to the append child rel, but
+ * the append parent, and so couldn't be used directly anyway.)  Otherwise
+ * this is exactly like build_join_rel.
+ */
+RelOptInfo *
+translate_join_rel(PlannerInfo *root,
+                                  RelOptInfo *oldjoinrel,
+                                  AppendRelInfo *appinfo,
+                                  RelOptInfo *outer_rel,
+                                  RelOptInfo *inner_rel,
+                                  JoinType jointype,
+                                  List **restrictlist_ptr)
+{
+       RelOptInfo *joinrel;
+       Relids          joinrelids;
+       List       *restrictlist;
+
+       /*
+        * Construct the Relids set for the translated joinrel, and see if
+        * we've already built it.
+        */
+       joinrelids = bms_copy(oldjoinrel->relids);
+       joinrelids = bms_del_member(joinrelids, appinfo->parent_relid);
+       joinrelids = bms_add_member(joinrelids, appinfo->child_relid);
+       joinrel = find_join_rel(root, joinrelids);
+       if (joinrel)
+       {
+               /*
+                * Yes, so we only need to figure the restrictlist for this particular
+                * pair of component relations.
+                */
+               bms_free(joinrelids);
+               if (restrictlist_ptr)
+                       *restrictlist_ptr = build_joinrel_restrictlist(root,
+                                                                                                                  joinrel,
+                                                                                                                  outer_rel,
+                                                                                                                  inner_rel,
+                                                                                                                  jointype);
+               return joinrel;
+       }
+
+       /*
+        * Nope, so make one.
+        */
+       joinrel = makeNode(RelOptInfo);
+       joinrel->reloptkind = RELOPT_JOINREL;
+       joinrel->relids = joinrelids;
+       joinrel->rows = 0;
+       joinrel->width = 0;
+       joinrel->reltargetlist = NIL;
+       joinrel->pathlist = NIL;
+       joinrel->cheapest_startup_path = NULL;
+       joinrel->cheapest_total_path = NULL;
+       joinrel->cheapest_unique_path = NULL;
+       joinrel->relid = 0;                     /* indicates not a baserel */
+       joinrel->rtekind = RTE_JOIN;
+       joinrel->min_attr = 0;
+       joinrel->max_attr = 0;
+       joinrel->attr_needed = NULL;
+       joinrel->attr_widths = NULL;
+       joinrel->indexlist = NIL;
+       joinrel->pages = 0;
+       joinrel->tuples = 0;
+       joinrel->subplan = NULL;
+       joinrel->baserestrictinfo = NIL;
+       joinrel->baserestrictcost.startup = 0;
+       joinrel->baserestrictcost.per_tuple = 0;
+       joinrel->joininfo = NIL;
+       joinrel->index_outer_relids = NULL;
+       joinrel->index_inner_paths = NIL;
+
+       /*
+        * Make the tlist by translating oldjoinrel's tlist, to ensure they
+        * are in compatible orders.  Since we don't call build_joinrel_tlist,
+        * we need another way to set the rel width; for the moment, just
+        * assume it is the same as oldjoinrel.  (The correct value may well be
+        * less, but it's not clear it's worth the trouble to get it right.)
+        */
+       joinrel->reltargetlist = (List *)
+               adjust_appendrel_attrs((Node *) oldjoinrel->reltargetlist,
+                                                          appinfo);
+       joinrel->width = oldjoinrel->width;
+
+       /*
+        * Construct restrict and join clause lists for the new joinrel. (The
+        * caller might or might not need the restrictlist, but I need it anyway
+        * for set_joinrel_size_estimates().)
+        */
+       restrictlist = build_joinrel_restrictlist(root,
+                                                                                         joinrel,
+                                                                                         outer_rel,
+                                                                                         inner_rel,
+                                                                                         jointype);
+       if (restrictlist_ptr)
+               *restrictlist_ptr = restrictlist;
+       build_joinrel_joinlist(joinrel, outer_rel, inner_rel);
+
+       /*
+        * Set estimates of the joinrel's size.
+        */
+       set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
+                                                          jointype, restrictlist);
+
+       /*
+        * Add the joinrel to the query's joinrel list, and store it into the
+        * auxiliary hashtable if there is one.  NB: GEQO requires us to append
+        * the new joinrel to the end of the list!
+        */
+       root->join_rel_list = lappend(root->join_rel_list, joinrel);
+
+       if (root->join_rel_hash)
+       {
+               JoinHashEntry *hentry;
+               bool            found;
+
+               hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
+                                                                                          &(joinrel->relids),
+                                                                                          HASH_ENTER,
+                                                                                          &found);
+               Assert(!found);
+               hentry->join_rel = joinrel;
+       }
+
+       return joinrel;
+}
index 955acee..6cfcc29 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.70 2005/10/15 02:49:21 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.71 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -167,3 +167,39 @@ get_sortgrouplist_exprs(List *sortClauses, List *targetList)
        }
        return result;
 }
+
+
+/*
+ * Does tlist have same output datatypes as listed in colTypes?
+ *
+ * Resjunk columns are ignored if junkOK is true; otherwise presence of
+ * a resjunk column will always cause a 'false' result.
+ */
+bool
+tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
+{
+       ListCell   *l;
+       ListCell   *curColType = list_head(colTypes);
+
+       foreach(l, tlist)
+       {
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+               if (tle->resjunk)
+               {
+                       if (!junkOK)
+                               return false;
+               }
+               else
+               {
+                       if (curColType == NULL)
+                               return false;   /* tlist longer than colTypes */
+                       if (exprType((Node *) tle->expr) != lfirst_oid(curColType))
+                               return false;
+                       curColType = lnext(curColType);
+               }
+       }
+       if (curColType != NULL)
+               return false;                   /* tlist shorter than colTypes */
+       return true;
+}
index 84d9f86..3ba612c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.64 2006/01/31 21:39:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.65 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -95,5 +95,12 @@ extern RelOptInfo *build_join_rel(PlannerInfo *root,
                           RelOptInfo *inner_rel,
                           JoinType jointype,
                           List **restrictlist_ptr);
+extern RelOptInfo *translate_join_rel(PlannerInfo *root,
+                                  RelOptInfo *oldjoinrel,
+                                  AppendRelInfo *appinfo,
+                                  RelOptInfo *outer_rel,
+                                  RelOptInfo *inner_rel,
+                                  JoinType jointype,
+                                  List **restrictlist_ptr);
 
 #endif   /* PATHNODE_H */
index ca28cbc..23b0700 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.54 2006/01/31 21:39:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.55 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,7 +23,7 @@
  */
 extern Node *pull_up_IN_clauses(PlannerInfo *root, Node *node);
 extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
-                                  bool below_outer_join);
+                                  bool below_outer_join, bool append_rel_member);
 extern void reduce_outer_joins(PlannerInfo *root);
 extern Relids get_relids_in_jointree(Node *jtnode);
 extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
@@ -55,9 +55,4 @@ extern Relids *adjust_appendrel_attr_needed(RelOptInfo *oldrel,
                                                                                        AttrNumber new_min_attr,
                                                                                        AttrNumber new_max_attr);
 
-extern Relids *adjust_other_rel_attr_needed(RelOptInfo *oldrel,
-                                                                                       AppendRelInfo *appinfo,
-                                                                                       AttrNumber new_min_attr,
-                                                                                       AttrNumber new_max_attr);
-
 #endif   /* PREP_H */
index 9141803..a282244 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/tlist.h,v 1.42 2005/04/06 16:34:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/tlist.h,v 1.43 2006/02/03 21:08:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,4 +29,6 @@ extern Node *get_sortgroupclause_expr(SortClause *sortClause,
 extern List *get_sortgrouplist_exprs(List *sortClauses,
                                                List *targetList);
 
+extern bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
+
 #endif   /* TLIST_H */