summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/prep/prepjointree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/prep/prepjointree.c')
-rw-r--r--src/backend/optimizer/prep/prepjointree.c248
1 files changed, 153 insertions, 95 deletions
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 1c73c57dadf..45de5096198 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -16,7 +16,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.56 2008/10/09 19:27:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.57 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,6 +25,7 @@
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/placeholder.h"
#include "optimizer/prep.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
@@ -60,7 +61,8 @@ 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 List *insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
+ int varno, bool wrap_non_vars);
static bool is_safe_append_member(Query *subquery);
static void resolvenew_in_jointree(Node *jtnode, int varno,
RangeTblEntry *rte, List *subtlist);
@@ -71,8 +73,8 @@ static void reduce_outer_joins_pass2(Node *jtnode,
Relids nonnullable_rels,
List *nonnullable_vars,
List *forced_null_vars);
-static void fix_flattened_sublink_relids(Node *node,
- int varno, Relids subrelids);
+static void substitute_multiple_relids(Node *node,
+ 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);
@@ -406,11 +408,13 @@ inline_set_returning_functions(PlannerInfo *root)
* 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.
+ * side of an outer join. This forces use of the PlaceHolderVar mechanism
+ * for non-nullable targetlist items.
*
* 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.
+ * an append relation. This forces use of the PlaceHolderVar mechanism
+ * for all non-Var targetlist items, and puts some additional restrictions
+ * on what can be pulled up.
*
* 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
@@ -434,24 +438,13 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
/*
* Is this a subquery RTE, and if so, is the subquery simple enough to
- * pull up? (If not, do nothing at this node.)
- *
- * If we are inside an outer join, only pull up subqueries whose
- * targetlists are nullable --- otherwise substituting their tlist
- * entries for upper Var references would do the wrong thing (the
- * results wouldn't become NULL when they're supposed to).
- *
- * XXX This could be improved by generating pseudo-variables for such
- * 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.
+ * pull up?
*
* 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(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,
@@ -459,12 +452,9 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
/*
* 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.
+ * into an "append relation".
*
- * It's also safe to do this regardless of whether this query is
+ * It's 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.)
@@ -472,6 +462,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
if (rte->rtekind == RTE_SUBQUERY &&
is_simple_union_all(rte->subquery))
return pull_up_simple_union_all(root, jtnode, rte);
+
+ /* Otherwise, do nothing at this node. */
}
else if (IsA(jtnode, FromExpr))
{
@@ -573,6 +565,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->cte_plan_ids = NIL;
subroot->eq_classes = NIL;
subroot->append_rel_list = NIL;
+ subroot->placeholder_list = NIL;
subroot->hasRecursion = false;
subroot->wt_param_id = -1;
subroot->non_recursive_plan = NULL;
@@ -614,7 +607,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* 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 */
@@ -635,11 +627,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
/*
* Adjust level-0 varnos in subquery so that we can append its rangetable
* to upper query's. We have to fix the subquery's append_rel_list
- * as well.
+ * and placeholder_list as well.
*/
rtoffset = list_length(parse->rtable);
OffsetVarNodes((Node *) subquery, rtoffset, 0);
OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
+ OffsetVarNodes((Node *) subroot->placeholder_list, rtoffset, 0);
/*
* Upper-level vars in subquery are now one level closer to their parent
@@ -647,6 +640,20 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
*/
IncrementVarSublevelsUp((Node *) subquery, -1, 1);
IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
+ IncrementVarSublevelsUp((Node *) subroot->placeholder_list, -1, 1);
+
+ /*
+ * The subquery's targetlist items are now in the appropriate form to
+ * insert into the top query, but if we are under an outer join then
+ * non-nullable items have to be turned into PlaceHolderVars. If we
+ * are dealing with an appendrel member then anything that's not a
+ * simple Var has to be turned into a PlaceHolderVar.
+ */
+ if (below_outer_join || append_rel_member)
+ subtlist = insert_targetlist_placeholders(root, subquery->targetList,
+ varno, append_rel_member);
+ else
+ subtlist = subquery->targetList;
/*
* Replace all of the top query's references to the subquery's outputs
@@ -654,7 +661,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* 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,
@@ -700,29 +706,41 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
/*
- * We also have to fix the relid sets of any FlattenedSubLink nodes in
- * the parent query. (This could perhaps be done by ResolveNew, but it
- * would clutter that routine's API unreasonably.)
+ * We also have to fix the relid sets of any FlattenedSubLink,
+ * PlaceHolderVar, and PlaceHolderInfo nodes in the parent query.
+ * (This could perhaps be done by ResolveNew, but it would clutter that
+ * routine's API unreasonably.) Note in particular that any placeholder
+ * nodes just created by insert_targetlist_placeholders() wiil be adjusted.
*
* 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 (parse->hasSubLinks || root->append_rel_list)
+ if (parse->hasSubLinks || root->placeholder_list || root->append_rel_list)
{
Relids subrelids;
subrelids = get_relids_in_jointree((Node *) subquery->jointree, false);
- fix_flattened_sublink_relids((Node *) parse, varno, subrelids);
- fix_append_rel_relids(root->append_rel_list, varno, subrelids);
+ substitute_multiple_relids((Node *) parse,
+ varno, subrelids);
+ substitute_multiple_relids((Node *) root->placeholder_list,
+ varno, subrelids);
+ fix_append_rel_relids(root->append_rel_list,
+ varno, subrelids);
}
/*
- * And now add subquery's AppendRelInfos to our list.
+ * And now add subquery's AppendRelInfos and PlaceHolderInfos to our lists.
+ * Note that any placeholders pulled up from the subquery will appear
+ * after any we just created; this preserves the property that placeholders
+ * can only refer to other placeholders that appear later in the list
+ * (needed by fix_placeholder_eval_levels).
*/
root->append_rel_list = list_concat(root->append_rel_list,
subroot->append_rel_list);
+ root->placeholder_list = list_concat(root->placeholder_list,
+ subroot->placeholder_list);
/*
* We don't have to do the equivalent bookkeeping for outer-join info,
@@ -950,7 +968,9 @@ is_simple_subquery(Query *subquery)
* Don't pull up a subquery that has any volatile functions in its
* targetlist. Otherwise we might introduce multiple evaluations of these
* functions, if they get copied to multiple places in the upper query,
- * leading to surprising results.
+ * leading to surprising results. (Note: the PlaceHolderVar mechanism
+ * doesn't quite guarantee single evaluation; else we could pull up anyway
+ * and just wrap such items in PlaceHolderVars ...)
*/
if (contain_volatile_functions((Node *) subquery->targetList))
return false;
@@ -959,8 +979,12 @@ is_simple_subquery(Query *subquery)
* Hack: don't try to pull up a subquery with an empty jointree.
* query_planner() will correctly generate a Result plan for a jointree
* that's totally empty, but I don't think the right things happen if an
- * empty FromExpr appears lower down in a jointree. Not worth working hard
- * on this, just to collapse SubqueryScan/Result into Result...
+ * empty FromExpr appears lower down in a jointree. It would pose a
+ * problem for the PlaceHolderVar mechanism too, since we'd have no
+ * way to identify where to evaluate a PHV coming out of the subquery.
+ * Not worth working hard on this, just to collapse SubqueryScan/Result
+ * into Result; especially since the SubqueryScan can often be optimized
+ * away by setrefs.c anyway.
*/
if (subquery->jointree->fromlist == NIL)
return false;
@@ -1043,40 +1067,59 @@ is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
}
/*
- * 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
- * variables (and, hence, will correctly go to NULL when examined above
- * the point of an outer join).
+ * insert_targetlist_placeholders
+ * Insert PlaceHolderVar nodes into any non-junk targetlist items that are
+ * not simple variables or strict functions of simple variables (and hence
+ * might not correctly go to NULL when examined above the point of an outer
+ * join). We assume we can modify the tlist items in-place.
*
- * NOTE: it would be correct (and useful) to ignore output columns that aren't
- * actually referenced by the enclosing query ... but we do not have that
- * information available at this point.
+ * varno is the upper-query relid of the subquery; this is used as the
+ * syntactic location of the PlaceHolderVars.
+ * If wrap_non_vars is true then *only* simple Var references escape being
+ * wrapped with PlaceHolderVars.
*/
-static bool
-has_nullable_targetlist(Query *subquery)
+static List *
+insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
+ int varno, bool wrap_non_vars)
{
- ListCell *l;
+ ListCell *lc;
- foreach(l, subquery->targetList)
+ foreach(lc, tlist)
{
- TargetEntry *tle = (TargetEntry *) lfirst(l);
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
/* ignore resjunk columns */
if (tle->resjunk)
continue;
- /* Must contain a Var of current level */
- if (!contain_vars_of_level((Node *) tle->expr, 0))
- return false;
+ /*
+ * Simple Vars always escape being wrapped. This is common enough
+ * to deserve a fast path even if we aren't doing wrap_non_vars.
+ */
+ if (tle->expr && IsA(tle->expr, Var) &&
+ ((Var *) tle->expr)->varlevelsup == 0)
+ continue;
- /* Must not contain any non-strict constructs */
- if (contain_nonstrict_functions((Node *) tle->expr))
- return false;
+ if (!wrap_non_vars)
+ {
+ /*
+ * If it contains a Var of current level, and does not contain
+ * any non-strict constructs, then it's certainly nullable and we
+ * don't need to insert a PlaceHolderVar. (Note: in future maybe
+ * we should insert PlaceHolderVars anyway, when a tlist item is
+ * expensive to evaluate?
+ */
+ if (contain_vars_of_level((Node *) tle->expr, 0) &&
+ !contain_nonstrict_functions((Node *) tle->expr))
+ continue;
+ }
- /* This one's OK, keep scanning */
+ /* Else wrap it in a PlaceHolderVar */
+ tle->expr = (Expr *) make_placeholder_expr(root,
+ tle->expr,
+ bms_make_singleton(varno));
}
- return true;
+ return tlist;
}
/*
@@ -1088,7 +1131,6 @@ 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
@@ -1113,24 +1155,6 @@ is_safe_append_member(Query *subquery)
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;
}
@@ -1579,28 +1603,29 @@ reduce_outer_joins_pass2(Node *jtnode,
}
/*
- * fix_flattened_sublink_relids - adjust FlattenedSubLink nodes after
- * pulling up a subquery
+ * substitute_multiple_relids - adjust node relid sets after pulling up
+ * a subquery
*
- * Find any FlattenedSubLink nodes in the given tree that reference the
- * pulled-up relid, and change them to reference the replacement relid(s).
- * We do not need to recurse into subqueries, since no subquery of the
- * current top query could contain such a reference.
+ * Find any FlattenedSubLink, PlaceHolderVar, or PlaceHolderInfo nodes in the
+ * given tree that reference the pulled-up relid, and change them to reference
+ * the replacement relid(s). We do not need to recurse into subqueries, since
+ * no subquery of the current top query could (yet) contain such a reference.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* nodes in-place. This should be OK since the tree was copied by ResolveNew
- * earlier.
+ * earlier. Avoid scribbling on the original values of the bitmapsets, though,
+ * because expression_tree_mutator doesn't copy those.
*/
typedef struct
{
int varno;
Relids subrelids;
-} fix_flattened_sublink_relids_context;
+} substitute_multiple_relids_context;
static bool
-fix_flattened_sublink_relids_walker(Node *node,
- fix_flattened_sublink_relids_context *context)
+substitute_multiple_relids_walker(Node *node,
+ substitute_multiple_relids_context *context)
{
if (node == NULL)
return false;
@@ -1610,28 +1635,61 @@ fix_flattened_sublink_relids_walker(Node *node,
if (bms_is_member(context->varno, fslink->lefthand))
{
+ fslink->lefthand = bms_union(fslink->lefthand,
+ context->subrelids);
fslink->lefthand = bms_del_member(fslink->lefthand,
context->varno);
- fslink->lefthand = bms_add_members(fslink->lefthand,
- context->subrelids);
}
if (bms_is_member(context->varno, fslink->righthand))
{
+ fslink->righthand = bms_union(fslink->righthand,
+ context->subrelids);
fslink->righthand = bms_del_member(fslink->righthand,
context->varno);
- fslink->righthand = bms_add_members(fslink->righthand,
- context->subrelids);
}
/* fall through to examine children */
}
- return expression_tree_walker(node, fix_flattened_sublink_relids_walker,
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ if (bms_is_member(context->varno, phv->phrels))
+ {
+ phv->phrels = bms_union(phv->phrels,
+ context->subrelids);
+ phv->phrels = bms_del_member(phv->phrels,
+ context->varno);
+ }
+ /* fall through to examine children */
+ }
+ if (IsA(node, PlaceHolderInfo))
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+
+ if (bms_is_member(context->varno, phinfo->ph_eval_at))
+ {
+ phinfo->ph_eval_at = bms_union(phinfo->ph_eval_at,
+ context->subrelids);
+ phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at,
+ context->varno);
+ }
+ if (bms_is_member(context->varno, phinfo->ph_needed))
+ {
+ phinfo->ph_needed = bms_union(phinfo->ph_needed,
+ context->subrelids);
+ phinfo->ph_needed = bms_del_member(phinfo->ph_needed,
+ context->varno);
+ }
+ /* fall through to examine children */
+ }
+ return expression_tree_walker(node, substitute_multiple_relids_walker,
(void *) context);
}
static void
-fix_flattened_sublink_relids(Node *node, int varno, Relids subrelids)
+substitute_multiple_relids(Node *node, int varno, Relids subrelids)
{
- fix_flattened_sublink_relids_context context;
+ substitute_multiple_relids_context context;
context.varno = varno;
context.subrelids = subrelids;
@@ -1640,7 +1698,7 @@ fix_flattened_sublink_relids(Node *node, int varno, Relids subrelids)
* Must be prepared to start with a Query or a bare expression tree.
*/
query_or_expression_tree_walker(node,
- fix_flattened_sublink_relids_walker,
+ substitute_multiple_relids_walker,
(void *) &context,
0);
}