diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2016-06-17 21:44:37 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2016-06-17 21:44:37 -0400 |
commit | 915b703e169c38573591e69ae3939a7bf25e90d2 (patch) | |
tree | 3bd901b3baebf660b3214a83a97675c3233e8c0a /src/backend/parser/parse_agg.c | |
parent | d30d1acf904b823a7cca59e5d403ae3b299773e9 (diff) | |
download | postgresql-915b703e169c38573591e69ae3939a7bf25e90d2.tar.gz postgresql-915b703e169c38573591e69ae3939a7bf25e90d2.zip |
Fix handling of argument and result datatypes for partial aggregation.
When doing partial aggregation, the args list of the upper (combining)
Aggref node is replaced by a Var representing the output of the partial
aggregation steps, which has either the aggregate's transition data type
or a serialized representation of that. However, nodeAgg.c blindly
continued to use the args list as an indication of the user-level argument
types. This broke resolution of polymorphic transition datatypes at
executor startup (though it accidentally failed to fail for the ANYARRAY
case, which is likely the only one anyone had tested). Moreover, the
constructed FuncExpr passed to the finalfunc contained completely wrong
information, which would have led to bogus answers or crashes for any case
where the finalfunc examined that information (which is only likely to be
with polymorphic aggregates using a non-polymorphic transition type).
As an independent bug, apply_partialaggref_adjustment neglected to resolve
a polymorphic transition datatype before assigning it as the output type
of the lower-level Aggref node. This again accidentally failed to fail
for ANYARRAY but would be unlikely to work in other cases.
To fix the first problem, record the user-level argument types in a
separate OID-list field of Aggref, and look to that rather than the args
list when asking what the argument types were. (It turns out to be
convenient to include any "direct" arguments in this list too, although
those are not currently subject to being overwritten.)
Rather than adding yet another resolve_aggregate_transtype() call to fix
the second problem, add an aggtranstype field to Aggref, and store the
resolved transition type OID there when the planner first computes it.
(By doing this in the planner and not the parser, we can allow the
aggregate's transition type to change from time to time, although no DDL
support yet exists for that.) This saves nothing of consequence for
simple non-polymorphic aggregates, but for polymorphic transition types
we save a catalog lookup during executor startup as well as several
planner lookups that are new in 9.6 due to parallel query planning.
In passing, fix an error that was introduced into count_agg_clauses_walker
some time ago: it was applying exprTypmod() to something that wasn't an
expression node at all, but a TargetEntry. exprTypmod silently returned
-1 so that there was not an obvious failure, but this broke the intended
sensitivity of aggregate space consumption estimates to the typmod of
varchar and similar data types. This part needs to be back-patched.
Catversion bump due to change of stored Aggref nodes.
Discussion: <8229.1466109074@sss.pgh.pa.us>
Diffstat (limited to 'src/backend/parser/parse_agg.c')
-rw-r--r-- | src/backend/parser/parse_agg.c | 46 |
1 files changed, 22 insertions, 24 deletions
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 91bfe66c590..b9ca066698e 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -77,10 +77,10 @@ static List *expand_groupingset_node(GroupingSet *gs); * Finish initial transformation of an aggregate call * * parse_func.c has recognized the function as an aggregate, and has set up - * all the fields of the Aggref except aggdirectargs, args, aggorder, - * aggdistinct and agglevelsup. The passed-in args list has been through - * standard expression transformation and type coercion to match the agg's - * declared arg types, while the passed-in aggorder list hasn't been + * all the fields of the Aggref except aggargtypes, aggdirectargs, args, + * aggorder, aggdistinct and agglevelsup. The passed-in args list has been + * through standard expression transformation and type coercion to match the + * agg's declared arg types, while the passed-in aggorder list hasn't been * transformed at all. * * Here we separate the args list into direct and aggregated args, storing the @@ -101,6 +101,7 @@ void transformAggregateCall(ParseState *pstate, Aggref *agg, List *args, List *aggorder, bool agg_distinct) { + List *argtypes = NIL; List *tlist = NIL; List *torder = NIL; List *tdistinct = NIL; @@ -108,6 +109,18 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, int save_next_resno; ListCell *lc; + /* + * Before separating the args into direct and aggregated args, make a list + * of their data type OIDs for use later. + */ + foreach(lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + argtypes = lappend_oid(argtypes, exprType((Node *) arg)); + } + agg->aggargtypes = argtypes; + if (AGGKIND_IS_ORDERED_SET(agg->aggkind)) { /* @@ -1763,26 +1776,11 @@ get_aggregate_argtypes(Aggref *aggref, Oid *inputTypes) int numArguments = 0; ListCell *lc; - /* Any direct arguments of an ordered-set aggregate come first */ - foreach(lc, aggref->aggdirectargs) - { - Node *expr = (Node *) lfirst(lc); - - inputTypes[numArguments] = exprType(expr); - numArguments++; - } + Assert(list_length(aggref->aggargtypes) <= FUNC_MAX_ARGS); - /* Now get the regular (aggregated) arguments */ - foreach(lc, aggref->args) + foreach(lc, aggref->aggargtypes) { - TargetEntry *tle = (TargetEntry *) lfirst(lc); - - /* Ignore ordering columns of a plain aggregate */ - if (tle->resjunk) - continue; - - inputTypes[numArguments] = exprType((Node *) tle->expr); - numArguments++; + inputTypes[numArguments++] = lfirst_oid(lc); } return numArguments; @@ -1795,8 +1793,8 @@ get_aggregate_argtypes(Aggref *aggref, Oid *inputTypes) * This function resolves a polymorphic aggregate's state datatype. * It must be passed the aggtranstype from the aggregate's catalog entry, * as well as the actual argument types extracted by get_aggregate_argtypes. - * (We could fetch these values internally, but for all existing callers that - * would just duplicate work the caller has to do too, so we pass them in.) + * (We could fetch pg_aggregate.aggtranstype internally, but all existing + * callers already have the value at hand, so we make them pass it.) */ Oid resolve_aggregate_transtype(Oid aggfuncid, |