]> granicus.if.org Git - postgresql/blobdiff - src/backend/catalog/pg_aggregate.c
Update copyright for 2009.
[postgresql] / src / backend / catalog / pg_aggregate.c
index 13a3c48e9f498fbcb4d53ae9787dcb81df464b51..5af71a54ea11d6819b20e736d0fcb47afa9eb1f3 100644 (file)
 /*-------------------------------------------------------------------------
  *
- * 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;
 }