aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/path/allpaths.c80
-rw-r--r--src/backend/optimizer/path/joinpath.c29
-rw-r--r--src/backend/optimizer/plan/planner.c4
-rw-r--r--src/backend/optimizer/prep/prepjointree.c830
-rw-r--r--src/backend/optimizer/prep/prepunion.c129
-rw-r--r--src/backend/optimizer/util/relnode.c144
-rw-r--r--src/backend/optimizer/util/tlist.c38
-rw-r--r--src/include/optimizer/pathnode.h9
-rw-r--r--src/include/optimizer/prep.h9
-rw-r--r--src/include/optimizer/tlist.h4
10 files changed, 888 insertions, 388 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 90add90e4e9..890bb59a12f 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -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
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 137be08e753..df3bd128747 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -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,
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index e007e4e594e..e530a1ac6eb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 546c4bd275d..a955c7365e9 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -15,16 +15,19 @@
*
*
* 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
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index a33213ef59d..cc44825be00 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -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
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index cedb8082271..ca258d5381a 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -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;
+}
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 955aceeffff..6cfcc2949a0 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -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;
+}
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 84d9f865e37..3ba612c1956 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -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 */
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index ca28cbc886f..23b07006eec 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -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 */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index 91418033ae1..a282244f962 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -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 */