Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_aggregate.c
4 : : * routines to support manipulation of the pg_aggregate relation
5 : : *
6 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/catalog/pg_aggregate.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/htup_details.h"
18 : : #include "access/table.h"
19 : : #include "catalog/dependency.h"
20 : : #include "catalog/indexing.h"
21 : : #include "catalog/pg_aggregate.h"
22 : : #include "catalog/pg_language.h"
23 : : #include "catalog/pg_operator.h"
24 : : #include "catalog/pg_proc.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"
35 : :
36 : :
37 : : static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
38 : : Oid variadicArgType,
39 : : Oid *rettype);
40 : :
41 : :
42 : : /*
43 : : * AggregateCreate
44 : : */
45 : : ObjectAddress
8562 tgl@sss.pgh.pa.us 46 :CBC 435 : AggregateCreate(const char *aggName,
47 : : Oid aggNamespace,
48 : : bool replace,
49 : : char aggKind,
50 : : int numArgs,
51 : : int numDirectArgs,
52 : : oidvector *parameterTypes,
53 : : Datum allParameterTypes,
54 : : Datum parameterModes,
55 : : Datum parameterNames,
56 : : List *parameterDefaults,
57 : : Oid variadicArgType,
58 : : List *aggtransfnName,
59 : : List *aggfinalfnName,
60 : : List *aggcombinefnName,
61 : : List *aggserialfnName,
62 : : List *aggdeserialfnName,
63 : : List *aggmtransfnName,
64 : : List *aggminvtransfnName,
65 : : List *aggmfinalfnName,
66 : : bool finalfnExtraArgs,
67 : : bool mfinalfnExtraArgs,
68 : : char finalfnModify,
69 : : char mfinalfnModify,
70 : : List *aggsortopName,
71 : : Oid aggTransType,
72 : : int32 aggTransSpace,
73 : : Oid aggmTransType,
74 : : int32 aggmTransSpace,
75 : : const char *agginitval,
76 : : const char *aggminitval,
77 : : char proparallel)
78 : : {
79 : : Relation aggdesc;
80 : : HeapTuple tup;
81 : : HeapTuple oldtup;
82 : : bool nulls[Natts_pg_aggregate];
83 : : Datum values[Natts_pg_aggregate];
84 : : bool replaces[Natts_pg_aggregate];
85 : : Form_pg_proc proc;
86 : : Oid transfn;
8934 bruce@momjian.us 87 : 435 : Oid finalfn = InvalidOid; /* can be omitted */
3376 rhaas@postgresql.org 88 : 435 : Oid combinefn = InvalidOid; /* can be omitted */
3448 89 : 435 : Oid serialfn = InvalidOid; /* can be omitted */
2999 tgl@sss.pgh.pa.us 90 : 435 : Oid deserialfn = InvalidOid; /* can be omitted */
4165 91 : 435 : Oid mtransfn = InvalidOid; /* can be omitted */
2999 92 : 435 : Oid minvtransfn = InvalidOid; /* can be omitted */
4165 93 : 435 : Oid mfinalfn = InvalidOid; /* can be omitted */
7452 94 : 435 : Oid sortop = InvalidOid; /* can be omitted */
4386 95 : 435 : Oid *aggArgTypes = parameterTypes->values;
4165 96 : 435 : bool mtransIsStrict = false;
97 : : Oid rettype;
98 : : Oid finaltype;
99 : : Oid fnArgs[FUNC_MAX_ARGS];
100 : : int nargs_transfn;
101 : : int nargs_finalfn;
102 : : Oid procOid;
103 : : TupleDesc tupDesc;
104 : : char *detailmsg;
105 : : int i;
106 : : ObjectAddress myself,
107 : : referenced;
108 : : ObjectAddresses *addrs;
109 : : AclResult aclresult;
110 : :
111 : : /* sanity checks (caller should have caught these) */
10226 bruce@momjian.us 112 [ - + ]: 435 : if (!aggName)
8793 peter_e@gmx.net 113 [ # # ]:UBC 0 : elog(ERROR, "no aggregate name supplied");
114 : :
9182 tgl@sss.pgh.pa.us 115 [ - + ]:CBC 435 : if (!aggtransfnName)
8793 peter_e@gmx.net 116 [ # # ]:UBC 0 : elog(ERROR, "aggregate must have a transition function");
117 : :
4275 tgl@sss.pgh.pa.us 118 [ + - - + ]:CBC 435 : if (numDirectArgs < 0 || numDirectArgs > numArgs)
2175 peter@eisentraut.org 119 [ # # ]:UBC 0 : elog(ERROR, "incorrect number of direct arguments for aggregate");
120 : :
121 : : /*
122 : : * Aggregates can have at most FUNC_MAX_ARGS-1 args, else the transfn
123 : : * and/or finalfn will be unrepresentable in pg_proc. We must check now
124 : : * to protect fixed-size arrays here and possibly in called functions.
125 : : */
4275 tgl@sss.pgh.pa.us 126 [ + - - + ]:CBC 435 : if (numArgs < 0 || numArgs > FUNC_MAX_ARGS - 1)
4275 tgl@sss.pgh.pa.us 127 [ # # ]:UBC 0 : ereport(ERROR,
128 : : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
129 : : errmsg_plural("aggregates cannot have more than %d argument",
130 : : "aggregates cannot have more than %d arguments",
131 : : FUNC_MAX_ARGS - 1,
132 : : FUNC_MAX_ARGS - 1)));
133 : :
134 : : /*
135 : : * If transtype is polymorphic, must have polymorphic argument also; else
136 : : * we will have no way to deduce the actual transtype.
137 : : */
1999 tgl@sss.pgh.pa.us 138 :CBC 435 : detailmsg = check_valid_polymorphic_signature(aggTransType,
139 : : aggArgTypes,
140 : : numArgs);
141 [ + + ]: 435 : if (detailmsg)
8083 142 [ + - ]: 54 : ereport(ERROR,
143 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
144 : : errmsg("cannot determine transition data type"),
145 : : errdetail_internal("%s", detailmsg)));
146 : :
147 : : /*
148 : : * Likewise for moving-aggregate transtype, if any
149 : : */
1999 150 [ + + ]: 381 : if (OidIsValid(aggmTransType))
151 : : {
152 : 30 : detailmsg = check_valid_polymorphic_signature(aggmTransType,
153 : : aggArgTypes,
154 : : numArgs);
155 [ - + ]: 30 : if (detailmsg)
1999 tgl@sss.pgh.pa.us 156 [ # # ]:UBC 0 : ereport(ERROR,
157 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
158 : : errmsg("cannot determine transition data type"),
159 : : errdetail_internal("%s", detailmsg)));
160 : : }
161 : :
162 : : /*
163 : : * An ordered-set aggregate that is VARIADIC must be VARIADIC ANY. In
164 : : * principle we could support regular variadic types, but it would make
165 : : * things much more complicated because we'd have to assemble the correct
166 : : * subsets of arguments into array values. Since no standard aggregates
167 : : * have use for such a case, we aren't bothering for now.
168 : : */
4275 tgl@sss.pgh.pa.us 169 [ + + + + :CBC 381 : if (AGGKIND_IS_ORDERED_SET(aggKind) && OidIsValid(variadicArgType) &&
- + ]
170 : : variadicArgType != ANYOID)
4275 tgl@sss.pgh.pa.us 171 [ # # ]:UBC 0 : ereport(ERROR,
172 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
173 : : errmsg("a variadic ordered-set aggregate must use VARIADIC type ANY")));
174 : :
175 : : /*
176 : : * If it's a hypothetical-set aggregate, there must be at least as many
177 : : * direct arguments as aggregated ones, and the last N direct arguments
178 : : * must match the aggregated ones in type. (We have to check this again
179 : : * when the aggregate is called, in case ANY is involved, but it makes
180 : : * sense to reject the aggregate definition now if the declared arg types
181 : : * don't match up.) It's unconditionally OK if numDirectArgs == numArgs,
182 : : * indicating that the grammar merged identical VARIADIC entries from both
183 : : * lists. Otherwise, if the agg is VARIADIC, then we had VARIADIC only on
184 : : * the aggregated side, which is not OK. Otherwise, insist on the last N
185 : : * parameter types on each side matching exactly.
186 : : */
4275 tgl@sss.pgh.pa.us 187 [ + + - + ]:CBC 381 : if (aggKind == AGGKIND_HYPOTHETICAL &&
188 : : numDirectArgs < numArgs)
189 : : {
4275 tgl@sss.pgh.pa.us 190 :UBC 0 : int numAggregatedArgs = numArgs - numDirectArgs;
191 : :
192 [ # # # # ]: 0 : if (OidIsValid(variadicArgType) ||
193 : 0 : numDirectArgs < numAggregatedArgs ||
194 : 0 : memcmp(aggArgTypes + (numDirectArgs - numAggregatedArgs),
195 [ # # ]: 0 : aggArgTypes + numDirectArgs,
196 : : numAggregatedArgs * sizeof(Oid)) != 0)
197 [ # # ]: 0 : ereport(ERROR,
198 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
199 : : errmsg("a hypothetical-set aggregate must have direct arguments matching its aggregated arguments")));
200 : : }
201 : :
202 : : /*
203 : : * Find the transfn. For ordinary aggs, it takes the transtype plus all
204 : : * aggregate arguments. For ordered-set aggs, it takes the transtype plus
205 : : * all aggregated args, but not direct args. However, we have to treat
206 : : * specially the case where a trailing VARIADIC item is considered to
207 : : * cover both direct and aggregated args.
208 : : */
4275 tgl@sss.pgh.pa.us 209 [ + + ]:CBC 381 : if (AGGKIND_IS_ORDERED_SET(aggKind))
210 : : {
211 [ + + ]: 11 : if (numDirectArgs < numArgs)
212 : 7 : nargs_transfn = numArgs - numDirectArgs + 1;
213 : : else
214 : : {
215 : : /* special case with VARIADIC last arg */
216 [ - + ]: 4 : Assert(variadicArgType != InvalidOid);
217 : 4 : nargs_transfn = 2;
218 : : }
219 : 11 : fnArgs[0] = aggTransType;
220 : 11 : memcpy(fnArgs + 1, aggArgTypes + (numArgs - (nargs_transfn - 1)),
221 : 11 : (nargs_transfn - 1) * sizeof(Oid));
222 : : }
223 : : else
224 : : {
225 : 370 : nargs_transfn = numArgs + 1;
226 : 370 : fnArgs[0] = aggTransType;
227 : 370 : memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
228 : : }
229 : 381 : transfn = lookup_agg_function(aggtransfnName, nargs_transfn,
230 : : fnArgs, variadicArgType,
231 : : &rettype);
232 : :
233 : : /*
234 : : * Return type of transfn (possibly after refinement by
235 : : * enforce_generic_type_consistency, if transtype isn't polymorphic) must
236 : : * exactly match declared transtype.
237 : : *
238 : : * In the non-polymorphic-transtype case, it might be okay to allow a
239 : : * rettype that's binary-coercible to transtype, but I'm not quite
240 : : * convinced that it's either safe or useful. When transtype is
241 : : * polymorphic we *must* demand exact equality.
242 : : */
8103 243 [ - + ]: 318 : if (rettype != aggTransType)
8083 tgl@sss.pgh.pa.us 244 [ # # ]:UBC 0 : ereport(ERROR,
245 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
246 : : errmsg("return type of transition function %s is not %s",
247 : : NameListToString(aggtransfnName),
248 : : format_type_be(aggTransType))));
249 : :
5683 rhaas@postgresql.org 250 :CBC 318 : tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
9182 tgl@sss.pgh.pa.us 251 [ - + ]: 318 : if (!HeapTupleIsValid(tup))
8083 tgl@sss.pgh.pa.us 252 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", transfn);
9182 tgl@sss.pgh.pa.us 253 :CBC 318 : proc = (Form_pg_proc) GETSTRUCT(tup);
254 : :
255 : : /*
256 : : * If the transfn is strict and the initval is NULL, make sure first input
257 : : * type and transtype are the same (or at least binary-compatible), so
258 : : * that it's OK to use the first input value as the initial transValue.
259 : : */
9060 260 [ + + + + ]: 318 : if (proc->proisstrict && agginitval == NULL)
261 : : {
6981 262 [ + - ]: 49 : if (numArgs < 1 ||
263 [ - + ]: 49 : !IsBinaryCoercible(aggArgTypes[0], aggTransType))
8083 tgl@sss.pgh.pa.us 264 [ # # ]:UBC 0 : ereport(ERROR,
265 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
266 : : errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
267 : : }
268 : :
9060 tgl@sss.pgh.pa.us 269 :CBC 318 : ReleaseSysCache(tup);
270 : :
271 : : /* handle moving-aggregate transfn, if supplied */
4165 272 [ + + ]: 318 : if (aggmtransfnName)
273 : : {
274 : : /*
275 : : * The arguments are the same as for the regular transfn, except that
276 : : * the transition data type might be different. So re-use the fnArgs
277 : : * values set up above, except for that one.
278 : : */
279 [ - + ]: 30 : Assert(OidIsValid(aggmTransType));
280 : 30 : fnArgs[0] = aggmTransType;
281 : :
282 : 30 : mtransfn = lookup_agg_function(aggmtransfnName, nargs_transfn,
283 : : fnArgs, variadicArgType,
284 : : &rettype);
285 : :
286 : : /* As above, return type must exactly match declared mtranstype. */
287 [ - + ]: 30 : if (rettype != aggmTransType)
4165 tgl@sss.pgh.pa.us 288 [ # # ]:UBC 0 : ereport(ERROR,
289 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
290 : : errmsg("return type of transition function %s is not %s",
291 : : NameListToString(aggmtransfnName),
292 : : format_type_be(aggmTransType))));
293 : :
4165 tgl@sss.pgh.pa.us 294 :CBC 30 : tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(mtransfn));
295 [ - + ]: 30 : if (!HeapTupleIsValid(tup))
4165 tgl@sss.pgh.pa.us 296 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", mtransfn);
4165 tgl@sss.pgh.pa.us 297 :CBC 30 : proc = (Form_pg_proc) GETSTRUCT(tup);
298 : :
299 : : /*
300 : : * If the mtransfn is strict and the minitval is NULL, check first
301 : : * input type and mtranstype are binary-compatible.
302 : : */
303 [ + + + + ]: 30 : if (proc->proisstrict && aggminitval == NULL)
304 : : {
305 [ + - ]: 18 : if (numArgs < 1 ||
306 [ - + ]: 18 : !IsBinaryCoercible(aggArgTypes[0], aggmTransType))
4165 tgl@sss.pgh.pa.us 307 [ # # ]:UBC 0 : ereport(ERROR,
308 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
309 : : errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
310 : : }
311 : :
312 : : /* Remember if mtransfn is strict; we may need this below */
4165 tgl@sss.pgh.pa.us 313 :CBC 30 : mtransIsStrict = proc->proisstrict;
314 : :
315 : 30 : ReleaseSysCache(tup);
316 : : }
317 : :
318 : : /* handle minvtransfn, if supplied */
319 [ + + ]: 318 : if (aggminvtransfnName)
320 : : {
321 : : /*
322 : : * This must have the same number of arguments with the same types as
323 : : * the forward transition function, so just re-use the fnArgs data.
324 : : */
325 [ - + ]: 30 : Assert(aggmtransfnName);
326 : :
327 : 30 : minvtransfn = lookup_agg_function(aggminvtransfnName, nargs_transfn,
328 : : fnArgs, variadicArgType,
329 : : &rettype);
330 : :
331 : : /* As above, return type must exactly match declared mtranstype. */
332 [ + + ]: 30 : if (rettype != aggmTransType)
333 [ + - ]: 3 : ereport(ERROR,
334 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
335 : : errmsg("return type of inverse transition function %s is not %s",
336 : : NameListToString(aggminvtransfnName),
337 : : format_type_be(aggmTransType))));
338 : :
339 : 27 : tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(minvtransfn));
340 [ - + ]: 27 : if (!HeapTupleIsValid(tup))
4165 tgl@sss.pgh.pa.us 341 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", minvtransfn);
4165 tgl@sss.pgh.pa.us 342 :CBC 27 : proc = (Form_pg_proc) GETSTRUCT(tup);
343 : :
344 : : /*
345 : : * We require the strictness settings of the forward and inverse
346 : : * transition functions to agree. This saves having to handle
347 : : * assorted special cases at execution time.
348 : : */
349 [ + + ]: 27 : if (proc->proisstrict != mtransIsStrict)
350 [ + - ]: 3 : ereport(ERROR,
351 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
352 : : errmsg("strictness of aggregate's forward and inverse transition functions must match")));
353 : :
354 : 24 : ReleaseSysCache(tup);
355 : : }
356 : :
357 : : /* handle finalfn, if supplied */
358 [ + + ]: 312 : if (aggfinalfnName)
359 : : {
360 : : /*
361 : : * If finalfnExtraArgs is specified, the transfn takes the transtype
362 : : * plus all args; otherwise, it just takes the transtype plus any
363 : : * direct args. (Non-direct args are useless at runtime, and are
364 : : * actually passed as NULLs, but we may need them in the function
365 : : * signature to allow resolution of a polymorphic agg's result type.)
366 : : */
4154 367 : 122 : Oid ffnVariadicArgType = variadicArgType;
368 : :
369 : 122 : fnArgs[0] = aggTransType;
370 : 122 : memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
371 [ + + ]: 122 : if (finalfnExtraArgs)
372 : 8 : nargs_finalfn = numArgs + 1;
373 : : else
374 : : {
375 : 114 : nargs_finalfn = numDirectArgs + 1;
376 [ + + ]: 114 : if (numDirectArgs < numArgs)
377 : : {
378 : : /* variadic argument doesn't affect finalfn */
379 : 98 : ffnVariadicArgType = InvalidOid;
380 : : }
381 : : }
382 : :
4275 383 : 122 : finalfn = lookup_agg_function(aggfinalfnName, nargs_finalfn,
384 : : fnArgs, ffnVariadicArgType,
385 : : &finaltype);
386 : :
387 : : /*
388 : : * When finalfnExtraArgs is specified, the finalfn will certainly be
389 : : * passed at least one null argument, so complain if it's strict.
390 : : * Nothing bad would happen at runtime (you'd just get a null result),
391 : : * but it's surely not what the user wants, so let's complain now.
392 : : */
4154 393 [ + + - + ]: 116 : if (finalfnExtraArgs && func_strict(finalfn))
4275 tgl@sss.pgh.pa.us 394 [ # # ]:UBC 0 : ereport(ERROR,
395 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
396 : : errmsg("final function with extra arguments must not be declared STRICT")));
397 : : }
398 : : else
399 : : {
400 : : /*
401 : : * If no finalfn, aggregate result type is type of the state value
402 : : */
8562 tgl@sss.pgh.pa.us 403 :CBC 190 : finaltype = aggTransType;
404 : : }
9182 405 [ - + ]: 306 : Assert(OidIsValid(finaltype));
406 : :
407 : : /* handle the combinefn, if supplied */
3517 rhaas@postgresql.org 408 [ + + ]: 306 : if (aggcombinefnName)
409 : : {
410 : : Oid combineType;
411 : :
412 : : /*
413 : : * Combine function must have 2 arguments, each of which is the trans
414 : : * type. VARIADIC doesn't affect it.
415 : : */
416 : 16 : fnArgs[0] = aggTransType;
417 : 16 : fnArgs[1] = aggTransType;
418 : :
2671 tgl@sss.pgh.pa.us 419 : 16 : combinefn = lookup_agg_function(aggcombinefnName, 2,
420 : : fnArgs, InvalidOid,
421 : : &combineType);
422 : :
423 : : /* Ensure the return type matches the aggregate's trans type */
3517 rhaas@postgresql.org 424 [ - + ]: 13 : if (combineType != aggTransType)
3517 rhaas@postgresql.org 425 [ # # ]:UBC 0 : ereport(ERROR,
426 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
427 : : errmsg("return type of combine function %s is not %s",
428 : : NameListToString(aggcombinefnName),
429 : : format_type_be(aggTransType))));
430 : :
431 : : /*
432 : : * A combine function to combine INTERNAL states must accept nulls and
433 : : * ensure that the returned state is in the correct memory context. We
434 : : * cannot directly check the latter, but we can check the former.
435 : : */
3448 rhaas@postgresql.org 436 [ + + - + ]:CBC 13 : if (aggTransType == INTERNALOID && func_strict(combinefn))
3448 rhaas@postgresql.org 437 [ # # ]:UBC 0 : ereport(ERROR,
438 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
439 : : errmsg("combine function with transition type %s must not be declared STRICT",
440 : : format_type_be(aggTransType))));
441 : : }
442 : :
443 : : /*
444 : : * Validate the serialization function, if present.
445 : : */
3448 rhaas@postgresql.org 446 [ + + ]:CBC 303 : if (aggserialfnName)
447 : : {
448 : : /* signature is always serialize(internal) returns bytea */
3363 tgl@sss.pgh.pa.us 449 : 12 : fnArgs[0] = INTERNALOID;
450 : :
3448 rhaas@postgresql.org 451 : 12 : serialfn = lookup_agg_function(aggserialfnName, 1,
452 : : fnArgs, InvalidOid,
453 : : &rettype);
454 : :
3363 tgl@sss.pgh.pa.us 455 [ - + ]: 9 : if (rettype != BYTEAOID)
3448 rhaas@postgresql.org 456 [ # # ]:UBC 0 : ereport(ERROR,
457 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
458 : : errmsg("return type of serialization function %s is not %s",
459 : : NameListToString(aggserialfnName),
460 : : format_type_be(BYTEAOID))));
461 : : }
462 : :
463 : : /*
464 : : * Validate the deserialization function, if present.
465 : : */
3448 rhaas@postgresql.org 466 [ + + ]:CBC 300 : if (aggdeserialfnName)
467 : : {
468 : : /* signature is always deserialize(bytea, internal) returns internal */
3363 tgl@sss.pgh.pa.us 469 : 9 : fnArgs[0] = BYTEAOID;
470 : 9 : fnArgs[1] = INTERNALOID; /* dummy argument for type safety */
471 : :
472 : 9 : deserialfn = lookup_agg_function(aggdeserialfnName, 2,
473 : : fnArgs, InvalidOid,
474 : : &rettype);
475 : :
476 [ - + ]: 6 : if (rettype != INTERNALOID)
3448 rhaas@postgresql.org 477 [ # # ]:UBC 0 : ereport(ERROR,
478 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
479 : : errmsg("return type of deserialization function %s is not %s",
480 : : NameListToString(aggdeserialfnName),
481 : : format_type_be(INTERNALOID))));
482 : : }
483 : :
484 : : /*
485 : : * If finaltype (i.e. aggregate return type) is polymorphic, inputs must
486 : : * be polymorphic also, else parser will fail to deduce result type.
487 : : * (Note: given the previous test on transtype and inputs, this cannot
488 : : * happen, unless someone has snuck a finalfn definition into the catalogs
489 : : * that itself violates the rule against polymorphic result with no
490 : : * polymorphic input.)
491 : : */
1999 tgl@sss.pgh.pa.us 492 :CBC 297 : detailmsg = check_valid_polymorphic_signature(finaltype,
493 : : aggArgTypes,
494 : : numArgs);
495 [ - + ]: 297 : if (detailmsg)
8083 tgl@sss.pgh.pa.us 496 [ # # ]:UBC 0 : ereport(ERROR,
497 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
498 : : errmsg("cannot determine result data type"),
499 : : errdetail_internal("%s", detailmsg)));
500 : :
501 : : /*
502 : : * Also, the return type can't be INTERNAL unless there's at least one
503 : : * INTERNAL argument. This is the same type-safety restriction we enforce
504 : : * for regular functions, but at the level of aggregates. We must test
505 : : * this explicitly because we allow INTERNAL as the transtype.
506 : : */
1999 tgl@sss.pgh.pa.us 507 :CBC 297 : detailmsg = check_valid_internal_signature(finaltype,
508 : : aggArgTypes,
509 : : numArgs);
510 [ - + ]: 297 : if (detailmsg)
6140 tgl@sss.pgh.pa.us 511 [ # # ]:UBC 0 : ereport(ERROR,
512 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
513 : : errmsg("unsafe use of pseudo-type \"internal\""),
514 : : errdetail_internal("%s", detailmsg)));
515 : :
516 : : /*
517 : : * If a moving-aggregate implementation is supplied, look up its finalfn
518 : : * if any, and check that the implied aggregate result type matches the
519 : : * plain implementation.
520 : : */
4165 tgl@sss.pgh.pa.us 521 [ + + ]:CBC 297 : if (OidIsValid(aggmTransType))
522 : : {
523 : : /* handle finalfn, if supplied */
524 [ - + ]: 24 : if (aggmfinalfnName)
525 : : {
526 : : /*
527 : : * The arguments are figured the same way as for the regular
528 : : * finalfn, but using aggmTransType and mfinalfnExtraArgs.
529 : : */
4154 tgl@sss.pgh.pa.us 530 :UBC 0 : Oid ffnVariadicArgType = variadicArgType;
531 : :
4165 532 : 0 : fnArgs[0] = aggmTransType;
4154 533 : 0 : memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
534 [ # # ]: 0 : if (mfinalfnExtraArgs)
535 : 0 : nargs_finalfn = numArgs + 1;
536 : : else
537 : : {
538 : 0 : nargs_finalfn = numDirectArgs + 1;
539 [ # # ]: 0 : if (numDirectArgs < numArgs)
540 : : {
541 : : /* variadic argument doesn't affect finalfn */
542 : 0 : ffnVariadicArgType = InvalidOid;
543 : : }
544 : : }
545 : :
4165 546 : 0 : mfinalfn = lookup_agg_function(aggmfinalfnName, nargs_finalfn,
547 : : fnArgs, ffnVariadicArgType,
548 : : &rettype);
549 : :
550 : : /* As above, check strictness if mfinalfnExtraArgs is given */
4154 551 [ # # # # ]: 0 : if (mfinalfnExtraArgs && func_strict(mfinalfn))
4165 552 [ # # ]: 0 : ereport(ERROR,
553 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
554 : : errmsg("final function with extra arguments must not be declared STRICT")));
555 : : }
556 : : else
557 : : {
558 : : /*
559 : : * If no finalfn, aggregate result type is type of the state value
560 : : */
4165 tgl@sss.pgh.pa.us 561 :CBC 24 : rettype = aggmTransType;
562 : : }
563 [ - + ]: 24 : Assert(OidIsValid(rettype));
564 [ - + ]: 24 : if (rettype != finaltype)
4165 tgl@sss.pgh.pa.us 565 [ # # ]:UBC 0 : ereport(ERROR,
566 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
567 : : errmsg("moving-aggregate implementation returns type %s, but plain implementation returns type %s",
568 : : format_type_be(rettype),
569 : : format_type_be(finaltype))));
570 : : }
571 : :
572 : : /* handle sortop, if supplied */
7452 tgl@sss.pgh.pa.us 573 [ + + ]:CBC 297 : if (aggsortopName)
574 : : {
6981 575 [ - + ]: 4 : if (numArgs != 1)
6981 tgl@sss.pgh.pa.us 576 [ # # ]:UBC 0 : ereport(ERROR,
577 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
578 : : errmsg("sort operator can only be specified for single-argument aggregates")));
7116 tgl@sss.pgh.pa.us 579 :CBC 4 : sortop = LookupOperName(NULL, aggsortopName,
580 : : aggArgTypes[0], aggArgTypes[0],
581 : : false, -1);
582 : : }
583 : :
584 : : /*
585 : : * permission checks on used types
586 : : */
5009 peter_e@gmx.net 587 [ + + ]: 604 : for (i = 0; i < numArgs; i++)
588 : : {
1028 peter@eisentraut.org 589 : 307 : aclresult = object_aclcheck(TypeRelationId, aggArgTypes[i], GetUserId(), ACL_USAGE);
5009 peter_e@gmx.net 590 [ - + ]: 307 : if (aclresult != ACLCHECK_OK)
4831 peter_e@gmx.net 591 :UBC 0 : aclcheck_error_type(aclresult, aggArgTypes[i]);
592 : : }
593 : :
1028 peter@eisentraut.org 594 :CBC 297 : aclresult = object_aclcheck(TypeRelationId, aggTransType, GetUserId(), ACL_USAGE);
5009 peter_e@gmx.net 595 [ - + ]: 297 : if (aclresult != ACLCHECK_OK)
4831 peter_e@gmx.net 596 :UBC 0 : aclcheck_error_type(aclresult, aggTransType);
597 : :
4165 tgl@sss.pgh.pa.us 598 [ + + ]:CBC 297 : if (OidIsValid(aggmTransType))
599 : : {
1028 peter@eisentraut.org 600 : 24 : aclresult = object_aclcheck(TypeRelationId, aggmTransType, GetUserId(), ACL_USAGE);
4165 tgl@sss.pgh.pa.us 601 [ - + ]: 24 : if (aclresult != ACLCHECK_OK)
4165 tgl@sss.pgh.pa.us 602 :UBC 0 : aclcheck_error_type(aclresult, aggmTransType);
603 : : }
604 : :
1028 peter@eisentraut.org 605 :CBC 297 : aclresult = object_aclcheck(TypeRelationId, finaltype, GetUserId(), ACL_USAGE);
5009 peter_e@gmx.net 606 [ - + ]: 297 : if (aclresult != ACLCHECK_OK)
4831 peter_e@gmx.net 607 :UBC 0 : aclcheck_error_type(aclresult, finaltype);
608 : :
609 : :
610 : : /*
611 : : * Everything looks okay. Try to create the pg_proc entry for the
612 : : * aggregate. (This could fail if there's already a conflicting entry.)
613 : : */
614 : :
3840 alvherre@alvh.no-ip. 615 :CBC 297 : myself = ProcedureCreate(aggName,
616 : : aggNamespace,
617 : : replace, /* maybe replacement */
618 : : false, /* doesn't return a set */
619 : : finaltype, /* returnType */
620 : : GetUserId(), /* proowner */
621 : : INTERNALlanguageId, /* languageObjectId */
622 : : InvalidOid, /* no validator */
623 : : "aggregate_dummy", /* placeholder (no such proc) */
624 : : NULL, /* probin */
625 : : NULL, /* prosqlbody */
626 : : PROKIND_AGGREGATE,
627 : : false, /* security invoker (currently not
628 : : * definable for agg) */
629 : : false, /* isLeakProof */
630 : : false, /* isStrict (not needed for agg) */
631 : : PROVOLATILE_IMMUTABLE, /* volatility (not needed
632 : : * for agg) */
633 : : proparallel,
634 : : parameterTypes, /* paramTypes */
635 : : allParameterTypes, /* allParamTypes */
636 : : parameterModes, /* parameterModes */
637 : : parameterNames, /* parameterNames */
638 : : parameterDefaults, /* parameterDefaults */
639 : : PointerGetDatum(NULL), /* trftypes */
640 : : NIL, /* trfoids */
641 : : PointerGetDatum(NULL), /* proconfig */
642 : : InvalidOid, /* no prosupport */
643 : : 1, /* procost */
644 : : 0); /* prorows */
645 : 291 : procOid = myself.objectId;
646 : :
647 : : /*
648 : : * Okay to create the pg_aggregate entry.
649 : : */
2420 andres@anarazel.de 650 : 291 : aggdesc = table_open(AggregateRelationId, RowExclusiveLock);
2482 651 : 291 : tupDesc = aggdesc->rd_att;
652 : :
653 : : /* initialize nulls and values */
10226 bruce@momjian.us 654 [ + + ]: 6693 : for (i = 0; i < Natts_pg_aggregate; i++)
655 : : {
6152 tgl@sss.pgh.pa.us 656 : 6402 : nulls[i] = false;
29 tgl@sss.pgh.pa.us 657 :GNC 6402 : values[i] = (Datum) 0;
2363 rhodiumtoad@postgres 658 :CBC 6402 : replaces[i] = true;
659 : : }
8549 tgl@sss.pgh.pa.us 660 : 291 : values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
4275 661 : 291 : values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(aggKind);
662 : 291 : values[Anum_pg_aggregate_aggnumdirectargs - 1] = Int16GetDatum(numDirectArgs);
9182 663 : 291 : values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
664 : 291 : values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
3517 rhaas@postgresql.org 665 : 291 : values[Anum_pg_aggregate_aggcombinefn - 1] = ObjectIdGetDatum(combinefn);
3448 666 : 291 : values[Anum_pg_aggregate_aggserialfn - 1] = ObjectIdGetDatum(serialfn);
667 : 291 : values[Anum_pg_aggregate_aggdeserialfn - 1] = ObjectIdGetDatum(deserialfn);
4165 tgl@sss.pgh.pa.us 668 : 291 : values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(mtransfn);
669 : 291 : values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(minvtransfn);
670 : 291 : values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(mfinalfn);
4154 671 : 291 : values[Anum_pg_aggregate_aggfinalextra - 1] = BoolGetDatum(finalfnExtraArgs);
672 : 291 : values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(mfinalfnExtraArgs);
2884 673 : 291 : values[Anum_pg_aggregate_aggfinalmodify - 1] = CharGetDatum(finalfnModify);
674 : 291 : values[Anum_pg_aggregate_aggmfinalmodify - 1] = CharGetDatum(mfinalfnModify);
7452 675 : 291 : values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
8562 676 : 291 : values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
4312 677 : 291 : values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
4165 678 : 291 : values[Anum_pg_aggregate_aggmtranstype - 1] = ObjectIdGetDatum(aggmTransType);
679 : 291 : values[Anum_pg_aggregate_aggmtransspace - 1] = Int32GetDatum(aggmTransSpace);
9182 680 [ + + ]: 291 : if (agginitval)
6374 681 : 171 : values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
682 : : else
6152 683 : 120 : nulls[Anum_pg_aggregate_agginitval - 1] = true;
4165 684 [ + + ]: 291 : if (aggminitval)
685 : 8 : values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
686 : : else
687 : 283 : nulls[Anum_pg_aggregate_aggminitval - 1] = true;
688 : :
2363 rhodiumtoad@postgres 689 [ + + ]: 291 : if (replace)
690 : 9 : oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid));
691 : : else
692 : 282 : oldtup = NULL;
693 : :
694 [ + + ]: 291 : if (HeapTupleIsValid(oldtup))
695 : : {
696 : 9 : Form_pg_aggregate oldagg = (Form_pg_aggregate) GETSTRUCT(oldtup);
697 : :
698 : : /*
699 : : * If we're replacing an existing entry, we need to validate that
700 : : * we're not changing anything that would break callers. Specifically
701 : : * we must not change aggkind or aggnumdirectargs, which affect how an
702 : : * aggregate call is treated in parse analysis.
703 : : */
704 [ + + ]: 9 : if (aggKind != oldagg->aggkind)
705 [ + - + - : 3 : ereport(ERROR,
- - - - ]
706 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
707 : : errmsg("cannot change routine kind"),
708 : : (oldagg->aggkind == AGGKIND_NORMAL ?
709 : : errdetail("\"%s\" is an ordinary aggregate function.", aggName) :
710 : : oldagg->aggkind == AGGKIND_ORDERED_SET ?
711 : : errdetail("\"%s\" is an ordered-set aggregate.", aggName) :
712 : : oldagg->aggkind == AGGKIND_HYPOTHETICAL ?
713 : : errdetail("\"%s\" is a hypothetical-set aggregate.", aggName) :
714 : : 0)));
715 [ - + ]: 6 : if (numDirectArgs != oldagg->aggnumdirectargs)
2363 rhodiumtoad@postgres 716 [ # # ]:UBC 0 : ereport(ERROR,
717 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
718 : : errmsg("cannot change number of direct arguments of an aggregate function")));
719 : :
2363 rhodiumtoad@postgres 720 :CBC 6 : replaces[Anum_pg_aggregate_aggfnoid - 1] = false;
721 : 6 : replaces[Anum_pg_aggregate_aggkind - 1] = false;
722 : 6 : replaces[Anum_pg_aggregate_aggnumdirectargs - 1] = false;
723 : :
724 : 6 : tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
725 : 6 : CatalogTupleUpdate(aggdesc, &tup->t_self, tup);
726 : 6 : ReleaseSysCache(oldtup);
727 : : }
728 : : else
729 : : {
730 : 282 : tup = heap_form_tuple(tupDesc, values, nulls);
731 : 282 : CatalogTupleInsert(aggdesc, tup);
732 : : }
733 : :
2420 andres@anarazel.de 734 : 288 : table_close(aggdesc, RowExclusiveLock);
735 : :
736 : : /*
737 : : * Create dependencies for the aggregate (above and beyond those already
738 : : * made by ProcedureCreate). Note: we don't need an explicit dependency
739 : : * on aggTransType since we depend on it indirectly through transfn.
740 : : * Likewise for aggmTransType using the mtransfn, if it exists.
741 : : *
742 : : * If we're replacing an existing definition, ProcedureCreate deleted all
743 : : * our existing dependencies, so we have to do the same things here either
744 : : * way.
745 : : */
746 : :
1827 michael@paquier.xyz 747 : 288 : addrs = new_object_addresses();
748 : :
749 : : /* Depends on transition function */
1893 750 : 288 : ObjectAddressSet(referenced, ProcedureRelationId, transfn);
1827 751 : 288 : add_exact_object_address(&referenced, addrs);
752 : :
753 : : /* Depends on final function, if any */
8453 tgl@sss.pgh.pa.us 754 [ + + ]: 288 : if (OidIsValid(finalfn))
755 : : {
1893 michael@paquier.xyz 756 : 113 : ObjectAddressSet(referenced, ProcedureRelationId, finalfn);
1827 757 : 113 : add_exact_object_address(&referenced, addrs);
758 : : }
759 : :
760 : : /* Depends on combine function, if any */
3517 rhaas@postgresql.org 761 [ + + ]: 288 : if (OidIsValid(combinefn))
762 : : {
1893 michael@paquier.xyz 763 : 13 : ObjectAddressSet(referenced, ProcedureRelationId, combinefn);
1827 764 : 13 : add_exact_object_address(&referenced, addrs);
765 : : }
766 : :
767 : : /* Depends on serialization function, if any */
3448 rhaas@postgresql.org 768 [ + + ]: 288 : if (OidIsValid(serialfn))
769 : : {
1893 michael@paquier.xyz 770 : 6 : ObjectAddressSet(referenced, ProcedureRelationId, serialfn);
1827 771 : 6 : add_exact_object_address(&referenced, addrs);
772 : : }
773 : :
774 : : /* Depends on deserialization function, if any */
3448 rhaas@postgresql.org 775 [ + + ]: 288 : if (OidIsValid(deserialfn))
776 : : {
1893 michael@paquier.xyz 777 : 6 : ObjectAddressSet(referenced, ProcedureRelationId, deserialfn);
1827 778 : 6 : add_exact_object_address(&referenced, addrs);
779 : : }
780 : :
781 : : /* Depends on forward transition function, if any */
4165 tgl@sss.pgh.pa.us 782 [ + + ]: 288 : if (OidIsValid(mtransfn))
783 : : {
1893 michael@paquier.xyz 784 : 24 : ObjectAddressSet(referenced, ProcedureRelationId, mtransfn);
1827 785 : 24 : add_exact_object_address(&referenced, addrs);
786 : : }
787 : :
788 : : /* Depends on inverse transition function, if any */
4165 tgl@sss.pgh.pa.us 789 [ + + ]: 288 : if (OidIsValid(minvtransfn))
790 : : {
1893 michael@paquier.xyz 791 : 24 : ObjectAddressSet(referenced, ProcedureRelationId, minvtransfn);
1827 792 : 24 : add_exact_object_address(&referenced, addrs);
793 : : }
794 : :
795 : : /* Depends on final function, if any */
4165 tgl@sss.pgh.pa.us 796 [ - + ]: 288 : if (OidIsValid(mfinalfn))
797 : : {
1893 michael@paquier.xyz 798 :UBC 0 : ObjectAddressSet(referenced, ProcedureRelationId, mfinalfn);
1827 799 : 0 : add_exact_object_address(&referenced, addrs);
800 : : }
801 : :
802 : : /* Depends on sort operator, if any */
7452 tgl@sss.pgh.pa.us 803 [ + + ]:CBC 288 : if (OidIsValid(sortop))
804 : : {
1893 michael@paquier.xyz 805 : 4 : ObjectAddressSet(referenced, OperatorRelationId, sortop);
1827 806 : 4 : add_exact_object_address(&referenced, addrs);
807 : : }
808 : :
809 : 288 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
810 : 288 : free_object_addresses(addrs);
3840 alvherre@alvh.no-ip. 811 : 288 : return myself;
812 : : }
813 : :
814 : : /*
815 : : * lookup_agg_function
816 : : * common code for finding aggregate support functions
817 : : *
818 : : * fnName: possibly-schema-qualified function name
819 : : * nargs, input_types: expected function argument types
820 : : * variadicArgType: type of variadic argument if any, else InvalidOid
821 : : *
822 : : * Returns OID of function, and stores its return type into *rettype
823 : : *
824 : : * NB: must not scribble on input_types[], as we may re-use those
825 : : */
826 : : static Oid
8103 tgl@sss.pgh.pa.us 827 : 600 : lookup_agg_function(List *fnName,
828 : : int nargs,
829 : : Oid *input_types,
830 : : Oid variadicArgType,
831 : : Oid *rettype)
832 : : {
833 : : Oid fnOid;
834 : : bool retset;
835 : : int nvargs;
836 : : Oid vatype;
837 : : Oid *true_oid_array;
838 : : FuncDetailCode fdresult;
839 : : AclResult aclresult;
840 : : int i;
841 : :
842 : : /*
843 : : * func_get_detail looks up the function in the catalogs, does
844 : : * disambiguation for polymorphic functions, handles inheritance, and
845 : : * returns the funcid and type and set or singleton status of the
846 : : * function's return value. it also returns the true argument types to
847 : : * the function.
848 : : */
5812 849 : 600 : fdresult = func_get_detail(fnName, NIL, NIL,
850 : : nargs, input_types, false, false, false,
851 : : &fnOid, rettype, &retset,
852 : : &nvargs, &vatype,
853 : : &true_oid_array, NULL);
854 : :
855 : : /* only valid case is a normal function not returning a set */
8100 856 [ + + - + ]: 600 : if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
8083 857 [ + - ]: 72 : ereport(ERROR,
858 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
859 : : errmsg("function %s does not exist",
860 : : func_signature_string(fnName, nargs,
861 : : NIL, input_types))));
8100 862 [ - + ]: 528 : if (retset)
8083 tgl@sss.pgh.pa.us 863 [ # # ]:UBC 0 : ereport(ERROR,
864 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
865 : : errmsg("function %s returns a set",
866 : : func_signature_string(fnName, nargs,
867 : : NIL, input_types))));
868 : :
869 : : /*
870 : : * If the agg is declared to take VARIADIC ANY, the underlying functions
871 : : * had better be declared that way too, else they may receive too many
872 : : * parameters; but func_get_detail would have been happy with plain ANY.
873 : : * (Probably nothing very bad would happen, but it wouldn't work as the
874 : : * user expects.) Other combinations should work without any special
875 : : * pushups, given that we told func_get_detail not to expand VARIADIC.
876 : : */
4275 tgl@sss.pgh.pa.us 877 [ + + - + ]:CBC 528 : if (variadicArgType == ANYOID && vatype != ANYOID)
4275 tgl@sss.pgh.pa.us 878 [ # # ]:UBC 0 : ereport(ERROR,
879 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
880 : : errmsg("function %s must accept VARIADIC ANY to be used in this aggregate",
881 : : func_signature_string(fnName, nargs,
882 : : NIL, input_types))));
883 : :
884 : : /*
885 : : * If there are any polymorphic types involved, enforce consistency, and
886 : : * possibly refine the result type. It's OK if the result is still
887 : : * polymorphic at this point, though.
888 : : */
6448 tgl@sss.pgh.pa.us 889 :CBC 528 : *rettype = enforce_generic_type_consistency(input_types,
890 : : true_oid_array,
891 : : nargs,
892 : : *rettype,
893 : : true);
894 : :
895 : : /*
896 : : * func_get_detail will find functions requiring run-time argument type
897 : : * coercion, but nodeAgg.c isn't prepared to deal with that
898 : : */
6981 899 [ + + ]: 1471 : for (i = 0; i < nargs; i++)
900 : : {
4275 901 [ + + ]: 949 : if (!IsBinaryCoercible(input_types[i], true_oid_array[i]))
6981 902 [ + - ]: 6 : ereport(ERROR,
903 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
904 : : errmsg("function %s requires run-time type coercion",
905 : : func_signature_string(fnName, nargs,
906 : : NIL, true_oid_array))));
907 : : }
908 : :
909 : : /* Check aggregate creator has permission to call the function */
1028 peter@eisentraut.org 910 : 522 : aclresult = object_aclcheck(ProcedureRelationId, fnOid, GetUserId(), ACL_EXECUTE);
7527 tgl@sss.pgh.pa.us 911 [ - + ]: 522 : if (aclresult != ACLCHECK_OK)
2835 peter_e@gmx.net 912 :UBC 0 : aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(fnOid));
913 : :
8103 tgl@sss.pgh.pa.us 914 :CBC 522 : return fnOid;
915 : : }
|