1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +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:
Tom Lane
2006-07-27 19:52:07 +00:00
parent c2d1138351
commit 108fe47301
39 changed files with 702 additions and 484 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.81 2006/07/14 14:52:17 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.82 2006/07/27 19:52:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,7 +42,8 @@ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
void
AggregateCreate(const char *aggName,
Oid aggNamespace,
Oid aggBaseType,
Oid *aggArgTypes,
int numArgs,
List *aggtransfnName,
List *aggfinalfnName,
List *aggsortopName,
@@ -57,9 +58,10 @@ AggregateCreate(const char *aggName,
Oid transfn;
Oid finalfn = InvalidOid; /* can be omitted */
Oid sortop = InvalidOid; /* can be omitted */
bool hasPolyArg;
Oid rettype;
Oid finaltype;
Oid fnArgs[2]; /* we only deal with 1- and 2-arg fns */
Oid *fnArgs;
int nargs_transfn;
Oid procOid;
TupleDesc tupDesc;
@@ -74,27 +76,34 @@ AggregateCreate(const char *aggName,
if (!aggtransfnName)
elog(ERROR, "aggregate must have a transition function");
/* check for polymorphic arguments */
hasPolyArg = false;
for (i = 0; i < numArgs; i++)
{
if (aggArgTypes[i] == ANYARRAYOID ||
aggArgTypes[i] == ANYELEMENTOID)
{
hasPolyArg = true;
break;
}
}
/*
* If transtype is polymorphic, basetype must be polymorphic also; else we
* will have no way to deduce the actual transtype.
* If transtype is polymorphic, must have polymorphic argument also;
* else we will have no way to deduce the actual transtype.
*/
if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) &&
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
if (!hasPolyArg &&
(aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine transition data type"),
errdetail("An aggregate using \"anyarray\" or \"anyelement\" as "
"transition type must have one of them as its base type.")));
errdetail("An aggregate using \"anyarray\" or \"anyelement\" as transition type must have at least one argument of either type.")));
/* handle transfn */
/* find the transfn */
nargs_transfn = numArgs + 1;
fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
fnArgs[0] = aggTransType;
if (aggBaseType == ANYOID)
nargs_transfn = 1;
else
{
fnArgs[1] = aggBaseType;
nargs_transfn = 2;
}
memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
&rettype);
@@ -123,13 +132,14 @@ AggregateCreate(const char *aggName,
proc = (Form_pg_proc) GETSTRUCT(tup);
/*
* If the transfn is strict and the initval is NULL, make sure input type
* and transtype are the same (or at least binary-compatible), so that
* If the transfn is strict and the initval is NULL, make sure first input
* type and transtype are the same (or at least binary-compatible), so that
* it's OK to use the first input value as the initial transValue.
*/
if (proc->proisstrict && agginitval == NULL)
{
if (!IsBinaryCoercible(aggBaseType, aggTransType))
if (numArgs < 1 ||
!IsBinaryCoercible(aggArgTypes[0], aggTransType))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
@@ -153,32 +163,37 @@ AggregateCreate(const char *aggName,
Assert(OidIsValid(finaltype));
/*
* If finaltype (i.e. aggregate return type) is polymorphic, basetype must
* If finaltype (i.e. aggregate return type) is polymorphic, inputs must
* be polymorphic also, else parser will fail to deduce result type.
* (Note: given the previous test on transtype and basetype, this cannot
* (Note: given the previous test on transtype and inputs, this cannot
* happen, unless someone has snuck a finalfn definition into the catalogs
* that itself violates the rule against polymorphic result with no
* polymorphic input.)
*/
if ((finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) &&
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
if (!hasPolyArg &&
(finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot determine result data type"),
errdetail("An aggregate returning \"anyarray\" or \"anyelement\" "
"must have one of them as its base type.")));
"must have at least one argument of either type.")));
/* handle sortop, if supplied */
if (aggsortopName)
{
if (numArgs != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("sort operator can only be specified for single-argument aggregates")));
sortop = LookupOperName(NULL, aggsortopName,
aggBaseType, aggBaseType,
aggArgTypes[0], aggArgTypes[0],
false, -1);
}
/*
* Everything looks okay. Try to create the pg_proc entry for the
* aggregate. (This could fail if there's already a conflicting entry.)
*/
fnArgs[0] = aggBaseType;
procOid = ProcedureCreate(aggName,
aggNamespace,
@@ -195,7 +210,8 @@ AggregateCreate(const char *aggName,
false, /* isStrict (not needed for agg) */
PROVOLATILE_IMMUTABLE, /* volatility (not
* needed for agg) */
buildoidvector(fnArgs, 1), /* paramTypes */
buildoidvector(aggArgTypes,
numArgs), /* paramTypes */
PointerGetDatum(NULL), /* allParamTypes */
PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL)); /* parameterNames */
@@ -279,6 +295,8 @@ lookup_agg_function(List *fnName,
Oid *true_oid_array;
FuncDetailCode fdresult;
AclResult aclresult;
int i;
bool allPolyArgs = true;
/*
* func_get_detail looks up the function in the catalogs, does
@@ -307,13 +325,17 @@ lookup_agg_function(List *fnName,
* If the given type(s) are all polymorphic, there's nothing we can check.
* Otherwise, enforce consistency, and possibly refine the result type.
*/
if ((input_types[0] == ANYARRAYOID || input_types[0] == ANYELEMENTOID) &&
(nargs == 1 ||
(input_types[1] == ANYARRAYOID || input_types[1] == ANYELEMENTOID)))
for (i = 0; i < nargs; i++)
{
/* nothing to check here */
if (input_types[i] != ANYARRAYOID &&
input_types[i] != ANYELEMENTOID)
{
allPolyArgs = false;
break;
}
}
else
if (!allPolyArgs)
{
*rettype = enforce_generic_type_consistency(input_types,
true_oid_array,
@@ -325,22 +347,16 @@ lookup_agg_function(List *fnName,
* func_get_detail will find functions requiring run-time argument type
* coercion, but nodeAgg.c isn't prepared to deal with that
*/
if (true_oid_array[0] != ANYARRAYOID &&
true_oid_array[0] != ANYELEMENTOID &&
!IsBinaryCoercible(input_types[0], true_oid_array[0]))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s requires run-time type coercion",
func_signature_string(fnName, nargs, true_oid_array))));
if (nargs == 2 &&
true_oid_array[1] != ANYARRAYOID &&
true_oid_array[1] != ANYELEMENTOID &&
!IsBinaryCoercible(input_types[1], true_oid_array[1]))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s requires run-time type coercion",
func_signature_string(fnName, nargs, true_oid_array))));
for (i = 0; i < nargs; i++)
{
if (true_oid_array[i] != ANYARRAYOID &&
true_oid_array[i] != ANYELEMENTOID &&
!IsBinaryCoercible(input_types[i], true_oid_array[i]))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s requires run-time type coercion",
func_signature_string(fnName, nargs, true_oid_array))));
}
/* Check aggregate creator has permission to call the function */
aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE);