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 | |
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')
-rw-r--r-- | src/backend/catalog/pg_aggregate.c | 2 | ||||
-rw-r--r-- | src/backend/commands/aggregatecmds.c | 4 | ||||
-rw-r--r-- | src/backend/commands/define.c | 24 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 40 |
4 files changed, 57 insertions, 13 deletions
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index d9e961ebfad..9dbec508a0d 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -55,6 +55,7 @@ AggregateCreate(const char *aggName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, + int32 aggTransSpace, const char *agginitval) { Relation aggdesc; @@ -273,6 +274,7 @@ AggregateCreate(const char *aggName, values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); + values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); else diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 78af0924654..6fc3e045492 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -60,6 +60,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, List *sortoperatorName = NIL; TypeName *baseType = NULL; TypeName *transType = NULL; + int32 transSpace = 0; char *initval = NULL; int numArgs; oidvector *parameterTypes; @@ -102,6 +103,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, transType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "stype1") == 0) transType = defGetTypeName(defel); + else if (pg_strcasecmp(defel->defname, "sspace") == 0) + transSpace = defGetInt32(defel); else if (pg_strcasecmp(defel->defname, "initcond") == 0) initval = defGetString(defel); else if (pg_strcasecmp(defel->defname, "initcond1") == 0) @@ -248,5 +251,6 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, finalfuncName, /* final function name */ sortoperatorName, /* sort operator name */ transTypeId, /* transition data type */ + transSpace, /* transition space */ initval); /* initial condition */ } diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index 9fa222f5fc0..75f77da2cff 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -165,6 +165,30 @@ defGetBoolean(DefElem *def) } /* + * Extract an int32 value from a DefElem. + */ +int32 +defGetInt32(DefElem *def) +{ + if (def->arg == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s requires an integer value", + def->defname))); + switch (nodeTag(def->arg)) + { + case T_Integer: + return (int32) intVal(def->arg); + default: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s requires an integer value", + def->defname))); + } + return 0; /* keep compiler quiet */ +} + +/* * Extract an int64 value from a DefElem. */ int64 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; } /* |