Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2013-11-16 21:03:40 +0000
committerTom Lane2013-11-16 21:03:40 +0000
commit6cb86143e8e1e855255edc706bce71c6ebfd9a6c (patch)
tree2ed7cf0b5fe28b8ba858ae3e384534cdb7f31aa3 /src/backend
parent55c3d86a2a374f9d8fd88fd947601c1f49a4da08 (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.c2
-rw-r--r--src/backend/commands/aggregatecmds.c4
-rw-r--r--src/backend/commands/define.c24
-rw-r--r--src/backend/optimizer/util/clauses.c40
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;
}
/*