postgresql/src/backend/catalog/pg_aggregate.c
Alvaro Herrera 2f5c9d9c9c Tweak catalog indexing abstraction for upcoming WARM
Split the existing CatalogUpdateIndexes into two different routines,
CatalogTupleInsert and CatalogTupleUpdate, which do both the heap
insert/update plus the index update.  This removes over 300 lines of
boilerplate code all over src/backend/catalog/ and src/backend/commands.
The resulting code is much more pleasing to the eye.

Also, by encapsulating what happens in detail during an UPDATE, this
facilitates the upcoming WARM patch, which is going to add a few more
lines to the update case making the boilerplate even more boring.

The original CatalogUpdateIndexes is removed; there was only one use
left, and since it's just three lines, we can as well expand it in place
there.  We could keep it, but WARM is going to break all the UPDATE
out-of-core callsites anyway, so there seems to be no benefit in doing
so.

Author: Pavan Deolasee
Discussion: https://www.postgr.es/m/CABOikdOcFYSZ4vA2gYfs=M2cdXzXX4qGHeEiW3fu9PCfkHLa2A@mail.gmail.com
2017-01-31 18:42:24 -03:00

867 lines
28 KiB
C

/*-------------------------------------------------------------------------
*
* pg_aggregate.c
* routines to support manipulation of the pg_aggregate relation
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/catalog/pg_aggregate.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "access/htup_details.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 variadicArgType,
Oid *rettype);
/*
* AggregateCreate
*/
ObjectAddress
AggregateCreate(const char *aggName,
Oid aggNamespace,
char aggKind,
int numArgs,
int numDirectArgs,
oidvector *parameterTypes,
Datum allParameterTypes,
Datum parameterModes,
Datum parameterNames,
List *parameterDefaults,
Oid variadicArgType,
List *aggtransfnName,
List *aggfinalfnName,
List *aggcombinefnName,
List *aggserialfnName,
List *aggdeserialfnName,
List *aggmtransfnName,
List *aggminvtransfnName,
List *aggmfinalfnName,
bool finalfnExtraArgs,
bool mfinalfnExtraArgs,
List *aggsortopName,
Oid aggTransType,
int32 aggTransSpace,
Oid aggmTransType,
int32 aggmTransSpace,
const char *agginitval,
const char *aggminitval,
char proparallel)
{
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 combinefn = InvalidOid; /* can be omitted */
Oid serialfn = InvalidOid; /* can be omitted */
Oid deserialfn = InvalidOid; /* can be omitted */
Oid mtransfn = InvalidOid; /* can be omitted */
Oid minvtransfn = InvalidOid; /* can be omitted */
Oid mfinalfn = InvalidOid; /* can be omitted */
Oid sortop = InvalidOid; /* can be omitted */
Oid *aggArgTypes = parameterTypes->values;
bool hasPolyArg;
bool hasInternalArg;
bool mtransIsStrict = false;
Oid rettype;
Oid finaltype;
Oid fnArgs[FUNC_MAX_ARGS];
int nargs_transfn;
int nargs_finalfn;
Oid procOid;
TupleDesc tupDesc;
int i;
ObjectAddress myself,
referenced;
AclResult aclresult;
/* 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");
if (numDirectArgs < 0 || numDirectArgs > numArgs)
elog(ERROR, "incorrect number of direct args for aggregate");
/*
* Aggregates can have at most FUNC_MAX_ARGS-1 args, else the transfn
* and/or finalfn will be unrepresentable in pg_proc. We must check now
* to protect fixed-size arrays here and possibly in called functions.
*/
if (numArgs < 0 || numArgs > FUNC_MAX_ARGS - 1)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg_plural("aggregates cannot have more than %d argument",
"aggregates cannot have more than %d arguments",
FUNC_MAX_ARGS - 1,
FUNC_MAX_ARGS - 1)));
/* 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.")));
/*
* Likewise for moving-aggregate transtype, if any
*/
if (OidIsValid(aggmTransType) &&
IsPolymorphicType(aggmTransType) && !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.")));
/*
* An ordered-set aggregate that is VARIADIC must be VARIADIC ANY. In
* principle we could support regular variadic types, but it would make
* things much more complicated because we'd have to assemble the correct
* subsets of arguments into array values. Since no standard aggregates
* have use for such a case, we aren't bothering for now.
*/
if (AGGKIND_IS_ORDERED_SET(aggKind) && OidIsValid(variadicArgType) &&
variadicArgType != ANYOID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("a variadic ordered-set aggregate must use VARIADIC type ANY")));
/*
* If it's a hypothetical-set aggregate, there must be at least as many
* direct arguments as aggregated ones, and the last N direct arguments
* must match the aggregated ones in type. (We have to check this again
* when the aggregate is called, in case ANY is involved, but it makes
* sense to reject the aggregate definition now if the declared arg types
* don't match up.) It's unconditionally OK if numDirectArgs == numArgs,
* indicating that the grammar merged identical VARIADIC entries from both
* lists. Otherwise, if the agg is VARIADIC, then we had VARIADIC only on
* the aggregated side, which is not OK. Otherwise, insist on the last N
* parameter types on each side matching exactly.
*/
if (aggKind == AGGKIND_HYPOTHETICAL &&
numDirectArgs < numArgs)
{
int numAggregatedArgs = numArgs - numDirectArgs;
if (OidIsValid(variadicArgType) ||
numDirectArgs < numAggregatedArgs ||
memcmp(aggArgTypes + (numDirectArgs - numAggregatedArgs),
aggArgTypes + numDirectArgs,
numAggregatedArgs * sizeof(Oid)) != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("a hypothetical-set aggregate must have direct arguments matching its aggregated arguments")));
}
/*
* Find the transfn. For ordinary aggs, it takes the transtype plus all
* aggregate arguments. For ordered-set aggs, it takes the transtype plus
* all aggregated args, but not direct args. However, we have to treat
* specially the case where a trailing VARIADIC item is considered to
* cover both direct and aggregated args.
*/
if (AGGKIND_IS_ORDERED_SET(aggKind))
{
if (numDirectArgs < numArgs)
nargs_transfn = numArgs - numDirectArgs + 1;
else
{
/* special case with VARIADIC last arg */
Assert(variadicArgType != InvalidOid);
nargs_transfn = 2;
}
fnArgs[0] = aggTransType;
memcpy(fnArgs + 1, aggArgTypes + (numArgs - (nargs_transfn - 1)),
(nargs_transfn - 1) * sizeof(Oid));
}
else
{
nargs_transfn = numArgs + 1;
fnArgs[0] = aggTransType;
memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
}
transfn = lookup_agg_function(aggtransfnName, nargs_transfn,
fnArgs, variadicArgType,
&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 = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for function %u", transfn);
proc = (Form_pg_proc) GETSTRUCT(tup);
/*
* 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 moving-aggregate transfn, if supplied */
if (aggmtransfnName)
{
/*
* The arguments are the same as for the regular transfn, except that
* the transition data type might be different. So re-use the fnArgs
* values set up above, except for that one.
*/
Assert(OidIsValid(aggmTransType));
fnArgs[0] = aggmTransType;
mtransfn = lookup_agg_function(aggmtransfnName, nargs_transfn,
fnArgs, variadicArgType,
&rettype);
/* As above, return type must exactly match declared mtranstype. */
if (rettype != aggmTransType)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("return type of transition function %s is not %s",
NameListToString(aggmtransfnName),
format_type_be(aggmTransType))));
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(mtransfn));
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for function %u", mtransfn);
proc = (Form_pg_proc) GETSTRUCT(tup);
/*
* If the mtransfn is strict and the minitval is NULL, check first
* input type and mtranstype are binary-compatible.
*/
if (proc->proisstrict && aggminitval == NULL)
{
if (numArgs < 1 ||
!IsBinaryCoercible(aggArgTypes[0], aggmTransType))
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")));
}
/* Remember if mtransfn is strict; we may need this below */
mtransIsStrict = proc->proisstrict;
ReleaseSysCache(tup);
}
/* handle minvtransfn, if supplied */
if (aggminvtransfnName)
{
/*
* This must have the same number of arguments with the same types as
* the forward transition function, so just re-use the fnArgs data.
*/
Assert(aggmtransfnName);
minvtransfn = lookup_agg_function(aggminvtransfnName, nargs_transfn,
fnArgs, variadicArgType,
&rettype);
/* As above, return type must exactly match declared mtranstype. */
if (rettype != aggmTransType)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("return type of inverse transition function %s is not %s",
NameListToString(aggminvtransfnName),
format_type_be(aggmTransType))));
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(minvtransfn));
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for function %u", minvtransfn);
proc = (Form_pg_proc) GETSTRUCT(tup);
/*
* We require the strictness settings of the forward and inverse
* transition functions to agree. This saves having to handle
* assorted special cases at execution time.
*/
if (proc->proisstrict != mtransIsStrict)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("strictness of aggregate's forward and inverse transition functions must match")));
ReleaseSysCache(tup);
}
/* handle finalfn, if supplied */
if (aggfinalfnName)
{
/*
* If finalfnExtraArgs is specified, the transfn takes the transtype
* plus all args; otherwise, it just takes the transtype plus any
* direct args. (Non-direct args are useless at runtime, and are
* actually passed as NULLs, but we may need them in the function
* signature to allow resolution of a polymorphic agg's result type.)
*/
Oid ffnVariadicArgType = variadicArgType;
fnArgs[0] = aggTransType;
memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
if (finalfnExtraArgs)
nargs_finalfn = numArgs + 1;
else
{
nargs_finalfn = numDirectArgs + 1;
if (numDirectArgs < numArgs)
{
/* variadic argument doesn't affect finalfn */
ffnVariadicArgType = InvalidOid;
}
}
finalfn = lookup_agg_function(aggfinalfnName, nargs_finalfn,
fnArgs, ffnVariadicArgType,
&finaltype);
/*
* When finalfnExtraArgs is specified, the finalfn will certainly be
* passed at least one null argument, so complain if it's strict.
* Nothing bad would happen at runtime (you'd just get a null result),
* but it's surely not what the user wants, so let's complain now.
*/
if (finalfnExtraArgs && func_strict(finalfn))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("final function with extra arguments must not be declared STRICT")));
}
else
{
/*
* If no finalfn, aggregate result type is type of the state value
*/
finaltype = aggTransType;
}
Assert(OidIsValid(finaltype));
/* handle the combinefn, if supplied */
if (aggcombinefnName)
{
Oid combineType;
/*
* Combine function must have 2 argument, each of which is the trans
* type
*/
fnArgs[0] = aggTransType;
fnArgs[1] = aggTransType;
combinefn = lookup_agg_function(aggcombinefnName, 2, fnArgs,
variadicArgType, &combineType);
/* Ensure the return type matches the aggregates trans type */
if (combineType != aggTransType)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("return type of combine function %s is not %s",
NameListToString(aggcombinefnName),
format_type_be(aggTransType))));
/*
* A combine function to combine INTERNAL states must accept nulls and
* ensure that the returned state is in the correct memory context.
*/
if (aggTransType == INTERNALOID && func_strict(combinefn))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("combine function with \"%s\" transition type must not be declared STRICT",
format_type_be(aggTransType))));
}
/*
* Validate the serialization function, if present.
*/
if (aggserialfnName)
{
fnArgs[0] = INTERNALOID;
serialfn = lookup_agg_function(aggserialfnName, 1,
fnArgs, variadicArgType,
&rettype);
if (rettype != BYTEAOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("return type of serialization function %s is not %s",
NameListToString(aggserialfnName),
format_type_be(BYTEAOID))));
}
/*
* Validate the deserialization function, if present.
*/
if (aggdeserialfnName)
{
fnArgs[0] = BYTEAOID;
fnArgs[1] = INTERNALOID; /* dummy argument for type safety */
deserialfn = lookup_agg_function(aggdeserialfnName, 2,
fnArgs, variadicArgType,
&rettype);
if (rettype != INTERNALOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("return type of deserialization function %s is not %s",
NameListToString(aggdeserialfnName),
format_type_be(INTERNALOID))));
}
/*
* 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.")));
/*
* If a moving-aggregate implementation is supplied, look up its finalfn
* if any, and check that the implied aggregate result type matches the
* plain implementation.
*/
if (OidIsValid(aggmTransType))
{
/* handle finalfn, if supplied */
if (aggmfinalfnName)
{
/*
* The arguments are figured the same way as for the regular
* finalfn, but using aggmTransType and mfinalfnExtraArgs.
*/
Oid ffnVariadicArgType = variadicArgType;
fnArgs[0] = aggmTransType;
memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
if (mfinalfnExtraArgs)
nargs_finalfn = numArgs + 1;
else
{
nargs_finalfn = numDirectArgs + 1;
if (numDirectArgs < numArgs)
{
/* variadic argument doesn't affect finalfn */
ffnVariadicArgType = InvalidOid;
}
}
mfinalfn = lookup_agg_function(aggmfinalfnName, nargs_finalfn,
fnArgs, ffnVariadicArgType,
&rettype);
/* As above, check strictness if mfinalfnExtraArgs is given */
if (mfinalfnExtraArgs && func_strict(mfinalfn))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("final function with extra arguments must not be declared STRICT")));
}
else
{
/*
* If no finalfn, aggregate result type is type of the state value
*/
rettype = aggmTransType;
}
Assert(OidIsValid(rettype));
if (rettype != finaltype)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("moving-aggregate implementation returns type %s, but plain implementation returns type %s",
format_type_be(aggmTransType),
format_type_be(aggTransType))));
}
/* 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);
}
/*
* permission checks on used types
*/
for (i = 0; i < numArgs; i++)
{
aclresult = pg_type_aclcheck(aggArgTypes[i], GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, aggArgTypes[i]);
}
aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, aggTransType);
if (OidIsValid(aggmTransType))
{
aclresult = pg_type_aclcheck(aggmTransType, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, aggmTransType);
}
aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, finaltype);
/*
* Everything looks okay. Try to create the pg_proc entry for the
* aggregate. (This could fail if there's already a conflicting entry.)
*/
myself = ProcedureCreate(aggName,
aggNamespace,
false, /* no replacement */
false, /* doesn't return a set */
finaltype, /* returnType */
GetUserId(), /* proowner */
INTERNALlanguageId, /* languageObjectId */
InvalidOid, /* no validator */
"aggregate_dummy", /* placeholder proc */
NULL, /* probin */
true, /* isAgg */
false, /* isWindowFunc */
false, /* security invoker (currently not
* definable for agg) */
false, /* isLeakProof */
false, /* isStrict (not needed for agg) */
PROVOLATILE_IMMUTABLE, /* volatility (not
* needed for agg) */
proparallel,
parameterTypes, /* paramTypes */
allParameterTypes, /* allParamTypes */
parameterModes, /* parameterModes */
parameterNames, /* parameterNames */
parameterDefaults, /* parameterDefaults */
PointerGetDatum(NULL), /* trftypes */
PointerGetDatum(NULL), /* proconfig */
1, /* procost */
0); /* prorows */
procOid = myself.objectId;
/*
* 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_aggkind - 1] = CharGetDatum(aggKind);
values[Anum_pg_aggregate_aggnumdirectargs - 1] = Int16GetDatum(numDirectArgs);
values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
values[Anum_pg_aggregate_aggcombinefn - 1] = ObjectIdGetDatum(combinefn);
values[Anum_pg_aggregate_aggserialfn - 1] = ObjectIdGetDatum(serialfn);
values[Anum_pg_aggregate_aggdeserialfn - 1] = ObjectIdGetDatum(deserialfn);
values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(mtransfn);
values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(minvtransfn);
values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(mfinalfn);
values[Anum_pg_aggregate_aggfinalextra - 1] = BoolGetDatum(finalfnExtraArgs);
values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(mfinalfnExtraArgs);
values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
values[Anum_pg_aggregate_aggmtranstype - 1] = ObjectIdGetDatum(aggmTransType);
values[Anum_pg_aggregate_aggmtransspace - 1] = Int32GetDatum(aggmTransSpace);
if (agginitval)
values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
else
nulls[Anum_pg_aggregate_agginitval - 1] = true;
if (aggminitval)
values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
else
nulls[Anum_pg_aggregate_aggminitval - 1] = true;
aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
tupDesc = aggdesc->rd_att;
tup = heap_form_tuple(tupDesc, values, nulls);
CatalogTupleInsert(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.
* Likewise for aggmTransType using the mtransfunc, if it exists.
*/
/* 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 combine function, if any */
if (OidIsValid(combinefn))
{
referenced.classId = ProcedureRelationId;
referenced.objectId = combinefn;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* Depends on serialization function, if any */
if (OidIsValid(serialfn))
{
referenced.classId = ProcedureRelationId;
referenced.objectId = serialfn;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* Depends on deserialization function, if any */
if (OidIsValid(deserialfn))
{
referenced.classId = ProcedureRelationId;
referenced.objectId = deserialfn;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* Depends on forward transition function, if any */
if (OidIsValid(mtransfn))
{
referenced.classId = ProcedureRelationId;
referenced.objectId = mtransfn;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* Depends on inverse transition function, if any */
if (OidIsValid(minvtransfn))
{
referenced.classId = ProcedureRelationId;
referenced.objectId = minvtransfn;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* Depends on final function, if any */
if (OidIsValid(mfinalfn))
{
referenced.classId = ProcedureRelationId;
referenced.objectId = mfinalfn;
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);
}
return myself;
}
/*
* lookup_agg_function
* common code for finding transfn, invtransfn, finalfn, and combinefn
*
* Returns OID of function, and stores its return type into *rettype
*
* NB: must not scribble on input_types[], as we may re-use those
*/
static Oid
lookup_agg_function(List *fnName,
int nargs,
Oid *input_types,
Oid variadicArgType,
Oid *rettype)
{
Oid fnOid;
bool retset;
int nvargs;
Oid vatype;
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, NIL,
nargs, input_types, false, false,
&fnOid, rettype, &retset,
&nvargs, &vatype,
&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,
NIL, input_types))));
if (retset)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s returns a set",
func_signature_string(fnName, nargs,
NIL, input_types))));
/*
* If the agg is declared to take VARIADIC ANY, the underlying functions
* had better be declared that way too, else they may receive too many
* parameters; but func_get_detail would have been happy with plain ANY.
* (Probably nothing very bad would happen, but it wouldn't work as the
* user expects.) Other combinations should work without any special
* pushups, given that we told func_get_detail not to expand VARIADIC.
*/
if (variadicArgType == ANYOID && vatype != ANYOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s must accept VARIADIC ANY to be used in this aggregate",
func_signature_string(fnName, nargs,
NIL, 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 (!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,
NIL, 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;
}