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