/*-------------------------------------------------------------------------
*
- * pg_aggregate.c--
- * routines to support manipulation of the pg_aggregate relation
+ * pg_aggregate.c
+ * routines to support manipulation of the pg_aggregate relation
*
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.5 1997/07/24 20:11:47 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.101 2009/01/01 17:23:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
-#include <postgres.h>
-
-#include <access/heapam.h>
-#include <utils/builtins.h>
-#include <fmgr.h>
-#include <catalog/catname.h>
-#include <utils/syscache.h>
-#include <catalog/pg_operator.h>
-#include <catalog/pg_proc.h>
-#include <catalog/pg_type.h>
-#include <catalog/pg_aggregate.h>
-#include <miscadmin.h>
-#ifndef HAVE_MEMMOVE
-# include <regex/utils.h>
-#else
-# include <string.h>
-#endif
-
-/* ----------------
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_proc_fn.h"
+#include "catalog/pg_type.h"
+#include "miscadmin.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_func.h"
+#include "parser/parse_oper.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
+ Oid *rettype);
+
+
+/*
* AggregateCreate
- *
- * aggregates overloading has been added. Instead of the full
- * overload support we have for functions, aggregate overloading only
- * applies to exact basetype matches. That is, we don't check the
- * the inheritance hierarchy
- *
- * OLD COMMENTS:
- * Currently, redefining aggregates using the same name is not
- * supported. In such a case, a warning is printed that the
- * aggregate already exists. If such is not the case, a new tuple
- * is created and inserted in the aggregate relation. The fields
- * of this tuple are aggregate name, owner id, 2 transition functions
- * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
- * type of data on which aggtransfn1 operates (aggbasetype), return
- * types of the two transition functions (aggtranstype1 and
- * aggtranstype2), final return type (aggfinaltype), and initial values
- * for the two state transition functions (agginitval1 and agginitval2).
- * All types and functions must have been defined
- * prior to defining the aggregate.
- *
- * ---------------
*/
void
-AggregateCreate(char *aggName,
- char *aggtransfn1Name,
- char *aggtransfn2Name,
- char *aggfinalfnName,
- char *aggbasetypeName,
- char *aggtransfn1typeName,
- char *aggtransfn2typeName,
- char *agginitval1,
- char *agginitval2)
+AggregateCreate(const char *aggName,
+ Oid aggNamespace,
+ Oid *aggArgTypes,
+ int numArgs,
+ List *aggtransfnName,
+ List *aggfinalfnName,
+ List *aggsortopName,
+ Oid aggTransType,
+ const char *agginitval)
{
- register i;
- Relation aggdesc;
- HeapTuple tup;
- char nulls[Natts_pg_aggregate];
- Datum values[Natts_pg_aggregate];
- Form_pg_proc proc;
- Oid xfn1 = InvalidOid;
- Oid xfn2 = InvalidOid;
- Oid ffn = InvalidOid;
- Oid xbase = InvalidOid;
- Oid xret1 = InvalidOid;
- Oid xret2 = InvalidOid;
- Oid fret = InvalidOid;
- Oid fnArgs[8];
- TupleDesc tupDesc;
-
- memset(fnArgs, 0, 8 * sizeof(Oid));
-
- /* sanity checks */
- if (!aggName)
- elog(WARN, "AggregateCreate: no aggregate name supplied");
-
- if (!aggtransfn1Name && !aggtransfn2Name)
- elog(WARN, "AggregateCreate: aggregate must have at least one transition function");
-
- tup = SearchSysCacheTuple(TYPNAME,
- PointerGetDatum(aggbasetypeName),
- 0,0,0);
- if(!HeapTupleIsValid(tup))
- elog(WARN, "AggregateCreate: Type '%s' undefined",aggbasetypeName);
- xbase = tup->t_oid;
-
- if (aggtransfn1Name) {
- tup = SearchSysCacheTuple(TYPNAME,
- PointerGetDatum(aggtransfn1typeName),
- 0,0,0);
- if(!HeapTupleIsValid(tup))
- elog(WARN, "AggregateCreate: Type '%s' undefined",
- aggtransfn1typeName);
- xret1 = tup->t_oid;
-
- fnArgs[0] = xret1;
- fnArgs[1] = xbase;
- tup = SearchSysCacheTuple(PRONAME,
- PointerGetDatum(aggtransfn1Name),
- Int32GetDatum(2),
- PointerGetDatum(fnArgs),
- 0);
- if(!HeapTupleIsValid(tup))
- elog(WARN, "AggregateCreate: '%s('%s', '%s') does not exist",
- aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
- if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
- elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
- aggtransfn1Name,
- aggtransfn1typeName);
- xfn1 = tup->t_oid;
- if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
- !OidIsValid(xbase))
- elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
- }
-
- if (aggtransfn2Name) {
- tup = SearchSysCacheTuple(TYPNAME,
- PointerGetDatum(aggtransfn2typeName),
- 0,0,0);
- if(!HeapTupleIsValid(tup))
- elog(WARN, "AggregateCreate: Type '%s' undefined",
- aggtransfn2typeName);
- xret2 = tup->t_oid;
-
- fnArgs[0] = xret2;
- fnArgs[1] = 0;
- tup = SearchSysCacheTuple(PRONAME,
- PointerGetDatum(aggtransfn2Name),
- Int32GetDatum(1),
- PointerGetDatum(fnArgs),
- 0);
- if(!HeapTupleIsValid(tup))
- elog(WARN, "AggregateCreate: '%s'('%s') does not exist",
- aggtransfn2Name, aggtransfn2typeName);
- if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
- elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
- aggtransfn2Name, aggtransfn2typeName);
- xfn2 = tup->t_oid;
- if (!OidIsValid(xfn2) || !OidIsValid(xret2))
- elog(WARN, "AggregateCreate: bogus function '%s'",aggfinalfnName);
- }
-
- tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName),
- ObjectIdGetDatum(xbase),
- 0,0);
- if (HeapTupleIsValid(tup))
- elog(WARN,
- "AggregateCreate: aggregate '%s' with base type '%s' already exists",
- aggName, aggbasetypeName);
-
- /* more sanity checks */
- if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
- elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
-
- if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
- elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
-
- if (aggfinalfnName) {
- fnArgs[0] = xret1;
- fnArgs[1] = xret2;
- tup = SearchSysCacheTuple(PRONAME,
- PointerGetDatum(aggfinalfnName),
- Int32GetDatum(2),
- PointerGetDatum(fnArgs),
- 0);
- if(!HeapTupleIsValid(tup))
- elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist",
- aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
- ffn = tup->t_oid;
+ Relation aggdesc;
+ HeapTuple tup;
+ bool nulls[Natts_pg_aggregate];
+ Datum values[Natts_pg_aggregate];
+ Form_pg_proc proc;
+ Oid transfn;
+ Oid finalfn = InvalidOid; /* can be omitted */
+ Oid sortop = InvalidOid; /* can be omitted */
+ bool hasPolyArg;
+ bool hasInternalArg;
+ Oid rettype;
+ Oid finaltype;
+ Oid *fnArgs;
+ int nargs_transfn;
+ Oid procOid;
+ TupleDesc tupDesc;
+ int i;
+ ObjectAddress myself,
+ referenced;
+
+ /* sanity checks (caller should have caught these) */
+ if (!aggName)
+ elog(ERROR, "no aggregate name supplied");
+
+ if (!aggtransfnName)
+ elog(ERROR, "aggregate must have a transition function");
+
+ /* check for polymorphic and INTERNAL arguments */
+ hasPolyArg = false;
+ hasInternalArg = false;
+ for (i = 0; i < numArgs; i++)
+ {
+ if (IsPolymorphicType(aggArgTypes[i]))
+ hasPolyArg = true;
+ else if (aggArgTypes[i] == INTERNALOID)
+ hasInternalArg = true;
+ }
+
+ /*
+ * If transtype is polymorphic, must have polymorphic argument also; else
+ * we will have no way to deduce the actual transtype.
+ */
+ if (IsPolymorphicType(aggTransType) && !hasPolyArg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot determine transition data type"),
+ errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
+
+ /* find the transfn */
+ nargs_transfn = numArgs + 1;
+ fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
+ fnArgs[0] = aggTransType;
+ memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
+ transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
+ &rettype);
+
+ /*
+ * Return type of transfn (possibly after refinement by
+ * enforce_generic_type_consistency, if transtype isn't polymorphic) must
+ * exactly match declared transtype.
+ *
+ * In the non-polymorphic-transtype case, it might be okay to allow a
+ * rettype that's binary-coercible to transtype, but I'm not quite
+ * convinced that it's either safe or useful. When transtype is
+ * polymorphic we *must* demand exact equality.
+ */
+ if (rettype != aggTransType)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("return type of transition function %s is not %s",
+ NameListToString(aggtransfnName),
+ format_type_be(aggTransType))));
+
+ tup = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(transfn),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for function %u", transfn);
proc = (Form_pg_proc) GETSTRUCT(tup);
- fret = proc->prorettype;
- if (!OidIsValid(ffn) || !OidIsValid(fret))
- elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
- }
-
- /*
- * If transition function 2 is defined, it must have an initial value,
- * whereas transition function 1 does not, which allows man and min
- * aggregates to return NULL if they are evaluated on empty sets.
- */
- if (OidIsValid(xfn2) && !agginitval2)
- elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value");
-
- /* initialize nulls and values */
- for(i=0; i < Natts_pg_aggregate; i++) {
- nulls[i] = ' ';
- values[i] = (Datum)NULL;
- }
- values[Anum_pg_aggregate_aggname-1] = PointerGetDatum(aggName);
- values[Anum_pg_aggregate_aggowner-1] =
- Int32GetDatum(GetUserId());
- values[Anum_pg_aggregate_aggtransfn1-1] =
- ObjectIdGetDatum(xfn1);
- values[Anum_pg_aggregate_aggtransfn2-1] =
- ObjectIdGetDatum(xfn2);
- values[Anum_pg_aggregate_aggfinalfn-1] =
- ObjectIdGetDatum(ffn);
-
- values[Anum_pg_aggregate_aggbasetype-1] =
- ObjectIdGetDatum(xbase);
- if (!OidIsValid(xfn1)) {
- values[Anum_pg_aggregate_aggtranstype1-1] =
- ObjectIdGetDatum(InvalidOid);
- values[Anum_pg_aggregate_aggtranstype2-1] =
- ObjectIdGetDatum(xret2);
- values[Anum_pg_aggregate_aggfinaltype-1] =
- ObjectIdGetDatum(xret2);
- }
- else if (!OidIsValid(xfn2)) {
- values[Anum_pg_aggregate_aggtranstype1-1] =
- ObjectIdGetDatum(xret1);
- values[Anum_pg_aggregate_aggtranstype2-1] =
- ObjectIdGetDatum(InvalidOid);
- values[Anum_pg_aggregate_aggfinaltype-1] =
- ObjectIdGetDatum(xret1);
- }
- else {
- values[Anum_pg_aggregate_aggtranstype1-1] =
- ObjectIdGetDatum(xret1);
- values[Anum_pg_aggregate_aggtranstype2-1] =
- ObjectIdGetDatum(xret2);
- values[Anum_pg_aggregate_aggfinaltype-1] =
- ObjectIdGetDatum(fret);
- }
-
- if (agginitval1)
- values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1));
- else
- nulls[Anum_pg_aggregate_agginitval1-1] = 'n';
-
- if (agginitval2)
- values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2));
- else
- nulls[Anum_pg_aggregate_agginitval2-1] = 'n';
-
- if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
- elog(WARN, "AggregateCreate: could not open '%s'",
- AggregateRelationName);
-
- tupDesc = aggdesc->rd_att;
- if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
- values,
- nulls)))
- elog(WARN, "AggregateCreate: heap_formtuple failed");
- if (!OidIsValid(heap_insert(aggdesc, tup)))
- elog(WARN, "AggregateCreate: heap_insert failed");
- heap_close(aggdesc);
+ /*
+ * 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 (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")));
+ }
+ ReleaseSysCache(tup);
+
+ /* handle finalfn, if supplied */
+ if (aggfinalfnName)
+ {
+ fnArgs[0] = aggTransType;
+ finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
+ &finaltype);
+ }
+ else
+ {
+ /*
+ * If no finalfn, aggregate result type is type of the state value
+ */
+ finaltype = aggTransType;
+ }
+ Assert(OidIsValid(finaltype));
+
+ /*
+ * 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 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 (IsPolymorphicType(finaltype) && !hasPolyArg)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot determine result data type"),
+ errdetail("An aggregate returning a polymorphic type "
+ "must have at least one polymorphic argument.")));
+
+ /*
+ * Also, the return type can't be INTERNAL unless there's at least one
+ * INTERNAL argument. This is the same type-safety restriction we
+ * enforce for regular functions, but at the level of aggregates. We
+ * must test this explicitly because we allow INTERNAL as the transtype.
+ */
+ if (finaltype == INTERNALOID && !hasInternalArg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("unsafe use of pseudo-type \"internal\""),
+ errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
+
+ /* 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,
+ 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.)
+ */
+
+ procOid = ProcedureCreate(aggName,
+ aggNamespace,
+ false, /* no replacement */
+ false, /* doesn't return a set */
+ finaltype, /* returnType */
+ INTERNALlanguageId, /* languageObjectId */
+ InvalidOid, /* no validator */
+ "aggregate_dummy", /* placeholder proc */
+ NULL, /* probin */
+ true, /* isAgg */
+ false, /* isWindowFunc */
+ false, /* security invoker (currently not
+ * definable for agg) */
+ false, /* isStrict (not needed for agg) */
+ PROVOLATILE_IMMUTABLE, /* volatility (not
+ * needed for agg) */
+ buildoidvector(aggArgTypes,
+ numArgs), /* paramTypes */
+ PointerGetDatum(NULL), /* allParamTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* proconfig */
+ 1, /* procost */
+ 0); /* prorows */
+
+ /*
+ * Okay to create the pg_aggregate entry.
+ */
+
+ /* initialize nulls and values */
+ for (i = 0; i < Natts_pg_aggregate; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) NULL;
+ }
+ values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
+ values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
+ values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
+ values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
+ values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
+ if (agginitval)
+ values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
+ else
+ nulls[Anum_pg_aggregate_agginitval - 1] = true;
+
+ aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
+ tupDesc = aggdesc->rd_att;
+
+ tup = heap_form_tuple(tupDesc, values, nulls);
+ simple_heap_insert(aggdesc, tup);
+
+ CatalogUpdateIndexes(aggdesc, tup);
+
+ heap_close(aggdesc, RowExclusiveLock);
+
+ /*
+ * Create dependencies for the aggregate (above and beyond those already
+ * made by ProcedureCreate). Note: we don't need an explicit dependency
+ * on aggTransType since we depend on it indirectly through transfn.
+ */
+ myself.classId = ProcedureRelationId;
+ myself.objectId = procOid;
+ myself.objectSubId = 0;
+
+ /* Depends on transition function */
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = transfn;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* Depends on final function, if any */
+ if (OidIsValid(finalfn))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = finalfn;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ /* Depends on sort operator, if any */
+ if (OidIsValid(sortop))
+ {
+ referenced.classId = OperatorRelationId;
+ referenced.objectId = sortop;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
-char *
-AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
+/*
+ * lookup_agg_function -- common code for finding both transfn and finalfn
+ */
+static Oid
+lookup_agg_function(List *fnName,
+ int nargs,
+ Oid *input_types,
+ Oid *rettype)
{
- HeapTuple tup;
- Relation aggRel;
- int initValAttno;
- Oid transtype;
- text *textInitVal;
- char *strInitVal, *initVal;
-
- Assert(PointerIsValid(aggName));
- Assert(PointerIsValid(isNull));
- Assert(xfuncno == 1 || xfuncno == 2);
-
- tup = SearchSysCacheTuple(AGGNAME,
- PointerGetDatum(aggName),
- PointerGetDatum(basetype),
- 0,0);
- if (!HeapTupleIsValid(tup))
- elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
- aggName);
- if (xfuncno == 1) {
- transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
- initValAttno = Anum_pg_aggregate_agginitval1;
- }
- else /* can only be 1 or 2 */ {
- transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
- initValAttno = Anum_pg_aggregate_agginitval2;
- }
-
- aggRel = heap_openr(AggregateRelationName);
- if (!RelationIsValid(aggRel))
- elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"",
- AggregateRelationName);
- /*
- * must use fastgetattr in case one or other of the init values is NULL
- */
- textInitVal = (text *) fastgetattr(tup, initValAttno,
- RelationGetTupleDescriptor(aggRel),
- isNull);
- if (!PointerIsValid(textInitVal))
- *isNull = true;
- if (*isNull) {
- heap_close(aggRel);
- return((char *) NULL);
- }
- strInitVal = textout(textInitVal);
- heap_close(aggRel);
-
- tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype),
- 0,0,0);
- if (!HeapTupleIsValid(tup)) {
- pfree(strInitVal);
- elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
- }
- initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1);
- pfree(strInitVal);
- return(initVal);
+ Oid fnOid;
+ bool retset;
+ int nvargs;
+ Oid *true_oid_array;
+ FuncDetailCode fdresult;
+ AclResult aclresult;
+ int i;
+
+ /*
+ * func_get_detail looks up the function in the catalogs, does
+ * disambiguation for polymorphic functions, handles inheritance, and
+ * returns the funcid and type and set or singleton status of the
+ * function's return value. it also returns the true argument types to
+ * the function.
+ */
+ fdresult = func_get_detail(fnName, NIL, nargs, input_types, false, false,
+ &fnOid, rettype, &retset, &nvargs,
+ &true_oid_array, NULL);
+
+ /* only valid case is a normal function not returning a set */
+ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(fnName, nargs, input_types))));
+ if (retset)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("function %s returns a set",
+ func_signature_string(fnName, nargs, input_types))));
+
+ /*
+ * If there are any polymorphic types involved, enforce consistency, and
+ * possibly refine the result type. It's OK if the result is still
+ * polymorphic at this point, though.
+ */
+ *rettype = enforce_generic_type_consistency(input_types,
+ true_oid_array,
+ nargs,
+ *rettype,
+ true);
+
+ /*
+ * func_get_detail will find functions requiring run-time argument type
+ * coercion, but nodeAgg.c isn't prepared to deal with that
+ */
+ for (i = 0; i < nargs; i++)
+ {
+ if (!IsPolymorphicType(true_oid_array[i]) &&
+ !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);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid));
+
+ return fnOid;
}