aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/initsplan.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2010-09-28 14:15:42 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2010-09-28 14:15:42 -0400
commitdc9cc887b74bfa0d40829c4df66dead509fdd8f6 (patch)
tree90e39f8b5f06e7acfd07bd7b1ba525f1af41643b /src/backend/optimizer/plan/initsplan.c
parent2dc2ea81f67b96473cefe8fc7d515bda37255660 (diff)
downloadpostgresql-dc9cc887b74bfa0d40829c4df66dead509fdd8f6.tar.gz
postgresql-dc9cc887b74bfa0d40829c4df66dead509fdd8f6.zip
Fix PlaceHolderVar mechanism's interaction with outer joins.
The point of a PlaceHolderVar is to allow a non-strict expression to be evaluated below an outer join, after which its value bubbles up like a Var and can be forced to NULL when the outer join's semantics require that. However, there was a serious design oversight in that, namely that we didn't ensure that there was actually a correct place in the plan tree to evaluate the placeholder :-(. It may be necessary to delay evaluation of an outer join to ensure that a placeholder that should be evaluated below the join can be evaluated there. Per recent bug report from Kirill Simonov. Back-patch to 8.4 where the PlaceHolderVar mechanism was introduced.
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r--src/backend/optimizer/plan/initsplan.c37
1 files changed, 37 insertions, 0 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 626b4217228..0ddf672e1e1 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -187,6 +187,13 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed)
phinfo->ph_needed = bms_add_members(phinfo->ph_needed,
where_needed);
+ /*
+ * Update ph_may_need too. This is currently only necessary
+ * when being called from build_base_rel_tlists, but we may as
+ * well do it always.
+ */
+ phinfo->ph_may_need = bms_add_members(phinfo->ph_may_need,
+ where_needed);
}
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
@@ -465,7 +472,11 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
/* Now we can add the SpecialJoinInfo to join_info_list */
if (sjinfo)
+ {
root->join_info_list = lappend(root->join_info_list, sjinfo);
+ /* Each time we do that, recheck placeholder eval levels */
+ update_placeholder_eval_levels(root, sjinfo);
+ }
/*
* Finally, compute the output joinlist. We fold subproblems together
@@ -685,6 +696,32 @@ make_outerjoininfo(PlannerInfo *root,
}
/*
+ * Examine PlaceHolderVars. If a PHV is supposed to be evaluated within
+ * this join's nullable side, and it may get used above this join, then
+ * ensure that min_righthand contains the full eval_at set of the PHV.
+ * This ensures that the PHV actually can be evaluated within the RHS.
+ * Note that this works only because we should already have determined
+ * the final eval_at level for any PHV syntactically within this join.
+ */
+ foreach(l, root->placeholder_list)
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
+ Relids ph_syn_level = phinfo->ph_var->phrels;
+
+ /* Ignore placeholder if it didn't syntactically come from RHS */
+ if (!bms_is_subset(ph_syn_level, right_rels))
+ continue;
+
+ /* We can also ignore it if it's certainly not used above this join */
+ /* XXX this test is probably overly conservative */
+ if (bms_is_subset(phinfo->ph_may_need, min_righthand))
+ continue;
+
+ /* Else, prevent join from being formed before we eval the PHV */
+ min_righthand = bms_add_members(min_righthand, phinfo->ph_eval_at);
+ }
+
+ /*
* If we found nothing to put in min_lefthand, punt and make it the full
* LHS, to avoid having an empty min_lefthand which will confuse later
* processing. (We don't try to be smart about such cases, just correct.)