Teach planner to convert simple UNION ALL subqueries into append relations,
authorTom Lane
Fri, 3 Feb 2006 21:08:49 +0000 (21:08 +0000)
committerTom Lane
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 90add90e4e98c447fb8ff02bf1287c238b5f8673..890bb59a12f1c0cc98cc582c2b1a937ccd0947d0 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 137be08e75384327e28c003f1c54993112a079c2..df3bd128747aa99107af946b5d4ec457e44960f4 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 e007e4e594e027ce0eb9820e3356b15363879f4f..e530a1ac6ebda35c1b3243c94de3b9006c61b993 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 546c4bd275ded9943f8a0a1895bf4f1a629fbeb1..a955c7365e90b938480ca14bf5d59a74cb014770 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:
 
@@ -425,6 +264,379 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
    return jtnode;
 }
 
+/*
+ * 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
@@ -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)
@@ -484,6 +697,78 @@ is_simple_subquery(Query *subquery)
    return true;
 }
 
+/*
+ * 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
@@ -521,6 +806,62 @@ has_nullable_targetlist(Query *subquery)
    return true;
 }
 
+/*
+ * 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,
@@ -851,6 +1192,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
  */
index a33213ef59d27450d0c7e49de199278fcc3d980c..cc44825be008972063e07ba0d80df9b7c8c031f8 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 */
@@ -1337,45 +1317,6 @@ adjust_appendrel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
    return new_attr_needed;
 }
 
-/*
- * 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
  *
index cedb8082271b5215259f704d0981c9163f41146e..ca258d5381af4b4ddcb04c7de1460cb87694da8e 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 955aceeffff815a3c35681a654f9bd971ee86afd..6cfcc2949a02d84d0df76e68cd648cd4d56a7fb8 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 84d9f865e37ee2a82c77f02c22409aad3dcd30ba..3ba612c1956fe1d49ed855e95efab292d5996bee 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 ca28cbc886f32d334e5b9fd7986cf11395533245..23b07006eec5636517177334322168e2866dd716 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 91418033ae1c00892452434422511b81f9483203..a282244f962007e32e01b07cf87a2de8a4836362 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 */