summaryrefslogtreecommitdiffstats
path: root/src/backend/catalog/pg_aggregate.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/catalog/pg_aggregate.c
parentInitial commit. (diff)
downloadpostgresql-14-upstream.tar.xz
postgresql-14-upstream.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/catalog/pg_aggregate.c')
-rw-r--r--src/backend/catalog/pg_aggregate.c914
1 files changed, 914 insertions, 0 deletions
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
new file mode 100644
index 0000000..1f63d80
--- /dev/null
+++ b/src/backend/catalog/pg_aggregate.c
@@ -0,0 +1,914 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_aggregate.c
+ * routines to support manipulation of the pg_aggregate relation
+ *
+ * Portions Copyright (c) 1996-2021, 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/htup_details.h"
+#include "access/table.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_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,
+ bool replace,
+ 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,
+ char finalfnModify,
+ char mfinalfnModify,
+ List *aggsortopName,
+ Oid aggTransType,
+ int32 aggTransSpace,
+ Oid aggmTransType,
+ int32 aggmTransSpace,
+ const char *agginitval,
+ const char *aggminitval,
+ char proparallel)
+{
+ Relation aggdesc;
+ HeapTuple tup;
+ HeapTuple oldtup;
+ bool nulls[Natts_pg_aggregate];
+ Datum values[Natts_pg_aggregate];
+ bool replaces[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 mtransIsStrict = false;
+ Oid rettype;
+ Oid finaltype;
+ Oid fnArgs[FUNC_MAX_ARGS];
+ int nargs_transfn;
+ int nargs_finalfn;
+ Oid procOid;
+ TupleDesc tupDesc;
+ char *detailmsg;
+ int i;
+ ObjectAddress myself,
+ referenced;
+ ObjectAddresses *addrs;
+ 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 arguments 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)));
+
+ /*
+ * If transtype is polymorphic, must have polymorphic argument also; else
+ * we will have no way to deduce the actual transtype.
+ */
+ detailmsg = check_valid_polymorphic_signature(aggTransType,
+ aggArgTypes,
+ numArgs);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot determine transition data type"),
+ errdetail_internal("%s", detailmsg)));
+
+ /*
+ * Likewise for moving-aggregate transtype, if any
+ */
+ if (OidIsValid(aggmTransType))
+ {
+ detailmsg = check_valid_polymorphic_signature(aggmTransType,
+ aggArgTypes,
+ numArgs);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot determine transition data type"),
+ errdetail_internal("%s", detailmsg)));
+ }
+
+ /*
+ * 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 arguments, each of which is the trans
+ * type. VARIADIC doesn't affect it.
+ */
+ fnArgs[0] = aggTransType;
+ fnArgs[1] = aggTransType;
+
+ combinefn = lookup_agg_function(aggcombinefnName, 2,
+ fnArgs, InvalidOid,
+ &combineType);
+
+ /* Ensure the return type matches the aggregate's 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. We
+ * cannot directly check the latter, but we can check the former.
+ */
+ if (aggTransType == INTERNALOID && func_strict(combinefn))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("combine function with transition type %s must not be declared STRICT",
+ format_type_be(aggTransType))));
+ }
+
+ /*
+ * Validate the serialization function, if present.
+ */
+ if (aggserialfnName)
+ {
+ /* signature is always serialize(internal) returns bytea */
+ fnArgs[0] = INTERNALOID;
+
+ serialfn = lookup_agg_function(aggserialfnName, 1,
+ fnArgs, InvalidOid,
+ &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)
+ {
+ /* signature is always deserialize(bytea, internal) returns internal */
+ fnArgs[0] = BYTEAOID;
+ fnArgs[1] = INTERNALOID; /* dummy argument for type safety */
+
+ deserialfn = lookup_agg_function(aggdeserialfnName, 2,
+ fnArgs, InvalidOid,
+ &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.)
+ */
+ detailmsg = check_valid_polymorphic_signature(finaltype,
+ aggArgTypes,
+ numArgs);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot determine result data type"),
+ errdetail_internal("%s", detailmsg)));
+
+ /*
+ * 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.
+ */
+ detailmsg = check_valid_internal_signature(finaltype,
+ aggArgTypes,
+ numArgs);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("unsafe use of pseudo-type \"internal\""),
+ errdetail_internal("%s", detailmsg)));
+
+ /*
+ * 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(rettype),
+ format_type_be(finaltype))));
+ }
+
+ /* 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,
+ replace, /* maybe replacement */
+ false, /* doesn't return a set */
+ finaltype, /* returnType */
+ GetUserId(), /* proowner */
+ INTERNALlanguageId, /* languageObjectId */
+ InvalidOid, /* no validator */
+ "aggregate_dummy", /* placeholder (no such proc) */
+ NULL, /* probin */
+ NULL, /* prosqlbody */
+ PROKIND_AGGREGATE,
+ 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 */
+ InvalidOid, /* no prosupport */
+ 1, /* procost */
+ 0); /* prorows */
+ procOid = myself.objectId;
+
+ /*
+ * Okay to create the pg_aggregate entry.
+ */
+ aggdesc = table_open(AggregateRelationId, RowExclusiveLock);
+ tupDesc = aggdesc->rd_att;
+
+ /* initialize nulls and values */
+ for (i = 0; i < Natts_pg_aggregate; i++)
+ {
+ nulls[i] = false;
+ values[i] = (Datum) NULL;
+ replaces[i] = true;
+ }
+ 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_aggfinalmodify - 1] = CharGetDatum(finalfnModify);
+ values[Anum_pg_aggregate_aggmfinalmodify - 1] = CharGetDatum(mfinalfnModify);
+ 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;
+
+ if (replace)
+ oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid));
+ else
+ oldtup = NULL;
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ Form_pg_aggregate oldagg = (Form_pg_aggregate) GETSTRUCT(oldtup);
+
+ /*
+ * If we're replacing an existing entry, we need to validate that
+ * we're not changing anything that would break callers. Specifically
+ * we must not change aggkind or aggnumdirectargs, which affect how an
+ * aggregate call is treated in parse analysis.
+ */
+ if (aggKind != oldagg->aggkind)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot change routine kind"),
+ (oldagg->aggkind == AGGKIND_NORMAL ?
+ errdetail("\"%s\" is an ordinary aggregate function.", aggName) :
+ oldagg->aggkind == AGGKIND_ORDERED_SET ?
+ errdetail("\"%s\" is an ordered-set aggregate.", aggName) :
+ oldagg->aggkind == AGGKIND_HYPOTHETICAL ?
+ errdetail("\"%s\" is a hypothetical-set aggregate.", aggName) :
+ 0)));
+ if (numDirectArgs != oldagg->aggnumdirectargs)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot change number of direct arguments of an aggregate function")));
+
+ replaces[Anum_pg_aggregate_aggfnoid - 1] = false;
+ replaces[Anum_pg_aggregate_aggkind - 1] = false;
+ replaces[Anum_pg_aggregate_aggnumdirectargs - 1] = false;
+
+ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
+ CatalogTupleUpdate(aggdesc, &tup->t_self, tup);
+ ReleaseSysCache(oldtup);
+ }
+ else
+ {
+ tup = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(aggdesc, tup);
+ }
+
+ table_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 mtransfn, if it exists.
+ *
+ * If we're replacing an existing definition, ProcedureCreate deleted all
+ * our existing dependencies, so we have to do the same things here either
+ * way.
+ */
+
+ addrs = new_object_addresses();
+
+ /* Depends on transition function */
+ ObjectAddressSet(referenced, ProcedureRelationId, transfn);
+ add_exact_object_address(&referenced, addrs);
+
+ /* Depends on final function, if any */
+ if (OidIsValid(finalfn))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, finalfn);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* Depends on combine function, if any */
+ if (OidIsValid(combinefn))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, combinefn);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* Depends on serialization function, if any */
+ if (OidIsValid(serialfn))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, serialfn);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* Depends on deserialization function, if any */
+ if (OidIsValid(deserialfn))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, deserialfn);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* Depends on forward transition function, if any */
+ if (OidIsValid(mtransfn))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, mtransfn);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* Depends on inverse transition function, if any */
+ if (OidIsValid(minvtransfn))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, minvtransfn);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* Depends on final function, if any */
+ if (OidIsValid(mfinalfn))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, mfinalfn);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* Depends on sort operator, if any */
+ if (OidIsValid(sortop))
+ {
+ ObjectAddressSet(referenced, OperatorRelationId, sortop);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+ free_object_addresses(addrs);
+ return myself;
+}
+
+/*
+ * lookup_agg_function
+ * common code for finding aggregate support functions
+ *
+ * fnName: possibly-schema-qualified function name
+ * nargs, input_types: expected function argument types
+ * variadicArgType: type of variadic argument if any, else InvalidOid
+ *
+ * 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, 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, OBJECT_FUNCTION, get_func_name(fnOid));
+
+ return fnOid;
+}