postgresql/src/backend/catalog/pg_aggregate.c

353 lines
11 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* pg_aggregate.c
* routines to support manipulation of the pg_aggregate relation
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
2005-10-15 04:49:52 +02:00
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.76 2005/10/15 02:49:14 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
1999-07-16 07:00:38 +02:00
#include "catalog/pg_aggregate.h"
#include "catalog/pg_language.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "utils/acl.h"
1999-07-16 07:00:38 +02:00
#include "utils/builtins.h"
#include "utils/lsyscache.h"
1999-07-16 07:00:38 +02:00
#include "utils/syscache.h"
2003-08-04 02:43:34 +02:00
static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
Oid *rettype);
/*
* AggregateCreate
*/
void
AggregateCreate(const char *aggName,
Oid aggNamespace,
Oid aggBaseType,
List *aggtransfnName,
List *aggfinalfnName,
List *aggsortopName,
Oid aggTransType,
const char *agginitval)
{
Relation aggdesc;
HeapTuple tup;
char nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate];
Form_pg_proc proc;
Oid transfn;
2001-03-22 05:01:46 +01:00
Oid finalfn = InvalidOid; /* can be omitted */
Oid sortop = InvalidOid; /* can be omitted */
Oid rettype;
Oid finaltype;
Oid fnArgs[2]; /* we only deal with 1- and 2-arg fns */
int nargs_transfn;
Oid procOid;
TupleDesc tupDesc;
int i;
2002-09-04 22:31:48 +02:00
ObjectAddress myself,
referenced;
/* sanity checks (caller should have caught these) */
if (!aggName)
2001-08-10 17:49:39 +02:00
elog(ERROR, "no aggregate name supplied");
if (!aggtransfnName)
2001-08-10 17:49:39 +02:00
elog(ERROR, "aggregate must have a transition function");
/*
2005-10-15 04:49:52 +02:00
* If transtype is polymorphic, basetype must be polymorphic also; else we
* will have no way to deduce the actual transtype.
*/
if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) &&
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine transition data type"),
2005-10-15 04:49:52 +02:00
errdetail("An aggregate using \"anyarray\" or \"anyelement\" as "
"transition type must have one of them as its base type.")));
/* handle transfn */
fnArgs[0] = aggTransType;
if (aggBaseType == ANYOID)
nargs_transfn = 1;
else
{
fnArgs[1] = aggBaseType;
nargs_transfn = 2;
}
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.
*
2005-10-15 04:49:52 +02: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.
*/
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);
2001-03-22 05:01:46 +01:00
/*
2005-10-15 04:49:52 +02:00
* 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
* it's OK to use the first input value as the initial transValue.
*/
if (proc->proisstrict && agginitval == NULL)
{
if (!IsBinaryCoercible(aggBaseType, 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));
/*
2005-10-15 04:49:52 +02:00
* If finaltype (i.e. aggregate return type) is polymorphic, basetype must
* be polymorphic also, else parser will fail to deduce result type.
* (Note: given the previous test on transtype and basetype, 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))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot determine result data type"),
2005-10-15 04:49:52 +02:00
errdetail("An aggregate returning \"anyarray\" or \"anyelement\" "
"must have one of them as its base type.")));
/* handle sortop, if supplied */
if (aggsortopName)
sortop = LookupOperName(aggsortopName,
aggBaseType, aggBaseType,
false);
/*
* 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.)
*/
fnArgs[0] = aggBaseType;
procOid = ProcedureCreate(aggName,
aggNamespace,
2002-09-04 22:31:48 +02:00
false, /* no replacement */
false, /* doesn't return a set */
finaltype, /* returnType */
INTERNALlanguageId, /* languageObjectId */
InvalidOid, /* no validator */
2002-09-04 22:31:48 +02:00
"aggregate_dummy", /* placeholder proc */
"-", /* probin */
true, /* isAgg */
false, /* security invoker (currently not
* definable for agg) */
false, /* isStrict (not needed for agg) */
PROVOLATILE_IMMUTABLE, /* volatility (not
* needed for agg) */
2005-10-15 04:49:52 +02:00
buildoidvector(fnArgs, 1), /* paramTypes */
PointerGetDatum(NULL), /* allParamTypes */
PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL)); /* parameterNames */
/*
* Okay to create the pg_aggregate entry.
*/
/* initialize nulls and values */
for (i = 0; i < Natts_pg_aggregate; i++)
{
nulls[i] = ' ';
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] =
DirectFunctionCall1(textin, CStringGetDatum(agginitval));
else
nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
tupDesc = aggdesc->rd_att;
tup = heap_formtuple(tupDesc, values, nulls);
simple_heap_insert(aggdesc, tup);
CatalogUpdateIndexes(aggdesc, tup);
heap_close(aggdesc, RowExclusiveLock);
/*
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.
*/
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);
}
}
/*
* 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;
Oid *true_oid_array;
FuncDetailCode fdresult;
AclResult aclresult;
/*
* 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.
*/
fdresult = func_get_detail(fnName, NIL, nargs, input_types,
&fnOid, rettype, &retset,
&true_oid_array);
/* 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",
2005-10-15 04:49:52 +02:00
func_signature_string(fnName, nargs, input_types))));
if (retset)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s returns a set",
2005-10-15 04:49:52 +02:00
func_signature_string(fnName, nargs, input_types))));
/*
2005-10-15 04:49:52 +02:00
* 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 ||
2005-10-15 04:49:52 +02:00
(input_types[1] == ANYARRAYOID || input_types[1] == ANYELEMENTOID)))
{
/* nothing to check here */
}
else
{
*rettype = enforce_generic_type_consistency(input_types,
true_oid_array,
nargs,
*rettype);
}
/*
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
*/
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",
2005-10-15 04:49:52 +02:00
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",
2005-10-15 04:49:52 +02:00
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;
}