Branch data 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
46 : 557 : 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;
88 : 557 : Oid finalfn = InvalidOid; /* can be omitted */
89 : 557 : Oid combinefn = InvalidOid; /* can be omitted */
90 : 557 : Oid serialfn = InvalidOid; /* can be omitted */
91 : 557 : Oid deserialfn = InvalidOid; /* can be omitted */
92 : 557 : Oid mtransfn = InvalidOid; /* can be omitted */
93 : 557 : Oid minvtransfn = InvalidOid; /* can be omitted */
94 : 557 : Oid mfinalfn = InvalidOid; /* can be omitted */
95 : 557 : Oid sortop = InvalidOid; /* can be omitted */
96 : 557 : Oid supportfn = InvalidOid; /* can be omitted */
97 : 557 : Oid *aggArgTypes = parameterTypes->values;
98 : 557 : 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) */
114 [ - + ]: 557 : if (!aggName)
115 [ # # ]: 0 : elog(ERROR, "no aggregate name supplied");
116 : :
117 [ - + ]: 557 : if (!aggtransfnName)
118 [ # # ]: 0 : elog(ERROR, "aggregate must have a transition function");
119 : :
120 [ + - - + ]: 557 : if (numDirectArgs < 0 || numDirectArgs > numArgs)
121 [ # # ]: 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 : : */
128 [ + - - + ]: 557 : if (numArgs < 0 || numArgs > FUNC_MAX_ARGS - 1)
129 [ # # ]: 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 : : */
140 : 557 : detailmsg = check_valid_polymorphic_signature(aggTransType,
141 : : aggArgTypes,
142 : : numArgs);
143 [ + + ]: 557 : if (detailmsg)
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 : : */
152 [ + + ]: 485 : if (OidIsValid(aggmTransType))
153 : : {
154 : 38 : detailmsg = check_valid_polymorphic_signature(aggmTransType,
155 : : aggArgTypes,
156 : : numArgs);
157 [ - + ]: 38 : if (detailmsg)
158 [ # # ]: 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 : : */
171 [ + + + + : 485 : if (AGGKIND_IS_ORDERED_SET(aggKind) && OidIsValid(variadicArgType) &&
- + ]
172 : : variadicArgType != ANYOID)
173 [ # # ]: 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 : : */
189 [ + + - + ]: 485 : if (aggKind == AGGKIND_HYPOTHETICAL &&
190 : : numDirectArgs < numArgs)
191 : : {
192 : 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 : : */
211 [ + + ]: 485 : 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 : : 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 : 471 : nargs_transfn = numArgs + 1;
228 : 471 : fnArgs[0] = aggTransType;
229 : 471 : memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
230 : : }
231 : 485 : 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 : : */
245 [ - + ]: 401 : if (rettype != aggTransType)
246 [ # # ]: 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 : :
252 : 401 : tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
253 [ - + ]: 401 : if (!HeapTupleIsValid(tup))
254 [ # # ]: 0 : elog(ERROR, "cache lookup failed for function %u", transfn);
255 : 401 : 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 : : */
262 [ + + + + ]: 401 : if (proc->proisstrict && agginitval == NULL)
263 : : {
264 [ + - ]: 63 : if (numArgs < 1 ||
265 [ - + ]: 63 : !IsBinaryCoercible(aggArgTypes[0], aggTransType))
266 [ # # ]: 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 : :
271 : 401 : ReleaseSysCache(tup);
272 : :
273 : : /* handle moving-aggregate transfn, if supplied */
274 [ + + ]: 401 : 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 : : 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)
290 [ # # ]: 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 : :
296 : 38 : tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(mtransfn));
297 [ - + ]: 38 : if (!HeapTupleIsValid(tup))
298 [ # # ]: 0 : elog(ERROR, "cache lookup failed for function %u", mtransfn);
299 : 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))
309 [ # # ]: 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 */
315 : 38 : mtransIsStrict = proc->proisstrict;
316 : :
317 : 38 : ReleaseSysCache(tup);
318 : : }
319 : :
320 : : /* handle minvtransfn, if supplied */
321 [ + + ]: 401 : 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 : : 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))
343 [ # # ]: 0 : elog(ERROR, "cache lookup failed for function %u", minvtransfn);
344 : 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 [ + + ]: 393 : 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 : : */
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 : :
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 : : */
395 [ + + - + ]: 147 : if (finalfnExtraArgs && func_strict(finalfn))
396 [ # # ]: 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 : : */
405 : 238 : finaltype = aggTransType;
406 : : }
407 : : Assert(OidIsValid(finaltype));
408 : :
409 : : /* handle the combinefn, if supplied */
410 [ + + ]: 385 : 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 : :
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 */
426 [ - + ]: 16 : if (combineType != aggTransType)
427 [ # # ]: 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 : : */
438 [ + + - + ]: 16 : if (aggTransType == INTERNALOID && func_strict(combinefn))
439 [ # # ]: 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 : : */
448 [ + + ]: 381 : if (aggserialfnName)
449 : : {
450 : : /* signature is always serialize(internal) returns bytea */
451 : 16 : fnArgs[0] = INTERNALOID;
452 : :
453 : 16 : serialfn = lookup_agg_function(aggserialfnName, 1,
454 : : fnArgs, InvalidOid,
455 : : &rettype);
456 : :
457 [ - + ]: 12 : if (rettype != BYTEAOID)
458 [ # # ]: 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 : : */
468 [ + + ]: 377 : if (aggdeserialfnName)
469 : : {
470 : : /* signature is always deserialize(bytea, internal) returns internal */
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)
479 [ # # ]: 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 : : */
494 : 373 : detailmsg = check_valid_polymorphic_signature(finaltype,
495 : : aggArgTypes,
496 : : numArgs);
497 [ - + ]: 373 : if (detailmsg)
498 [ # # ]: 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 : : */
509 : 373 : detailmsg = check_valid_internal_signature(finaltype,
510 : : aggArgTypes,
511 : : numArgs);
512 [ - + ]: 373 : if (detailmsg)
513 [ # # ]: 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 : : */
523 [ + + ]: 373 : 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 : : */
532 : 0 : Oid ffnVariadicArgType = variadicArgType;
533 : :
534 : 0 : fnArgs[0] = aggmTransType;
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 : :
548 : 0 : mfinalfn = lookup_agg_function(aggmfinalfnName, nargs_finalfn,
549 : : fnArgs, ffnVariadicArgType,
550 : : &rettype);
551 : :
552 : : /* As above, check strictness if mfinalfnExtraArgs is given */
553 [ # # # # ]: 0 : if (mfinalfnExtraArgs && func_strict(mfinalfn))
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 : : */
563 : 30 : rettype = aggmTransType;
564 : : }
565 : : Assert(OidIsValid(rettype));
566 [ - + ]: 30 : if (rettype != finaltype)
567 [ # # ]: 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 */
575 [ + + ]: 373 : if (aggsortopName)
576 : : {
577 [ - + ]: 4 : if (numArgs != 1)
578 [ # # ]: 0 : ereport(ERROR,
579 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
580 : : errmsg("sort operator can only be specified for single-argument aggregates")));
581 : :
582 : : /*
583 : : * A sort operator only makes sense for an aggregate that behaves like
584 : : * MIN or MAX. We cannot check the aggregate's semantics carefully
585 : : * enough to be sure of that, but we must at least insist that its
586 : : * input and result types match; otherwise transforming to a
587 : : * MinMaxAggregate plan would result in type violations.
588 : : */
589 [ - + ]: 4 : if (finaltype != aggArgTypes[0])
590 [ # # ]: 0 : ereport(ERROR,
591 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
592 : : errmsg("sort operator can only be specified when aggregate's input and result types match")));
593 : :
594 : 4 : sortop = LookupOperName(NULL, aggsortopName,
595 : : aggArgTypes[0], aggArgTypes[0],
596 : : false, -1);
597 : :
598 : : /*
599 : : * Ideally we'd insist that the sortop be an ordering operator in some
600 : : * index opclass. But checking that here risks dump/reload ordering
601 : : * problems, and anyway it's not essential: if that isn't the case
602 : : * then the planner will throw a "could not find equality operator"
603 : : * error at plan time. We can at least verify that the operator
604 : : * returns boolean.
605 : : */
606 [ - + ]: 4 : if (get_op_rettype(sortop) != BOOLOID)
607 [ # # ]: 0 : ereport(ERROR,
608 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
609 : : errmsg("sort operator must return boolean")));
610 : : }
611 : :
612 : : /*
613 : : * Validate the planner support function, if present.
614 : : */
615 [ + + ]: 373 : if (aggsupportfuncName)
616 : : {
617 : : /* signature is always support(internal) returns internal */
618 : 5 : fnArgs[0] = INTERNALOID;
619 : :
620 : 5 : supportfn = lookup_agg_function(aggsupportfuncName, 1,
621 : : fnArgs, InvalidOid,
622 : : &rettype);
623 : :
624 [ - + ]: 5 : if (rettype != INTERNALOID)
625 [ # # ]: 0 : ereport(ERROR,
626 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
627 : : errmsg("return type of support function %s is not %s",
628 : : NameListToString(aggsupportfuncName),
629 : : format_type_be(INTERNALOID))));
630 : :
631 : : /*
632 : : * Specifying a support function requires superuser, same as in CREATE
633 : : * FUNCTION.
634 : : */
635 [ - + ]: 5 : if (!superuser())
636 [ # # ]: 0 : ereport(ERROR,
637 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
638 : : errmsg("must be superuser to specify a support function")));
639 : : }
640 : :
641 : : /*
642 : : * permission checks on used types
643 : : */
644 [ + + ]: 759 : for (i = 0; i < numArgs; i++)
645 : : {
646 : 386 : aclresult = object_aclcheck(TypeRelationId, aggArgTypes[i], GetUserId(), ACL_USAGE);
647 [ - + ]: 386 : if (aclresult != ACLCHECK_OK)
648 : 0 : aclcheck_error_type(aclresult, aggArgTypes[i]);
649 : : }
650 : :
651 : 373 : aclresult = object_aclcheck(TypeRelationId, aggTransType, GetUserId(), ACL_USAGE);
652 [ - + ]: 373 : if (aclresult != ACLCHECK_OK)
653 : 0 : aclcheck_error_type(aclresult, aggTransType);
654 : :
655 [ + + ]: 373 : if (OidIsValid(aggmTransType))
656 : : {
657 : 30 : aclresult = object_aclcheck(TypeRelationId, aggmTransType, GetUserId(), ACL_USAGE);
658 [ - + ]: 30 : if (aclresult != ACLCHECK_OK)
659 : 0 : aclcheck_error_type(aclresult, aggmTransType);
660 : : }
661 : :
662 : 373 : aclresult = object_aclcheck(TypeRelationId, finaltype, GetUserId(), ACL_USAGE);
663 [ - + ]: 373 : if (aclresult != ACLCHECK_OK)
664 : 0 : aclcheck_error_type(aclresult, finaltype);
665 : :
666 : :
667 : : /*
668 : : * Everything looks okay. Try to create the pg_proc entry for the
669 : : * aggregate. (This could fail if there's already a conflicting entry.)
670 : : */
671 : :
672 : 373 : myself = ProcedureCreate(aggName,
673 : : aggNamespace,
674 : : replace, /* maybe replacement */
675 : : false, /* doesn't return a set */
676 : : finaltype, /* returnType */
677 : : GetUserId(), /* proowner */
678 : : INTERNALlanguageId, /* languageObjectId */
679 : : InvalidOid, /* no validator */
680 : : "aggregate_dummy", /* placeholder (no such proc) */
681 : : NULL, /* probin */
682 : : NULL, /* prosqlbody */
683 : : PROKIND_AGGREGATE,
684 : : false, /* security invoker (currently not
685 : : * definable for agg) */
686 : : false, /* isLeakProof */
687 : : false, /* isStrict (not needed for agg) */
688 : : PROVOLATILE_IMMUTABLE, /* volatility (not needed
689 : : * for agg) */
690 : : proparallel,
691 : : parameterTypes, /* paramTypes */
692 : : allParameterTypes, /* allParamTypes */
693 : : parameterModes, /* parameterModes */
694 : : parameterNames, /* parameterNames */
695 : : parameterDefaults, /* parameterDefaults */
696 : : PointerGetDatum(NULL), /* trftypes */
697 : : NIL, /* trfoids */
698 : : PointerGetDatum(NULL), /* proconfig */
699 : : supportfn, /* prosupport */
700 : : 1, /* procost */
701 : : 0); /* prorows */
702 : 365 : procOid = myself.objectId;
703 : :
704 : : /*
705 : : * Okay to create the pg_aggregate entry.
706 : : */
707 : 365 : aggdesc = table_open(AggregateRelationId, RowExclusiveLock);
708 : 365 : tupDesc = aggdesc->rd_att;
709 : :
710 : : /* initialize nulls and values */
711 [ + + ]: 8395 : for (i = 0; i < Natts_pg_aggregate; i++)
712 : : {
713 : 8030 : nulls[i] = false;
714 : 8030 : values[i] = (Datum) 0;
715 : 8030 : replaces[i] = true;
716 : : }
717 : 365 : values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
718 : 365 : values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(aggKind);
719 : 365 : values[Anum_pg_aggregate_aggnumdirectargs - 1] = Int16GetDatum(numDirectArgs);
720 : 365 : values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
721 : 365 : values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
722 : 365 : values[Anum_pg_aggregate_aggcombinefn - 1] = ObjectIdGetDatum(combinefn);
723 : 365 : values[Anum_pg_aggregate_aggserialfn - 1] = ObjectIdGetDatum(serialfn);
724 : 365 : values[Anum_pg_aggregate_aggdeserialfn - 1] = ObjectIdGetDatum(deserialfn);
725 : 365 : values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(mtransfn);
726 : 365 : values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(minvtransfn);
727 : 365 : values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(mfinalfn);
728 : 365 : values[Anum_pg_aggregate_aggfinalextra - 1] = BoolGetDatum(finalfnExtraArgs);
729 : 365 : values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(mfinalfnExtraArgs);
730 : 365 : values[Anum_pg_aggregate_aggfinalmodify - 1] = CharGetDatum(finalfnModify);
731 : 365 : values[Anum_pg_aggregate_aggmfinalmodify - 1] = CharGetDatum(mfinalfnModify);
732 : 365 : values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
733 : 365 : values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
734 : 365 : values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
735 : 365 : values[Anum_pg_aggregate_aggmtranstype - 1] = ObjectIdGetDatum(aggmTransType);
736 : 365 : values[Anum_pg_aggregate_aggmtransspace - 1] = Int32GetDatum(aggmTransSpace);
737 [ + + ]: 365 : if (agginitval)
738 : 215 : values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
739 : : else
740 : 150 : nulls[Anum_pg_aggregate_agginitval - 1] = true;
741 [ + + ]: 365 : if (aggminitval)
742 : 10 : values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
743 : : else
744 : 355 : nulls[Anum_pg_aggregate_aggminitval - 1] = true;
745 : :
746 [ + + ]: 365 : if (replace)
747 : 12 : oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid));
748 : : else
749 : 353 : oldtup = NULL;
750 : :
751 [ + + ]: 365 : if (HeapTupleIsValid(oldtup))
752 : : {
753 : 12 : Form_pg_aggregate oldagg = (Form_pg_aggregate) GETSTRUCT(oldtup);
754 : :
755 : : /*
756 : : * If we're replacing an existing entry, we need to validate that
757 : : * we're not changing anything that would break callers. Specifically
758 : : * we must not change aggkind or aggnumdirectargs, which affect how an
759 : : * aggregate call is treated in parse analysis.
760 : : */
761 [ + + ]: 12 : if (aggKind != oldagg->aggkind)
762 [ + - + - : 4 : ereport(ERROR,
- - - - ]
763 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
764 : : errmsg("cannot change routine kind"),
765 : : (oldagg->aggkind == AGGKIND_NORMAL ?
766 : : errdetail("\"%s\" is an ordinary aggregate function.", aggName) :
767 : : oldagg->aggkind == AGGKIND_ORDERED_SET ?
768 : : errdetail("\"%s\" is an ordered-set aggregate.", aggName) :
769 : : oldagg->aggkind == AGGKIND_HYPOTHETICAL ?
770 : : errdetail("\"%s\" is a hypothetical-set aggregate.", aggName) :
771 : : 0)));
772 [ - + ]: 8 : if (numDirectArgs != oldagg->aggnumdirectargs)
773 [ # # ]: 0 : ereport(ERROR,
774 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
775 : : errmsg("cannot change number of direct arguments of an aggregate function")));
776 : :
777 : 8 : replaces[Anum_pg_aggregate_aggfnoid - 1] = false;
778 : 8 : replaces[Anum_pg_aggregate_aggkind - 1] = false;
779 : 8 : replaces[Anum_pg_aggregate_aggnumdirectargs - 1] = false;
780 : :
781 : 8 : tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
782 : 8 : CatalogTupleUpdate(aggdesc, &tup->t_self, tup);
783 : 8 : ReleaseSysCache(oldtup);
784 : : }
785 : : else
786 : : {
787 : 353 : tup = heap_form_tuple(tupDesc, values, nulls);
788 : 353 : CatalogTupleInsert(aggdesc, tup);
789 : : }
790 : :
791 : 361 : table_close(aggdesc, RowExclusiveLock);
792 : :
793 : : /*
794 : : * Create dependencies for the aggregate (above and beyond those already
795 : : * made by ProcedureCreate). Note: we don't need an explicit dependency
796 : : * on aggTransType since we depend on it indirectly through transfn.
797 : : * Likewise for aggmTransType using the mtransfn, if it exists.
798 : : *
799 : : * If we're replacing an existing definition, ProcedureCreate deleted all
800 : : * our existing dependencies, so we have to do the same things here either
801 : : * way.
802 : : */
803 : :
804 : 361 : addrs = new_object_addresses();
805 : :
806 : : /* Depends on transition function */
807 : 361 : ObjectAddressSet(referenced, ProcedureRelationId, transfn);
808 : 361 : add_exact_object_address(&referenced, addrs);
809 : :
810 : : /* Depends on final function, if any */
811 [ + + ]: 361 : if (OidIsValid(finalfn))
812 : : {
813 : 143 : ObjectAddressSet(referenced, ProcedureRelationId, finalfn);
814 : 143 : add_exact_object_address(&referenced, addrs);
815 : : }
816 : :
817 : : /* Depends on combine function, if any */
818 [ + + ]: 361 : if (OidIsValid(combinefn))
819 : : {
820 : 16 : ObjectAddressSet(referenced, ProcedureRelationId, combinefn);
821 : 16 : add_exact_object_address(&referenced, addrs);
822 : : }
823 : :
824 : : /* Depends on serialization function, if any */
825 [ + + ]: 361 : if (OidIsValid(serialfn))
826 : : {
827 : 8 : ObjectAddressSet(referenced, ProcedureRelationId, serialfn);
828 : 8 : add_exact_object_address(&referenced, addrs);
829 : : }
830 : :
831 : : /* Depends on deserialization function, if any */
832 [ + + ]: 361 : if (OidIsValid(deserialfn))
833 : : {
834 : 8 : ObjectAddressSet(referenced, ProcedureRelationId, deserialfn);
835 : 8 : add_exact_object_address(&referenced, addrs);
836 : : }
837 : :
838 : : /* Depends on forward transition function, if any */
839 [ + + ]: 361 : if (OidIsValid(mtransfn))
840 : : {
841 : 30 : ObjectAddressSet(referenced, ProcedureRelationId, mtransfn);
842 : 30 : add_exact_object_address(&referenced, addrs);
843 : : }
844 : :
845 : : /* Depends on inverse transition function, if any */
846 [ + + ]: 361 : if (OidIsValid(minvtransfn))
847 : : {
848 : 30 : ObjectAddressSet(referenced, ProcedureRelationId, minvtransfn);
849 : 30 : add_exact_object_address(&referenced, addrs);
850 : : }
851 : :
852 : : /* Depends on final function, if any */
853 [ - + ]: 361 : if (OidIsValid(mfinalfn))
854 : : {
855 : 0 : ObjectAddressSet(referenced, ProcedureRelationId, mfinalfn);
856 : 0 : add_exact_object_address(&referenced, addrs);
857 : : }
858 : :
859 : : /* Depends on sort operator, if any */
860 [ + + ]: 361 : if (OidIsValid(sortop))
861 : : {
862 : 4 : ObjectAddressSet(referenced, OperatorRelationId, sortop);
863 : 4 : add_exact_object_address(&referenced, addrs);
864 : : }
865 : :
866 : 361 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
867 : 361 : free_object_addresses(addrs);
868 : 361 : return myself;
869 : : }
870 : :
871 : : /*
872 : : * lookup_agg_function
873 : : * common code for finding aggregate support functions
874 : : *
875 : : * fnName: possibly-schema-qualified function name
876 : : * nargs, input_types: expected function argument types
877 : : * variadicArgType: type of variadic argument if any, else InvalidOid
878 : : *
879 : : * Returns OID of function, and stores its return type into *rettype
880 : : *
881 : : * NB: must not scribble on input_types[], as we may re-use those
882 : : */
883 : : static Oid
884 : 769 : lookup_agg_function(List *fnName,
885 : : int nargs,
886 : : Oid *input_types,
887 : : Oid variadicArgType,
888 : : Oid *rettype)
889 : : {
890 : : Oid fnOid;
891 : : bool retset;
892 : : int nvargs;
893 : : Oid vatype;
894 : : Oid *true_oid_array;
895 : : FuncDetailCode fdresult;
896 : : int fgc_flags;
897 : : AclResult aclresult;
898 : : int i;
899 : :
900 : : /*
901 : : * func_get_detail looks up the function in the catalogs, does
902 : : * disambiguation for polymorphic functions, handles inheritance, and
903 : : * returns the funcid and type and set or singleton status of the
904 : : * function's return value. it also returns the true argument types to
905 : : * the function.
906 : : */
907 : 769 : fdresult = func_get_detail(fnName, NIL, NIL,
908 : : nargs, input_types, false, false, false,
909 : : &fgc_flags,
910 : : &fnOid, rettype, &retset,
911 : : &nvargs, &vatype,
912 : : &true_oid_array, NULL);
913 : :
914 : : /* only valid case is a normal function not returning a set */
915 [ + + - + ]: 769 : if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
916 [ + - ]: 96 : ereport(ERROR,
917 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
918 : : errmsg("function %s does not exist",
919 : : func_signature_string(fnName, nargs,
920 : : NIL, input_types))));
921 [ - + ]: 673 : if (retset)
922 [ # # ]: 0 : ereport(ERROR,
923 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
924 : : errmsg("function %s returns a set",
925 : : func_signature_string(fnName, nargs,
926 : : NIL, input_types))));
927 : :
928 : : /*
929 : : * If the agg is declared to take VARIADIC ANY, the underlying functions
930 : : * had better be declared that way too, else they may receive too many
931 : : * parameters; but func_get_detail would have been happy with plain ANY.
932 : : * (Probably nothing very bad would happen, but it wouldn't work as the
933 : : * user expects.) Other combinations should work without any special
934 : : * pushups, given that we told func_get_detail not to expand VARIADIC.
935 : : */
936 [ + + - + ]: 673 : if (variadicArgType == ANYOID && vatype != ANYOID)
937 [ # # ]: 0 : ereport(ERROR,
938 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
939 : : errmsg("function %s must accept VARIADIC ANY to be used in this aggregate",
940 : : func_signature_string(fnName, nargs,
941 : : NIL, input_types))));
942 : :
943 : : /*
944 : : * If there are any polymorphic types involved, enforce consistency, and
945 : : * possibly refine the result type. It's OK if the result is still
946 : : * polymorphic at this point, though.
947 : : */
948 : 673 : *rettype = enforce_generic_type_consistency(input_types,
949 : : true_oid_array,
950 : : nargs,
951 : : *rettype,
952 : : true);
953 : :
954 : : /*
955 : : * func_get_detail will find functions requiring run-time argument type
956 : : * coercion, but nodeAgg.c isn't prepared to deal with that
957 : : */
958 [ + + ]: 1870 : for (i = 0; i < nargs; i++)
959 : : {
960 [ + + ]: 1205 : if (!IsBinaryCoercible(input_types[i], true_oid_array[i]))
961 [ + - ]: 8 : ereport(ERROR,
962 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
963 : : errmsg("function %s requires run-time type coercion",
964 : : func_signature_string(fnName, nargs,
965 : : NIL, true_oid_array))));
966 : : }
967 : :
968 : : /* Check aggregate creator has permission to call the function */
969 : 665 : aclresult = object_aclcheck(ProcedureRelationId, fnOid, GetUserId(), ACL_EXECUTE);
970 [ - + ]: 665 : if (aclresult != ACLCHECK_OK)
971 : 0 : aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(fnOid));
972 : :
973 : 665 : return fnOid;
974 : : }
|