aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2013-11-22 14:37:31 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2013-11-22 14:37:31 -0500
commit92a752151fa9c279595ab6f896534e49a5625920 (patch)
tree95dd34c71d376923a3ba04a111107d0e04e4bce7 /src/backend
parent673019936866652bfb8ac399fac6bc21fa6810fe (diff)
downloadpostgresql-92a752151fa9c279595ab6f896534e49a5625920.tar.gz
postgresql-92a752151fa9c279595ab6f896534e49a5625920.zip
Flatten join alias Vars before pulling up targetlist items from a subquery.
pullup_replace_vars()'s decisions about whether a pulled-up replacement expression needs to be wrapped in a PlaceHolderVar depend on the assumption that what looks like a Var behaves like a Var. However, if the Var is a join alias reference, later flattening of join aliases might replace the Var with something that's not a Var at all, and should have been wrapped. To fix, do a forcible pass of flatten_join_alias_vars() on the subquery targetlist before we start to copy items out of it. We'll re-run that processing on the pulled-up expressions later, but that's harmless. Per report from Ken Tanzer; the added regression test case is based on his example. This bug has been there since the PlaceHolderVar mechanism was invented, but has escaped detection because the circumstances that trigger it are fairly narrow. You need a flattenable query underneath an outer join, which contains another flattenable query inside a join of its own, with a dangerous expression (a constant or something else non-strict) in that one's targetlist. Having seen this, I'm wondering if it wouldn't be prudent to do all alias-variable flattening earlier, perhaps even in the rewriter. But that would probably not be a back-patchable change.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/optimizer/prep/prepjointree.c12
-rw-r--r--src/backend/optimizer/util/var.c8
2 files changed, 14 insertions, 6 deletions
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 1750a5e3e1f..a7a7574af64 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -732,6 +732,18 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
}
/*
+ * We must flatten any join alias Vars in the subquery's targetlist,
+ * because pulling up the subquery's subqueries might have changed their
+ * expansions into arbitrary expressions, which could affect
+ * pullup_replace_vars' decisions about whether PlaceHolderVar wrappers
+ * are needed for tlist entries. (Likely it'd be better to do
+ * flatten_join_alias_vars on the whole query tree at some earlier stage,
+ * maybe even in the rewriter; but for now let's just fix this case here.)
+ */
+ subquery->targetList = (List *)
+ flatten_join_alias_vars(subroot, (Node *) subquery->targetList);
+
+ /*
* 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.
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 96bf733dd4d..e5fa48d3644 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -779,16 +779,14 @@ flatten_join_alias_vars_mutator(Node *node,
/* Ignore dropped columns */
if (newvar == NULL)
continue;
+ newvar = copyObject(newvar);
/*
* If we are expanding an alias carried down from an upper
* query, must adjust its varlevelsup fields.
*/
if (context->sublevels_up != 0)
- {
- newvar = copyObject(newvar);
IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
- }
/* Recurse in case join input is itself a join */
/* (also takes care of setting inserted_sublink if needed) */
newvar = flatten_join_alias_vars_mutator(newvar, context);
@@ -808,16 +806,14 @@ flatten_join_alias_vars_mutator(Node *node,
Assert(var->varattno > 0);
newvar = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1);
Assert(newvar != NULL);
+ newvar = copyObject(newvar);
/*
* If we are expanding an alias carried down from an upper query, must
* adjust its varlevelsup fields.
*/
if (context->sublevels_up != 0)
- {
- newvar = copyObject(newvar);
IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
- }
/* Recurse in case join input is itself a join */
newvar = flatten_join_alias_vars_mutator(newvar, context);