1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_aggregate relation
6 * Portions Copyright (c) 1996-2004, 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.67 2004/08/29 04:12:28 momjian Exp $
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "catalog/catname.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_aggregate.h"
23 #include "catalog/pg_language.h"
24 #include "catalog/pg_proc.h"
25 #include "optimizer/cost.h"
26 #include "parser/parse_coerce.h"
27 #include "parser/parse_func.h"
28 #include "utils/builtins.h"
29 #include "utils/syscache.h"
32 static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
40 AggregateCreate(const char *aggName,
46 const char *agginitval)
50 char nulls[Natts_pg_aggregate];
51 Datum values[Natts_pg_aggregate];
54 Oid finalfn = InvalidOid; /* can be omitted */
57 Oid fnArgs[FUNC_MAX_ARGS];
65 /* sanity checks (caller should have caught these) */
67 elog(ERROR, "no aggregate name supplied");
70 elog(ERROR, "aggregate must have a transition function");
73 * If transtype is polymorphic, basetype must be polymorphic also;
74 * else we will have no way to deduce the actual transtype.
76 if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) &&
77 !(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
79 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
80 errmsg("cannot determine transition data type"),
81 errdetail("An aggregate using \"anyarray\" or \"anyelement\" as "
82 "transition type must have one of them as its base type.")));
85 MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
86 fnArgs[0] = aggTransType;
87 if (aggBaseType == ANYOID)
91 fnArgs[1] = aggBaseType;
94 transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
98 * Return type of transfn (possibly after refinement by
99 * enforce_generic_type_consistency, if transtype isn't polymorphic)
100 * must exactly match declared transtype.
102 * In the non-polymorphic-transtype case, it might be okay to allow a
103 * rettype that's binary-coercible to transtype, but I'm not quite
104 * convinced that it's either safe or useful. When transtype is
105 * polymorphic we *must* demand exact equality.
107 if (rettype != aggTransType)
109 (errcode(ERRCODE_DATATYPE_MISMATCH),
110 errmsg("return type of transition function %s is not %s",
111 NameListToString(aggtransfnName),
112 format_type_be(aggTransType))));
114 tup = SearchSysCache(PROCOID,
115 ObjectIdGetDatum(transfn),
117 if (!HeapTupleIsValid(tup))
118 elog(ERROR, "cache lookup failed for function %u", transfn);
119 proc = (Form_pg_proc) GETSTRUCT(tup);
122 * If the transfn is strict and the initval is NULL, make sure input
123 * type and transtype are the same (or at least binary-compatible), so
124 * that it's OK to use the first input value as the initial
127 if (proc->proisstrict && agginitval == NULL)
129 if (!IsBinaryCoercible(aggBaseType, aggTransType))
131 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
132 errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
134 ReleaseSysCache(tup);
136 /* handle finalfn, if supplied */
139 MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
140 fnArgs[0] = aggTransType;
141 finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
147 * If no finalfn, aggregate result type is type of the state value
149 finaltype = aggTransType;
151 Assert(OidIsValid(finaltype));
154 * If finaltype (i.e. aggregate return type) is polymorphic, basetype
155 * must be polymorphic also, else parser will fail to deduce result
156 * type. (Note: given the previous test on transtype and basetype,
157 * this cannot happen, unless someone has snuck a finalfn definition
158 * into the catalogs that itself violates the rule against polymorphic
159 * result with no polymorphic input.)
161 if ((finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) &&
162 !(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
164 (errcode(ERRCODE_DATATYPE_MISMATCH),
165 errmsg("cannot determine result data type"),
166 errdetail("An aggregate returning \"anyarray\" or \"anyelement\" "
167 "must have one of them as its base type.")));
170 * Everything looks okay. Try to create the pg_proc entry for the
171 * aggregate. (This could fail if there's already a conflicting
174 MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
175 fnArgs[0] = aggBaseType;
177 procOid = ProcedureCreate(aggName,
179 false, /* no replacement */
180 false, /* doesn't return a set */
181 finaltype, /* returnType */
182 INTERNALlanguageId, /* languageObjectId */
184 "aggregate_dummy", /* placeholder proc */
187 false, /* security invoker (currently not
188 * definable for agg) */
189 false, /* isStrict (not needed for agg) */
190 PROVOLATILE_IMMUTABLE, /* volatility (not
192 1, /* parameterCount */
193 fnArgs, /* parameterTypes */
194 NULL); /* parameterNames */
197 * Okay to create the pg_aggregate entry.
200 /* initialize nulls and values */
201 for (i = 0; i < Natts_pg_aggregate; i++)
204 values[i] = (Datum) NULL;
206 values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
207 values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
208 values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
209 values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
211 values[Anum_pg_aggregate_agginitval - 1] =
212 DirectFunctionCall1(textin, CStringGetDatum(agginitval));
214 nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
216 aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
217 tupDesc = aggdesc->rd_att;
219 tup = heap_formtuple(tupDesc, values, nulls);
220 simple_heap_insert(aggdesc, tup);
222 CatalogUpdateIndexes(aggdesc, tup);
224 heap_close(aggdesc, RowExclusiveLock);
227 * Create dependencies for the aggregate (above and beyond those
228 * already made by ProcedureCreate). Note: we don't need an explicit
229 * dependency on aggTransType since we depend on it indirectly through
232 myself.classId = RelOid_pg_proc;
233 myself.objectId = procOid;
234 myself.objectSubId = 0;
236 /* Depends on transition function */
237 referenced.classId = RelOid_pg_proc;
238 referenced.objectId = transfn;
239 referenced.objectSubId = 0;
240 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
242 /* Depends on final function, if any */
243 if (OidIsValid(finalfn))
245 referenced.classId = RelOid_pg_proc;
246 referenced.objectId = finalfn;
247 referenced.objectSubId = 0;
248 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
253 * lookup_agg_function -- common code for finding both transfn and finalfn
256 lookup_agg_function(List *fnName,
264 FuncDetailCode fdresult;
267 * func_get_detail looks up the function in the catalogs, does
268 * disambiguation for polymorphic functions, handles inheritance, and
269 * returns the funcid and type and set or singleton status of the
270 * function's return value. it also returns the true argument types
273 fdresult = func_get_detail(fnName, NIL, nargs, input_types,
274 &fnOid, rettype, &retset,
277 /* only valid case is a normal function not returning a set */
278 if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
280 (errcode(ERRCODE_UNDEFINED_FUNCTION),
281 errmsg("function %s does not exist",
282 func_signature_string(fnName, nargs, input_types))));
285 (errcode(ERRCODE_DATATYPE_MISMATCH),
286 errmsg("function %s returns a set",
287 func_signature_string(fnName, nargs, input_types))));
290 * If the given type(s) are all polymorphic, there's nothing we can
291 * check. Otherwise, enforce consistency, and possibly refine the
294 if ((input_types[0] == ANYARRAYOID || input_types[0] == ANYELEMENTOID) &&
296 (input_types[1] == ANYARRAYOID || input_types[1] == ANYELEMENTOID)))
298 /* nothing to check here */
302 *rettype = enforce_generic_type_consistency(input_types,
309 * func_get_detail will find functions requiring run-time argument
310 * type coercion, but nodeAgg.c isn't prepared to deal with that
312 if (true_oid_array[0] != ANYARRAYOID &&
313 true_oid_array[0] != ANYELEMENTOID &&
314 !IsBinaryCoercible(input_types[0], true_oid_array[0]))
316 (errcode(ERRCODE_DATATYPE_MISMATCH),
317 errmsg("function %s requires run-time type coercion",
318 func_signature_string(fnName, nargs, true_oid_array))));
321 true_oid_array[1] != ANYARRAYOID &&
322 true_oid_array[1] != ANYELEMENTOID &&
323 !IsBinaryCoercible(input_types[1], true_oid_array[1]))
325 (errcode(ERRCODE_DATATYPE_MISMATCH),
326 errmsg("function %s requires run-time type coercion",
327 func_signature_string(fnName, nargs, true_oid_array))));