diff options
Diffstat (limited to 'src/backend/optimizer/util/clauses.c')
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 54 |
1 files changed, 35 insertions, 19 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 8d31204b621..0e738c1ccc0 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -523,7 +523,7 @@ contain_agg_clause_walker(Node *node, void *context) /* * count_agg_clauses * Recursively count the Aggref nodes in an expression tree, and - * accumulate other cost information about them too. + * accumulate other information about them too. * * Note: this also checks for nested aggregates, which are an error. * @@ -532,6 +532,10 @@ contain_agg_clause_walker(Node *node, void *context) * values if all are evaluated in parallel (as would be done in a HashAgg * plan). See AggClauseCosts for the exact set of statistics collected. * + * In addition, we mark Aggref nodes with the correct aggtranstype, so + * that that doesn't need to be done repeatedly. (That makes this function's + * name a bit of a misnomer.) + * * NOTE that the counts/costs are ADDED to those already in *costs ... so * the caller is responsible for zeroing the struct initially. * @@ -572,8 +576,6 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context) Oid aggtranstype; int32 aggtransspace; QualCost argcosts; - Oid inputTypes[FUNC_MAX_ARGS]; - int numArguments; Assert(aggref->agglevelsup == 0); @@ -597,6 +599,28 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context) aggtransspace = aggform->aggtransspace; ReleaseSysCache(aggTuple); + /* + * Resolve the possibly-polymorphic aggregate transition type, unless + * already done in a previous pass over the expression. + */ + if (OidIsValid(aggref->aggtranstype)) + aggtranstype = aggref->aggtranstype; + else + { + Oid inputTypes[FUNC_MAX_ARGS]; + int numArguments; + + /* extract argument types (ignoring any ORDER BY expressions) */ + numArguments = get_aggregate_argtypes(aggref, inputTypes); + + /* resolve actual type of transition state, if polymorphic */ + aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid, + aggtranstype, + inputTypes, + numArguments); + aggref->aggtranstype = aggtranstype; + } + /* count it; note ordered-set aggs always have nonempty aggorder */ costs->numAggs++; if (aggref->aggorder != NIL || aggref->aggdistinct != NIL) @@ -668,15 +692,6 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context) costs->finalCost += argcosts.per_tuple; } - /* extract argument types (ignoring any ORDER BY expressions) */ - numArguments = get_aggregate_argtypes(aggref, inputTypes); - - /* resolve actual type of transition state, if polymorphic */ - aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid, - aggtranstype, - inputTypes, - numArguments); - /* * If the transition type is pass-by-value then it doesn't add * anything to the required size of the hashtable. If it is @@ -698,14 +713,15 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context) * This works for cases like MAX/MIN and is probably somewhat * reasonable otherwise. */ - int numdirectargs = list_length(aggref->aggdirectargs); - int32 aggtranstypmod; + int32 aggtranstypmod = -1; - if (numArguments > numdirectargs && - aggtranstype == inputTypes[numdirectargs]) - aggtranstypmod = exprTypmod((Node *) linitial(aggref->args)); - else - aggtranstypmod = -1; + if (aggref->args) + { + TargetEntry *tle = (TargetEntry *) linitial(aggref->args); + + if (aggtranstype == exprType((Node *) tle->expr)) + aggtranstypmod = exprTypmod((Node *) tle->expr); + } avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod); } |