1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-12 21:01:52 +03:00

Avoid listing ungrouped Vars in the targetlist of Agg-underneath-Window.

Regular aggregate functions in combination with, or within the arguments
of, window functions are OK per spec; they have the semantics that the
aggregate output rows are computed and then we run the window functions
over that row set.  (Thus, this combination is not really useful unless
there's a GROUP BY so that more than one aggregate output row is possible.)
The case without GROUP BY could fail, as recently reported by Jeff Davis,
because sloppy construction of the Agg node's targetlist resulted in extra
references to possibly-ungrouped Vars appearing outside the aggregate
function calls themselves.  See the added regression test case for an
example.

Fixing this requires modifying the API of flatten_tlist and its underlying
function pull_var_clause.  I chose to make pull_var_clause's API for
aggregates identical to what it was already doing for placeholders, since
the useful behaviors turn out to be the same (error, report node as-is, or
recurse into it).  I also tightened the error checking in this area a bit:
if it was ever valid to see an uplevel Var, Aggref, or PlaceHolderVar here,
that was a long time ago, so complain instead of ignoring them.

Backpatch into 9.1.  The failure exists in 8.4 and 9.0 as well, but seeing
that it only occurs in a basically-useless corner case, it doesn't seem
worth the risks of changing a function API in a minor release.  There might
be third-party code using pull_var_clause.
This commit is contained in:
Tom Lane
2011-07-12 18:23:55 -04:00
parent 846af54dd5
commit c1d9579dd8
18 changed files with 119 additions and 78 deletions

View File

@ -1474,11 +1474,16 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
* step. That's handled internally by make_sort_from_pathkeys,
* but we need the copyObject steps here to ensure that each plan
* node has a separately modifiable tlist.
*
* Note: it's essential here to use PVC_INCLUDE_AGGREGATES so that
* Vars mentioned only in aggregate expressions aren't pulled out
* as separate targetlist entries. Otherwise we could be putting
* ungrouped Vars directly into an Agg node's tlist, resulting in
* undefined behavior.
*/
window_tlist = flatten_tlist(tlist);
if (parse->hasAggs)
window_tlist = add_to_flat_tlist(window_tlist,
pull_agg_clause((Node *) tlist));
window_tlist = flatten_tlist(tlist,
PVC_INCLUDE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
window_tlist = add_volatile_sort_exprs(window_tlist, tlist,
activeWindows);
result_plan->targetlist = (List *) copyObject(window_tlist);
@ -2577,14 +2582,18 @@ make_subplanTargetList(PlannerInfo *root,
}
/*
* Otherwise, start with a "flattened" tlist (having just the vars
* mentioned in the targetlist and HAVING qual --- but not upper-level
* Vars; they will be replaced by Params later on). Note this includes
* vars used in resjunk items, so we are covering the needs of ORDER BY
* and window specifications.
* Otherwise, start with a "flattened" tlist (having just the Vars
* mentioned in the targetlist and HAVING qual). Note this includes Vars
* used in resjunk items, so we are covering the needs of ORDER BY and
* window specifications. Vars used within Aggrefs will be pulled out
* here, too.
*/
sub_tlist = flatten_tlist(tlist);
extravars = pull_var_clause(parse->havingQual, PVC_INCLUDE_PLACEHOLDERS);
sub_tlist = flatten_tlist(tlist,
PVC_RECURSE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
extravars = pull_var_clause(parse->havingQual,
PVC_RECURSE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
sub_tlist = add_to_flat_tlist(sub_tlist, extravars);
list_free(extravars);
*need_tlist_eval = false; /* only eval if not flat tlist */