1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_aggregate relation
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.101 2009/01/01 17:23:37 momjian Exp $
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_aggregate.h"
21 #include "catalog/pg_language.h"
22 #include "catalog/pg_operator.h"
23 #include "catalog/pg_proc.h"
24 #include "catalog/pg_proc_fn.h"
25 #include "catalog/pg_type.h"
26 #include "miscadmin.h"
27 #include "parser/parse_coerce.h"
28 #include "parser/parse_func.h"
29 #include "parser/parse_oper.h"
30 #include "utils/acl.h"
31 #include "utils/builtins.h"
32 #include "utils/lsyscache.h"
33 #include "utils/rel.h"
34 #include "utils/syscache.h"
37 static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
45 AggregateCreate(const char *aggName,
53 const char *agginitval)
57 bool nulls[Natts_pg_aggregate];
58 Datum values[Natts_pg_aggregate];
61 Oid finalfn = InvalidOid; /* can be omitted */
62 Oid sortop = InvalidOid; /* can be omitted */
75 /* sanity checks (caller should have caught these) */
77 elog(ERROR, "no aggregate name supplied");
80 elog(ERROR, "aggregate must have a transition function");
82 /* check for polymorphic and INTERNAL arguments */
84 hasInternalArg = false;
85 for (i = 0; i < numArgs; i++)
87 if (IsPolymorphicType(aggArgTypes[i]))
89 else if (aggArgTypes[i] == INTERNALOID)
90 hasInternalArg = true;
94 * If transtype is polymorphic, must have polymorphic argument also; else
95 * we will have no way to deduce the actual transtype.
97 if (IsPolymorphicType(aggTransType) && !hasPolyArg)
99 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
100 errmsg("cannot determine transition data type"),
101 errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
103 /* find the transfn */
104 nargs_transfn = numArgs + 1;
105 fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
106 fnArgs[0] = aggTransType;
107 memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
108 transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
112 * Return type of transfn (possibly after refinement by
113 * enforce_generic_type_consistency, if transtype isn't polymorphic) must
114 * exactly match declared transtype.
116 * In the non-polymorphic-transtype case, it might be okay to allow a
117 * rettype that's binary-coercible to transtype, but I'm not quite
118 * convinced that it's either safe or useful. When transtype is
119 * polymorphic we *must* demand exact equality.
121 if (rettype != aggTransType)
123 (errcode(ERRCODE_DATATYPE_MISMATCH),
124 errmsg("return type of transition function %s is not %s",
125 NameListToString(aggtransfnName),
126 format_type_be(aggTransType))));
128 tup = SearchSysCache(PROCOID,
129 ObjectIdGetDatum(transfn),
131 if (!HeapTupleIsValid(tup))
132 elog(ERROR, "cache lookup failed for function %u", transfn);
133 proc = (Form_pg_proc) GETSTRUCT(tup);
136 * If the transfn is strict and the initval is NULL, make sure first input
137 * type and transtype are the same (or at least binary-compatible), so
138 * that it's OK to use the first input value as the initial transValue.
140 if (proc->proisstrict && agginitval == NULL)
143 !IsBinaryCoercible(aggArgTypes[0], aggTransType))
145 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
146 errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
148 ReleaseSysCache(tup);
150 /* handle finalfn, if supplied */
153 fnArgs[0] = aggTransType;
154 finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
160 * If no finalfn, aggregate result type is type of the state value
162 finaltype = aggTransType;
164 Assert(OidIsValid(finaltype));
167 * If finaltype (i.e. aggregate return type) is polymorphic, inputs must
168 * be polymorphic also, else parser will fail to deduce result type.
169 * (Note: given the previous test on transtype and inputs, this cannot
170 * happen, unless someone has snuck a finalfn definition into the catalogs
171 * that itself violates the rule against polymorphic result with no
172 * polymorphic input.)
174 if (IsPolymorphicType(finaltype) && !hasPolyArg)
176 (errcode(ERRCODE_DATATYPE_MISMATCH),
177 errmsg("cannot determine result data type"),
178 errdetail("An aggregate returning a polymorphic type "
179 "must have at least one polymorphic argument.")));
182 * Also, the return type can't be INTERNAL unless there's at least one
183 * INTERNAL argument. This is the same type-safety restriction we
184 * enforce for regular functions, but at the level of aggregates. We
185 * must test this explicitly because we allow INTERNAL as the transtype.
187 if (finaltype == INTERNALOID && !hasInternalArg)
189 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
190 errmsg("unsafe use of pseudo-type \"internal\""),
191 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
193 /* handle sortop, if supplied */
198 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
199 errmsg("sort operator can only be specified for single-argument aggregates")));
200 sortop = LookupOperName(NULL, aggsortopName,
201 aggArgTypes[0], aggArgTypes[0],
206 * Everything looks okay. Try to create the pg_proc entry for the
207 * aggregate. (This could fail if there's already a conflicting entry.)
210 procOid = ProcedureCreate(aggName,
212 false, /* no replacement */
213 false, /* doesn't return a set */
214 finaltype, /* returnType */
215 INTERNALlanguageId, /* languageObjectId */
216 InvalidOid, /* no validator */
217 "aggregate_dummy", /* placeholder proc */
220 false, /* isWindowFunc */
221 false, /* security invoker (currently not
222 * definable for agg) */
223 false, /* isStrict (not needed for agg) */
224 PROVOLATILE_IMMUTABLE, /* volatility (not
226 buildoidvector(aggArgTypes,
227 numArgs), /* paramTypes */
228 PointerGetDatum(NULL), /* allParamTypes */
229 PointerGetDatum(NULL), /* parameterModes */
230 PointerGetDatum(NULL), /* parameterNames */
231 NIL, /* parameterDefaults */
232 PointerGetDatum(NULL), /* proconfig */
237 * Okay to create the pg_aggregate entry.
240 /* initialize nulls and values */
241 for (i = 0; i < Natts_pg_aggregate; i++)
244 values[i] = (Datum) NULL;
246 values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
247 values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
248 values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
249 values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
250 values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
252 values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
254 nulls[Anum_pg_aggregate_agginitval - 1] = true;
256 aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
257 tupDesc = aggdesc->rd_att;
259 tup = heap_form_tuple(tupDesc, values, nulls);
260 simple_heap_insert(aggdesc, tup);
262 CatalogUpdateIndexes(aggdesc, tup);
264 heap_close(aggdesc, RowExclusiveLock);
267 * Create dependencies for the aggregate (above and beyond those already
268 * made by ProcedureCreate). Note: we don't need an explicit dependency
269 * on aggTransType since we depend on it indirectly through transfn.
271 myself.classId = ProcedureRelationId;
272 myself.objectId = procOid;
273 myself.objectSubId = 0;
275 /* Depends on transition function */
276 referenced.classId = ProcedureRelationId;
277 referenced.objectId = transfn;
278 referenced.objectSubId = 0;
279 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
281 /* Depends on final function, if any */
282 if (OidIsValid(finalfn))
284 referenced.classId = ProcedureRelationId;
285 referenced.objectId = finalfn;
286 referenced.objectSubId = 0;
287 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
290 /* Depends on sort operator, if any */
291 if (OidIsValid(sortop))
293 referenced.classId = OperatorRelationId;
294 referenced.objectId = sortop;
295 referenced.objectSubId = 0;
296 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
301 * lookup_agg_function -- common code for finding both transfn and finalfn
304 lookup_agg_function(List *fnName,
313 FuncDetailCode fdresult;
318 * func_get_detail looks up the function in the catalogs, does
319 * disambiguation for polymorphic functions, handles inheritance, and
320 * returns the funcid and type and set or singleton status of the
321 * function's return value. it also returns the true argument types to
324 fdresult = func_get_detail(fnName, NIL, nargs, input_types, false, false,
325 &fnOid, rettype, &retset, &nvargs,
326 &true_oid_array, NULL);
328 /* only valid case is a normal function not returning a set */
329 if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
331 (errcode(ERRCODE_UNDEFINED_FUNCTION),
332 errmsg("function %s does not exist",
333 func_signature_string(fnName, nargs, input_types))));
336 (errcode(ERRCODE_DATATYPE_MISMATCH),
337 errmsg("function %s returns a set",
338 func_signature_string(fnName, nargs, input_types))));
341 * If there are any polymorphic types involved, enforce consistency, and
342 * possibly refine the result type. It's OK if the result is still
343 * polymorphic at this point, though.
345 *rettype = enforce_generic_type_consistency(input_types,
352 * func_get_detail will find functions requiring run-time argument type
353 * coercion, but nodeAgg.c isn't prepared to deal with that
355 for (i = 0; i < nargs; i++)
357 if (!IsPolymorphicType(true_oid_array[i]) &&
358 !IsBinaryCoercible(input_types[i], true_oid_array[i]))
360 (errcode(ERRCODE_DATATYPE_MISMATCH),
361 errmsg("function %s requires run-time type coercion",
362 func_signature_string(fnName, nargs, true_oid_array))));
365 /* Check aggregate creator has permission to call the function */
366 aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE);
367 if (aclresult != ACLCHECK_OK)
368 aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid));