summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/prep/prepjointree.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2012-03-24 16:21:48 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2012-03-24 16:21:48 -0400
commit811a2cbc167bfef4e5a88d468d5258385318ad87 (patch)
treeb5183ea3ed8610ef888dd297fbab76ed9f5bd221 /src/backend/optimizer/prep/prepjointree.c
parenteca0c389f185b6bd76b4ca77195aa47d2e90dbb2 (diff)
Fix planner's handling of outer PlaceHolderVars within subqueries.
For some reason, in the original coding of the PlaceHolderVar mechanism I had supposed that PlaceHolderVars couldn't propagate into subqueries. That is of course entirely possible. When it happens, we need to treat an outer-level PlaceHolderVar much like an outer Var or Aggref, that is SS_replace_correlation_vars() needs to replace the PlaceHolderVar with a Param, and then when building the finished SubPlan we have to provide the PlaceHolderVar expression as an actual parameter for the SubPlan. The handling of the contained expression is a bit delicate but it can be treated exactly like an Aggref's expression. In addition to the missing logic in subselect.c, prepjointree.c was failing to search subqueries for PlaceHolderVars that need their relids adjusted during subquery pullup. It looks like everyplace else that touches PlaceHolderVars got it right, though. Per report from Mark Murawski. In 9.1 and HEAD, queries affected by this oversight would fail with "ERROR: Upper-level PlaceHolderVar found where not expected". But in 9.0 and 8.4, you'd silently get possibly-wrong answers, since the value transmitted into the subquery wouldn't go to null when it should.
Diffstat (limited to 'src/backend/optimizer/prep/prepjointree.c')
-rw-r--r--src/backend/optimizer/prep/prepjointree.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 9e6cf09dd1a..ad0580b7723 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1984,8 +1984,6 @@ reduce_outer_joins_pass2(Node *jtnode,
*
* Find any PlaceHolderVar 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
@@ -1996,6 +1994,7 @@ reduce_outer_joins_pass2(Node *jtnode,
typedef struct
{
int varno;
+ int sublevels_up;
Relids subrelids;
} substitute_multiple_relids_context;
@@ -2009,7 +2008,8 @@ substitute_multiple_relids_walker(Node *node,
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
- if (bms_is_member(context->varno, phv->phrels))
+ if (phv->phlevelsup == context->sublevels_up &&
+ bms_is_member(context->varno, phv->phrels))
{
phv->phrels = bms_union(phv->phrels,
context->subrelids);
@@ -2018,6 +2018,18 @@ substitute_multiple_relids_walker(Node *node,
}
/* fall through to examine children */
}
+ if (IsA(node, Query))
+ {
+ /* Recurse into subselects */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node,
+ substitute_multiple_relids_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
/* Shouldn't need to handle planner auxiliary nodes here */
Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo));
@@ -2034,6 +2046,7 @@ substitute_multiple_relids(Node *node, int varno, Relids subrelids)
substitute_multiple_relids_context context;
context.varno = varno;
+ context.sublevels_up = 0;
context.subrelids = subrelids;
/*