mirror of
https://github.com/postgres/postgres.git
synced 2025-08-27 07:42:10 +03:00
Aggregate functions now support multiple input arguments. I also took
the opportunity to treat COUNT(*) as a zero-argument aggregate instead of the old hack that equated it to COUNT(1); this is materially cleaner (no more weird ANYOID cases) and ought to be at least a tiny bit faster. Original patch by Sergey Koposov; review, documentation, simple regression tests, pg_dump and psql support by moi.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.19 2006/07/26 19:31:50 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.20 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -217,12 +217,13 @@ find_minmax_aggs_walker(Node *node, List **context)
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
Oid aggsortop;
|
||||
Expr *curTarget;
|
||||
MinMaxAggInfo *info;
|
||||
ListCell *l;
|
||||
|
||||
Assert(aggref->agglevelsup == 0);
|
||||
if (aggref->aggstar)
|
||||
return true; /* foo(*) is surely not optimizable */
|
||||
if (list_length(aggref->args) != 1)
|
||||
return true; /* it couldn't be MIN/MAX */
|
||||
/* note: we do not care if DISTINCT is mentioned ... */
|
||||
|
||||
aggsortop = fetch_agg_sort_op(aggref->aggfnoid);
|
||||
@@ -232,18 +233,19 @@ find_minmax_aggs_walker(Node *node, List **context)
|
||||
/*
|
||||
* Check whether it's already in the list, and add it if not.
|
||||
*/
|
||||
curTarget = linitial(aggref->args);
|
||||
foreach(l, *context)
|
||||
{
|
||||
info = (MinMaxAggInfo *) lfirst(l);
|
||||
if (info->aggfnoid == aggref->aggfnoid &&
|
||||
equal(info->target, aggref->target))
|
||||
equal(info->target, curTarget))
|
||||
return false;
|
||||
}
|
||||
|
||||
info = (MinMaxAggInfo *) palloc0(sizeof(MinMaxAggInfo));
|
||||
info->aggfnoid = aggref->aggfnoid;
|
||||
info->aggsortop = aggsortop;
|
||||
info->target = aggref->target;
|
||||
info->target = curTarget;
|
||||
|
||||
*context = lappend(*context, info);
|
||||
|
||||
@@ -520,13 +522,14 @@ replace_aggs_with_params_mutator(Node *node, List **context)
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
ListCell *l;
|
||||
Expr *curTarget = linitial(aggref->args);
|
||||
|
||||
foreach(l, *context)
|
||||
{
|
||||
MinMaxAggInfo *info = (MinMaxAggInfo *) lfirst(l);
|
||||
|
||||
if (info->aggfnoid == aggref->aggfnoid &&
|
||||
equal(info->target, aggref->target))
|
||||
equal(info->target, curTarget))
|
||||
return (Node *) info->param;
|
||||
}
|
||||
elog(ERROR, "failed to re-find aggregate info record");
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.214 2006/07/14 14:52:21 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.215 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -397,17 +397,27 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
if (IsA(node, Aggref))
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
Oid inputType;
|
||||
Oid *inputTypes;
|
||||
int numArguments;
|
||||
HeapTuple aggTuple;
|
||||
Form_pg_aggregate aggform;
|
||||
Oid aggtranstype;
|
||||
int i;
|
||||
ListCell *l;
|
||||
|
||||
Assert(aggref->agglevelsup == 0);
|
||||
counts->numAggs++;
|
||||
if (aggref->aggdistinct)
|
||||
counts->numDistinctAggs++;
|
||||
|
||||
inputType = exprType((Node *) aggref->target);
|
||||
/* extract argument types */
|
||||
numArguments = list_length(aggref->args);
|
||||
inputTypes = (Oid *) palloc(sizeof(Oid) * numArguments);
|
||||
i = 0;
|
||||
foreach(l, aggref->args)
|
||||
{
|
||||
inputTypes[i++] = exprType((Node *) lfirst(l));
|
||||
}
|
||||
|
||||
/* fetch aggregate transition datatype from pg_aggregate */
|
||||
aggTuple = SearchSysCache(AGGFNOID,
|
||||
@@ -423,17 +433,18 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
/* resolve actual type of transition state, if polymorphic */
|
||||
if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
|
||||
{
|
||||
/* have to fetch the agg's declared input type... */
|
||||
Oid *agg_arg_types;
|
||||
/* have to fetch the agg's declared input types... */
|
||||
Oid *declaredArgTypes;
|
||||
int agg_nargs;
|
||||
|
||||
(void) get_func_signature(aggref->aggfnoid,
|
||||
&agg_arg_types, &agg_nargs);
|
||||
Assert(agg_nargs == 1);
|
||||
aggtranstype = resolve_generic_type(aggtranstype,
|
||||
inputType,
|
||||
agg_arg_types[0]);
|
||||
pfree(agg_arg_types);
|
||||
&declaredArgTypes, &agg_nargs);
|
||||
Assert(agg_nargs == numArguments);
|
||||
aggtranstype = enforce_generic_type_consistency(inputTypes,
|
||||
declaredArgTypes,
|
||||
agg_nargs,
|
||||
aggtranstype);
|
||||
pfree(declaredArgTypes);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -448,12 +459,12 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
int32 avgwidth;
|
||||
|
||||
/*
|
||||
* If transition state is of same type as 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 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 (aggtranstype == inputType)
|
||||
aggtranstypmod = exprTypmod((Node *) aggref->target);
|
||||
if (numArguments > 0 && aggtranstype == inputTypes[0])
|
||||
aggtranstypmod = exprTypmod((Node *) linitial(aggref->args));
|
||||
else
|
||||
aggtranstypmod = -1;
|
||||
|
||||
@@ -464,10 +475,10 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
}
|
||||
|
||||
/*
|
||||
* Complain if the aggregate's argument contains any aggregates;
|
||||
* Complain if the aggregate's arguments contain any aggregates;
|
||||
* nested agg functions are semantically nonsensical.
|
||||
*/
|
||||
if (contain_agg_clause((Node *) aggref->target))
|
||||
if (contain_agg_clause((Node *) aggref->args))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("aggregate function calls may not be nested")));
|
||||
@@ -3026,7 +3037,14 @@ expression_tree_walker(Node *node,
|
||||
/* primitive node types with no expression subnodes */
|
||||
break;
|
||||
case T_Aggref:
|
||||
return walker(((Aggref *) node)->target, context);
|
||||
{
|
||||
Aggref *expr = (Aggref *) node;
|
||||
|
||||
if (expression_tree_walker((Node *) expr->args,
|
||||
walker, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_ArrayRef:
|
||||
{
|
||||
ArrayRef *aref = (ArrayRef *) node;
|
||||
@@ -3448,7 +3466,7 @@ expression_tree_mutator(Node *node,
|
||||
Aggref *newnode;
|
||||
|
||||
FLATCOPY(newnode, aggref, Aggref);
|
||||
MUTATE(newnode->target, aggref->target, Expr *);
|
||||
MUTATE(newnode->args, aggref->args, List *);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
|
Reference in New Issue
Block a user