diff options
author | Tom Lane | 2013-11-16 21:03:40 +0000 |
---|---|---|
committer | Tom Lane | 2013-11-16 21:03:40 +0000 |
commit | 6cb86143e8e1e855255edc706bce71c6ebfd9a6c (patch) | |
tree | 2ed7cf0b5fe28b8ba858ae3e384534cdb7f31aa3 /src/backend/optimizer/util/clauses.c | |
parent | 55c3d86a2a374f9d8fd88fd947601c1f49a4da08 (diff) |
Allow aggregates to provide estimates of their transition state data size.
Formerly the planner had a hard-wired rule of thumb for guessing the amount
of space consumed by an aggregate function's transition state data. This
estimate is critical to deciding whether it's OK to use hash aggregation,
and in many situations the built-in estimate isn't very good. This patch
adds a column to pg_aggregate wherein a per-aggregate estimate can be
provided, overriding the planner's default, and infrastructure for setting
the column via CREATE AGGREGATE.
It may be that additional smarts will be required in future, perhaps even
a per-aggregate estimation function. But this is already a step forward.
This is extracted from a larger patch to improve the performance of numeric
and int8 aggregates. I (tgl) thought it was worth reviewing and committing
this infrastructure separately. In this commit, all built-in aggregates
are given aggtransspace = 0, so no behavior should change.
Hadi Moshayedi, reviewed by Pavel Stehule and Tomas Vondra
Diffstat (limited to 'src/backend/optimizer/util/clauses.c')
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 40 |
1 files changed, 27 insertions, 13 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index add29f54d09..7ce8a9d8180 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -461,6 +461,7 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context) Oid aggtransfn; Oid aggfinalfn; Oid aggtranstype; + int32 aggtransspace; QualCost argcosts; Oid *inputTypes; int numArguments; @@ -478,6 +479,7 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context) aggtransfn = aggform->aggtransfn; aggfinalfn = aggform->aggfinalfn; aggtranstype = aggform->aggtranstype; + aggtransspace = aggform->aggtransspace; ReleaseSysCache(aggTuple); /* count it */ @@ -541,22 +543,30 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context) */ if (!get_typbyval(aggtranstype)) { - int32 aggtranstypmod; int32 avgwidth; - /* - * If transition state is of same type as first input, assume it's - * the same typmod (same width) as well. This works for cases - * like MAX/MIN and is probably somewhat reasonable otherwise. - */ - if (numArguments > 0 && aggtranstype == inputTypes[0]) - aggtranstypmod = exprTypmod((Node *) linitial(aggref->args)); + /* Use average width if aggregate definition gave one */ + if (aggtransspace > 0) + avgwidth = aggtransspace; else - aggtranstypmod = -1; + { + /* + * If transition state is of same type as first input, assume + * it's the same typmod (same width) as well. This works for + * cases like MAX/MIN and is probably somewhat reasonable + * otherwise. + */ + int32 aggtranstypmod; - avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod); - avgwidth = MAXALIGN(avgwidth); + if (numArguments > 0 && aggtranstype == inputTypes[0]) + aggtranstypmod = exprTypmod((Node *) linitial(aggref->args)); + else + aggtranstypmod = -1; + + avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod); + } + avgwidth = MAXALIGN(avgwidth); costs->transitionSpace += avgwidth + 2 * sizeof(void *); } else if (aggtranstype == INTERNALOID) @@ -564,12 +574,16 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context) /* * INTERNAL transition type is a special case: although INTERNAL * is pass-by-value, it's almost certainly being used as a pointer - * to some large data structure. We assume usage of + * to some large data structure. The aggregate definition can + * provide an estimate of the size. If it doesn't, then we assume * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is * being kept in a private memory context, as is done by * array_agg() for instance. */ - costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE; + if (aggtransspace > 0) + costs->transitionSpace += aggtransspace; + else + costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE; } /* |