1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* pg_aggregate.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* routines to support manipulation of the pg_aggregate relation
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2013-01-01 23:15:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/catalog/pg_aggregate.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/heapam.h"
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2002-07-17 00:12:20 +02:00
|
|
|
#include "catalog/dependency.h"
|
1999-11-22 18:56:41 +01:00
|
|
|
#include "catalog/indexing.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_aggregate.h"
|
2002-04-11 22:00:18 +02:00
|
|
|
#include "catalog/pg_language.h"
|
2005-04-14 22:03:27 +02:00
|
|
|
#include "catalog/pg_operator.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/pg_proc.h"
|
2008-03-27 04:57:34 +01:00
|
|
|
#include "catalog/pg_proc_fn.h"
|
2006-07-13 18:49:20 +02:00
|
|
|
#include "catalog/pg_type.h"
|
2005-01-28 00:42:18 +01:00
|
|
|
#include "miscadmin.h"
|
2000-07-17 05:05:41 +02:00
|
|
|
#include "parser/parse_coerce.h"
|
|
|
|
#include "parser/parse_func.h"
|
2005-04-12 06:26:34 +02:00
|
|
|
#include "parser/parse_oper.h"
|
2005-01-28 00:42:18 +01:00
|
|
|
#include "utils/acl.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/builtins.h"
|
2005-01-28 00:42:18 +01:00
|
|
|
#include "utils/lsyscache.h"
|
2008-06-19 02:46:06 +02:00
|
|
|
#include "utils/rel.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/syscache.h"
|
1996-11-04 00:27:08 +01:00
|
|
|
|
2002-04-11 22:00:18 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
|
|
|
|
Oid *rettype);
|
2003-07-01 21:10:53 +02:00
|
|
|
|
|
|
|
|
2002-03-29 20:06:29 +01:00
|
|
|
/*
|
1996-07-09 08:22:35 +02:00
|
|
|
* AggregateCreate
|
|
|
|
*/
|
2012-12-24 00:25:03 +01:00
|
|
|
Oid
|
2002-03-29 20:06:29 +01:00
|
|
|
AggregateCreate(const char *aggName,
|
|
|
|
Oid aggNamespace,
|
2006-07-27 21:52:07 +02:00
|
|
|
int numArgs,
|
Allow aggregate functions to be VARIADIC.
There's no inherent reason why an aggregate function can't be variadic
(even VARIADIC ANY) if its transition function can handle the case.
Indeed, this patch to add the feature touches none of the planner or
executor, and little of the parser; the main missing stuff was DDL and
pg_dump support.
It is true that variadic aggregates can create the same sort of ambiguity
about parameters versus ORDER BY keys that was complained of when we
(briefly) had both one- and two-argument forms of string_agg(). However,
the policy formed in response to that discussion only said that we'd not
create any built-in aggregates with varying numbers of arguments, not that
we shouldn't allow users to do it. So the logical extension of that is
we can allow users to make variadic aggregates as long as we're wary about
shipping any such in core.
In passing, this patch allows aggregate function arguments to be named, to
the extent of remembering the names in pg_proc and dumping them in pg_dump.
You can't yet call an aggregate using named-parameter notation. That seems
like a likely future extension, but it'll take some work, and it's not what
this patch is really about. Likewise, there's still some work needed to
make window functions handle VARIADIC fully, but I left that for another
day.
initdb forced because of new aggvariadic field in Aggref parse nodes.
2013-09-03 23:08:38 +02:00
|
|
|
oidvector *parameterTypes,
|
|
|
|
Datum allParameterTypes,
|
|
|
|
Datum parameterModes,
|
|
|
|
Datum parameterNames,
|
|
|
|
List *parameterDefaults,
|
2002-04-09 22:35:55 +02:00
|
|
|
List *aggtransfnName,
|
|
|
|
List *aggfinalfnName,
|
2005-04-12 06:26:34 +02:00
|
|
|
List *aggsortopName,
|
2002-03-29 20:06:29 +01:00
|
|
|
Oid aggTransType,
|
|
|
|
const char *agginitval)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation aggdesc;
|
|
|
|
HeapTuple tup;
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_aggregate];
|
1997-09-08 04:41:22 +02:00
|
|
|
Datum values[Natts_pg_aggregate];
|
|
|
|
Form_pg_proc proc;
|
2000-07-17 05:05:41 +02:00
|
|
|
Oid transfn;
|
2001-03-22 05:01:46 +01:00
|
|
|
Oid finalfn = InvalidOid; /* can be omitted */
|
2005-04-12 06:26:34 +02:00
|
|
|
Oid sortop = InvalidOid; /* can be omitted */
|
Allow aggregate functions to be VARIADIC.
There's no inherent reason why an aggregate function can't be variadic
(even VARIADIC ANY) if its transition function can handle the case.
Indeed, this patch to add the feature touches none of the planner or
executor, and little of the parser; the main missing stuff was DDL and
pg_dump support.
It is true that variadic aggregates can create the same sort of ambiguity
about parameters versus ORDER BY keys that was complained of when we
(briefly) had both one- and two-argument forms of string_agg(). However,
the policy formed in response to that discussion only said that we'd not
create any built-in aggregates with varying numbers of arguments, not that
we shouldn't allow users to do it. So the logical extension of that is
we can allow users to make variadic aggregates as long as we're wary about
shipping any such in core.
In passing, this patch allows aggregate function arguments to be named, to
the extent of remembering the names in pg_proc and dumping them in pg_dump.
You can't yet call an aggregate using named-parameter notation. That seems
like a likely future extension, but it'll take some work, and it's not what
this patch is really about. Likewise, there's still some work needed to
make window functions handle VARIADIC fully, but I left that for another
day.
initdb forced because of new aggvariadic field in Aggref parse nodes.
2013-09-03 23:08:38 +02:00
|
|
|
Oid *aggArgTypes = parameterTypes->values;
|
2006-07-27 21:52:07 +02:00
|
|
|
bool hasPolyArg;
|
2008-11-14 20:47:50 +01:00
|
|
|
bool hasInternalArg;
|
2003-07-01 21:10:53 +02:00
|
|
|
Oid rettype;
|
2000-07-17 05:05:41 +02:00
|
|
|
Oid finaltype;
|
2006-07-27 21:52:07 +02:00
|
|
|
Oid *fnArgs;
|
2003-07-01 21:10:53 +02:00
|
|
|
int nargs_transfn;
|
2002-04-11 22:00:18 +02:00
|
|
|
Oid procOid;
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc tupDesc;
|
2000-07-17 05:05:41 +02:00
|
|
|
int i;
|
2002-09-04 22:31:48 +02:00
|
|
|
ObjectAddress myself,
|
|
|
|
referenced;
|
2011-12-19 23:05:19 +01:00
|
|
|
AclResult aclresult;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-07-21 03:59:11 +02:00
|
|
|
/* sanity checks (caller should have caught these) */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!aggName)
|
2001-08-10 17:49:39 +02:00
|
|
|
elog(ERROR, "no aggregate name supplied");
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
if (!aggtransfnName)
|
2001-08-10 17:49:39 +02:00
|
|
|
elog(ERROR, "aggregate must have a transition function");
|
2000-03-26 21:43:58 +02:00
|
|
|
|
2008-11-14 20:47:50 +01:00
|
|
|
/* check for polymorphic and INTERNAL arguments */
|
2006-07-27 21:52:07 +02:00
|
|
|
hasPolyArg = false;
|
2008-11-14 20:47:50 +01:00
|
|
|
hasInternalArg = false;
|
2006-07-27 21:52:07 +02:00
|
|
|
for (i = 0; i < numArgs; i++)
|
|
|
|
{
|
2007-04-02 05:49:42 +02:00
|
|
|
if (IsPolymorphicType(aggArgTypes[i]))
|
2006-07-27 21:52:07 +02:00
|
|
|
hasPolyArg = true;
|
2008-11-14 20:47:50 +01:00
|
|
|
else if (aggArgTypes[i] == INTERNALOID)
|
|
|
|
hasInternalArg = true;
|
2006-07-27 21:52:07 +02:00
|
|
|
}
|
|
|
|
|
2003-07-01 21:10:53 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* If transtype is polymorphic, must have polymorphic argument also; else
|
|
|
|
* we will have no way to deduce the actual transtype.
|
2003-07-01 21:10:53 +02:00
|
|
|
*/
|
2007-04-02 05:49:42 +02:00
|
|
|
if (IsPolymorphicType(aggTransType) && !hasPolyArg)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("cannot determine transition data type"),
|
2007-04-02 05:49:42 +02:00
|
|
|
errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
|
2003-07-01 21:10:53 +02:00
|
|
|
|
2006-07-27 21:52:07 +02:00
|
|
|
/* find the transfn */
|
|
|
|
nargs_transfn = numArgs + 1;
|
|
|
|
fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
|
2002-03-29 20:06:29 +01:00
|
|
|
fnArgs[0] = aggTransType;
|
2006-07-27 21:52:07 +02:00
|
|
|
memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
|
2003-07-01 21:10:53 +02:00
|
|
|
transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
|
|
|
|
&rettype);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return type of transfn (possibly after refinement by
|
2005-10-15 04:49:52 +02:00
|
|
|
* enforce_generic_type_consistency, if transtype isn't polymorphic) must
|
|
|
|
* exactly match declared transtype.
|
2003-07-01 21:10:53 +02:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* 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.
|
2003-07-01 21:10:53 +02:00
|
|
|
*/
|
|
|
|
if (rettype != aggTransType)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
|
|
errmsg("return type of transition function %s is not %s",
|
|
|
|
NameListToString(aggtransfnName),
|
|
|
|
format_type_be(aggTransType))));
|
2003-07-01 21:10:53 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
|
2000-07-17 05:05:41 +02:00
|
|
|
if (!HeapTupleIsValid(tup))
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "cache lookup failed for function %u", transfn);
|
2000-07-17 05:05:41 +02:00
|
|
|
proc = (Form_pg_proc) GETSTRUCT(tup);
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
/*
|
2006-07-27 21:52:07 +02:00
|
|
|
* If the transfn is strict and the initval is NULL, make sure first input
|
2006-10-04 02:30:14 +02:00
|
|
|
* 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.
|
2000-07-17 05:05:41 +02:00
|
|
|
*/
|
2000-11-16 23:30:52 +01:00
|
|
|
if (proc->proisstrict && agginitval == NULL)
|
2000-07-17 05:05:41 +02:00
|
|
|
{
|
2006-07-27 21:52:07 +02:00
|
|
|
if (numArgs < 1 ||
|
|
|
|
!IsBinaryCoercible(aggArgTypes[0], aggTransType))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tup);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
/* handle finalfn, if supplied */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (aggfinalfnName)
|
|
|
|
{
|
2002-03-29 20:06:29 +01:00
|
|
|
fnArgs[0] = aggTransType;
|
2003-07-01 21:10:53 +02:00
|
|
|
finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
|
|
|
|
&finaltype);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-03-26 21:43:58 +02:00
|
|
|
else
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2000-07-17 05:05:41 +02:00
|
|
|
* If no finalfn, aggregate result type is type of the state value
|
2000-03-26 21:43:58 +02:00
|
|
|
*/
|
2002-03-29 20:06:29 +01:00
|
|
|
finaltype = aggTransType;
|
2000-03-26 21:43:58 +02:00
|
|
|
}
|
2000-07-17 05:05:41 +02:00
|
|
|
Assert(OidIsValid(finaltype));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-07-01 21:10:53 +02:00
|
|
|
/*
|
2006-07-27 21:52:07 +02:00
|
|
|
* If finaltype (i.e. aggregate return type) is polymorphic, inputs must
|
2005-10-15 04:49:52 +02:00
|
|
|
* be polymorphic also, else parser will fail to deduce result type.
|
2006-07-27 21:52:07 +02:00
|
|
|
* (Note: given the previous test on transtype and inputs, this cannot
|
2005-10-15 04:49:52 +02:00
|
|
|
* happen, unless someone has snuck a finalfn definition into the catalogs
|
|
|
|
* that itself violates the rule against polymorphic result with no
|
|
|
|
* polymorphic input.)
|
2003-07-01 21:10:53 +02:00
|
|
|
*/
|
2007-04-02 05:49:42 +02:00
|
|
|
if (IsPolymorphicType(finaltype) && !hasPolyArg)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("cannot determine result data type"),
|
2007-11-15 22:14:46 +01:00
|
|
|
errdetail("An aggregate returning a polymorphic type "
|
|
|
|
"must have at least one polymorphic argument.")));
|
2003-07-01 21:10:53 +02:00
|
|
|
|
2008-11-14 20:47:50 +01:00
|
|
|
/*
|
|
|
|
* Also, the return type can't be INTERNAL unless there's at least one
|
2009-06-11 16:49:15 +02:00
|
|
|
* 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.
|
2008-11-14 20:47:50 +01:00
|
|
|
*/
|
|
|
|
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.")));
|
|
|
|
|
2005-04-12 06:26:34 +02:00
|
|
|
/* handle sortop, if supplied */
|
|
|
|
if (aggsortopName)
|
2006-07-27 21:52:07 +02:00
|
|
|
{
|
|
|
|
if (numArgs != 1)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
|
|
errmsg("sort operator can only be specified for single-argument aggregates")));
|
2006-03-14 23:48:25 +01:00
|
|
|
sortop = LookupOperName(NULL, aggsortopName,
|
2006-07-27 21:52:07 +02:00
|
|
|
aggArgTypes[0], aggArgTypes[0],
|
2006-03-14 23:48:25 +01:00
|
|
|
false, -1);
|
2006-07-27 21:52:07 +02:00
|
|
|
}
|
2005-04-12 06:26:34 +02:00
|
|
|
|
2011-12-19 23:05:19 +01:00
|
|
|
/*
|
|
|
|
* permission checks on used types
|
|
|
|
*/
|
|
|
|
for (i = 0; i < numArgs; i++)
|
|
|
|
{
|
|
|
|
aclresult = pg_type_aclcheck(aggArgTypes[i], GetUserId(), ACL_USAGE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
2012-06-15 21:55:03 +02:00
|
|
|
aclcheck_error_type(aclresult, aggArgTypes[i]);
|
2011-12-19 23:05:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
2012-06-15 21:55:03 +02:00
|
|
|
aclcheck_error_type(aclresult, aggTransType);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
2012-06-15 21:55:03 +02:00
|
|
|
aclcheck_error_type(aclresult, finaltype);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
|
2002-04-11 22:00:18 +02:00
|
|
|
/*
|
|
|
|
* Everything looks okay. Try to create the pg_proc entry for the
|
2005-10-15 04:49:52 +02:00
|
|
|
* aggregate. (This could fail if there's already a conflicting entry.)
|
2002-04-11 22:00:18 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
procOid = ProcedureCreate(aggName,
|
|
|
|
aggNamespace,
|
2002-09-04 22:31:48 +02:00
|
|
|
false, /* no replacement */
|
|
|
|
false, /* doesn't return a set */
|
|
|
|
finaltype, /* returnType */
|
2012-05-31 05:47:57 +02:00
|
|
|
GetUserId(), /* proowner */
|
2002-09-04 22:31:48 +02:00
|
|
|
INTERNALlanguageId, /* languageObjectId */
|
2005-04-01 00:46:33 +02:00
|
|
|
InvalidOid, /* no validator */
|
2002-09-04 22:31:48 +02:00
|
|
|
"aggregate_dummy", /* placeholder proc */
|
2008-07-16 18:55:24 +02:00
|
|
|
NULL, /* probin */
|
2002-09-04 22:31:48 +02:00
|
|
|
true, /* isAgg */
|
2008-12-31 03:25:06 +01:00
|
|
|
false, /* isWindowFunc */
|
2002-09-04 22:31:48 +02:00
|
|
|
false, /* security invoker (currently not
|
|
|
|
* definable for agg) */
|
2012-02-14 04:20:27 +01:00
|
|
|
false, /* isLeakProof */
|
2002-09-04 22:31:48 +02:00
|
|
|
false, /* isStrict (not needed for agg) */
|
|
|
|
PROVOLATILE_IMMUTABLE, /* volatility (not
|
|
|
|
* needed for agg) */
|
Allow aggregate functions to be VARIADIC.
There's no inherent reason why an aggregate function can't be variadic
(even VARIADIC ANY) if its transition function can handle the case.
Indeed, this patch to add the feature touches none of the planner or
executor, and little of the parser; the main missing stuff was DDL and
pg_dump support.
It is true that variadic aggregates can create the same sort of ambiguity
about parameters versus ORDER BY keys that was complained of when we
(briefly) had both one- and two-argument forms of string_agg(). However,
the policy formed in response to that discussion only said that we'd not
create any built-in aggregates with varying numbers of arguments, not that
we shouldn't allow users to do it. So the logical extension of that is
we can allow users to make variadic aggregates as long as we're wary about
shipping any such in core.
In passing, this patch allows aggregate function arguments to be named, to
the extent of remembering the names in pg_proc and dumping them in pg_dump.
You can't yet call an aggregate using named-parameter notation. That seems
like a likely future extension, but it'll take some work, and it's not what
this patch is really about. Likewise, there's still some work needed to
make window functions handle VARIADIC fully, but I left that for another
day.
initdb forced because of new aggvariadic field in Aggref parse nodes.
2013-09-03 23:08:38 +02:00
|
|
|
parameterTypes, /* paramTypes */
|
|
|
|
allParameterTypes, /* allParamTypes */
|
|
|
|
parameterModes, /* parameterModes */
|
|
|
|
parameterNames, /* parameterNames */
|
|
|
|
parameterDefaults, /* parameterDefaults */
|
2007-09-03 02:39:26 +02:00
|
|
|
PointerGetDatum(NULL), /* proconfig */
|
2007-11-15 22:14:46 +01:00
|
|
|
1, /* procost */
|
2008-12-18 19:20:35 +01:00
|
|
|
0); /* prorows */
|
2002-04-11 22:00:18 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Okay to create the pg_aggregate entry.
|
|
|
|
*/
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* initialize nulls and values */
|
|
|
|
for (i = 0; i < Natts_pg_aggregate; i++)
|
|
|
|
{
|
2008-11-02 02:45:28 +01:00
|
|
|
nulls[i] = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
values[i] = (Datum) NULL;
|
|
|
|
}
|
2002-04-11 22:00:18 +02:00
|
|
|
values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
|
2000-07-17 05:05:41 +02:00
|
|
|
values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
|
|
|
|
values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
|
2005-04-12 06:26:34 +02:00
|
|
|
values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
|
2002-03-29 20:06:29 +01:00
|
|
|
values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
|
2000-07-17 05:05:41 +02:00
|
|
|
if (agginitval)
|
2008-03-25 23:42:46 +01:00
|
|
|
values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
2008-11-02 02:45:28 +01:00
|
|
|
nulls[Anum_pg_aggregate_agginitval - 1] = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
tupDesc = aggdesc->rd_att;
|
2002-04-11 22:00:18 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
2002-05-22 00:05:55 +02:00
|
|
|
simple_heap_insert(aggdesc, tup);
|
1999-11-22 18:56:41 +01:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(aggdesc, tup);
|
1999-11-22 18:56:41 +01:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
heap_close(aggdesc, RowExclusiveLock);
|
2002-07-17 00:12:20 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* 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.
|
2002-07-17 00:12:20 +02:00
|
|
|
*/
|
2005-04-14 03:38:22 +02:00
|
|
|
myself.classId = ProcedureRelationId;
|
2002-07-17 00:12:20 +02:00
|
|
|
myself.objectId = procOid;
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
|
|
|
/* Depends on transition function */
|
2005-04-14 03:38:22 +02:00
|
|
|
referenced.classId = ProcedureRelationId;
|
2002-07-17 00:12:20 +02:00
|
|
|
referenced.objectId = transfn;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
/* Depends on final function, if any */
|
|
|
|
if (OidIsValid(finalfn))
|
|
|
|
{
|
2005-04-14 03:38:22 +02:00
|
|
|
referenced.classId = ProcedureRelationId;
|
2002-07-17 00:12:20 +02:00
|
|
|
referenced.objectId = finalfn;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
2005-04-12 06:26:34 +02:00
|
|
|
|
|
|
|
/* Depends on sort operator, if any */
|
|
|
|
if (OidIsValid(sortop))
|
|
|
|
{
|
2005-04-14 22:03:27 +02:00
|
|
|
referenced.classId = OperatorRelationId;
|
2005-04-12 06:26:34 +02:00
|
|
|
referenced.objectId = sortop;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
2012-12-24 00:25:03 +01:00
|
|
|
|
|
|
|
return procOid;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2003-07-01 21:10:53 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
Oid fnOid;
|
|
|
|
bool retset;
|
2008-07-16 03:30:23 +02:00
|
|
|
int nvargs;
|
2013-07-18 17:52:12 +02:00
|
|
|
Oid vatype;
|
2003-07-01 21:10:53 +02:00
|
|
|
Oid *true_oid_array;
|
|
|
|
FuncDetailCode fdresult;
|
2005-01-28 00:42:18 +01:00
|
|
|
AclResult aclresult;
|
2006-07-27 21:52:07 +02:00
|
|
|
int i;
|
2003-07-01 21:10:53 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2005-10-15 04:49:52 +02:00
|
|
|
* function's return value. it also returns the true argument types to
|
|
|
|
* the function.
|
2003-07-01 21:10:53 +02:00
|
|
|
*/
|
2009-10-08 04:39:25 +02:00
|
|
|
fdresult = func_get_detail(fnName, NIL, NIL,
|
|
|
|
nargs, input_types, false, false,
|
2013-07-18 17:52:12 +02:00
|
|
|
&fnOid, rettype, &retset,
|
|
|
|
&nvargs, &vatype,
|
2008-12-04 18:51:28 +01:00
|
|
|
&true_oid_array, NULL);
|
2003-07-01 21:10:53 +02:00
|
|
|
|
|
|
|
/* only valid case is a normal function not returning a set */
|
2003-07-04 04:51:34 +02:00
|
|
|
if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
|
|
errmsg("function %s does not exist",
|
2009-10-08 04:39:25 +02:00
|
|
|
func_signature_string(fnName, nargs,
|
|
|
|
NIL, input_types))));
|
2003-07-04 04:51:34 +02:00
|
|
|
if (retset)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
|
|
errmsg("function %s returns a set",
|
2009-10-08 04:39:25 +02:00
|
|
|
func_signature_string(fnName, nargs,
|
|
|
|
NIL, input_types))));
|
2003-07-01 21:10:53 +02:00
|
|
|
|
|
|
|
/*
|
2008-01-11 19:39:41 +01:00
|
|
|
* 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.
|
2003-07-01 21:10:53 +02:00
|
|
|
*/
|
2008-01-11 19:39:41 +01:00
|
|
|
*rettype = enforce_generic_type_consistency(input_types,
|
|
|
|
true_oid_array,
|
|
|
|
nargs,
|
|
|
|
*rettype,
|
|
|
|
true);
|
2003-07-01 21:10:53 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* func_get_detail will find functions requiring run-time argument type
|
|
|
|
* coercion, but nodeAgg.c isn't prepared to deal with that
|
2003-07-01 21:10:53 +02:00
|
|
|
*/
|
2006-07-27 21:52:07 +02:00
|
|
|
for (i = 0; i < nargs; i++)
|
|
|
|
{
|
2007-04-02 05:49:42 +02:00
|
|
|
if (!IsPolymorphicType(true_oid_array[i]) &&
|
2006-07-27 21:52:07 +02:00
|
|
|
!IsBinaryCoercible(input_types[i], true_oid_array[i]))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
|
|
errmsg("function %s requires run-time type coercion",
|
2010-02-26 03:01:40 +01:00
|
|
|
func_signature_string(fnName, nargs,
|
|
|
|
NIL, true_oid_array))));
|
2006-07-27 21:52:07 +02:00
|
|
|
}
|
2003-07-01 21:10:53 +02:00
|
|
|
|
2005-01-28 00:42:18 +01:00
|
|
|
/* 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));
|
|
|
|
|
2003-07-01 21:10:53 +02:00
|
|
|
return fnOid;
|
|
|
|
}
|