Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * aggregatecmds.c
4 : *
5 : * Routines for aggregate-manipulation commands
6 : *
7 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : *
11 : * IDENTIFICATION
12 : * src/backend/commands/aggregatecmds.c
13 : *
14 : * DESCRIPTION
15 : * The "DefineFoo" routines take the parse tree and pick out the
16 : * appropriate arguments/flags, passing the results to the
17 : * corresponding "FooDefine" routines (in src/catalog) that do
18 : * the actual catalog-munging. These routines also verify permission
19 : * of the user to execute the command.
20 : *
21 : *-------------------------------------------------------------------------
22 : */
23 : #include "postgres.h"
24 :
25 : #include "catalog/namespace.h"
26 : #include "catalog/pg_aggregate.h"
27 : #include "catalog/pg_namespace.h"
28 : #include "catalog/pg_proc.h"
29 : #include "catalog/pg_type.h"
30 : #include "commands/defrem.h"
31 : #include "miscadmin.h"
32 : #include "parser/parse_type.h"
33 : #include "utils/acl.h"
34 : #include "utils/builtins.h"
35 : #include "utils/lsyscache.h"
36 :
37 :
38 : static char extractModify(DefElem *defel);
39 :
40 :
41 : /*
42 : * DefineAggregate
43 : *
44 : * "oldstyle" signals the old (pre-8.2) style where the aggregate input type
45 : * is specified by a BASETYPE element in the parameters. Otherwise,
46 : * "args" is a pair, whose first element is a list of FunctionParameter structs
47 : * defining the agg's arguments (both direct and aggregated), and whose second
48 : * element is an Integer node with the number of direct args, or -1 if this
49 : * isn't an ordered-set aggregate.
50 : * "parameters" is a list of DefElem representing the agg's definition clauses.
51 : */
52 : ObjectAddress
53 906 : DefineAggregate(ParseState *pstate,
54 : List *name,
55 : List *args,
56 : bool oldstyle,
57 : List *parameters,
58 : bool replace)
59 : {
60 : char *aggName;
61 : Oid aggNamespace;
62 : AclResult aclresult;
63 906 : char aggKind = AGGKIND_NORMAL;
64 906 : List *transfuncName = NIL;
65 906 : List *finalfuncName = NIL;
66 906 : List *combinefuncName = NIL;
67 906 : List *serialfuncName = NIL;
68 906 : List *deserialfuncName = NIL;
69 906 : List *mtransfuncName = NIL;
70 906 : List *minvtransfuncName = NIL;
71 906 : List *mfinalfuncName = NIL;
72 906 : bool finalfuncExtraArgs = false;
73 906 : bool mfinalfuncExtraArgs = false;
74 906 : char finalfuncModify = 0;
75 906 : char mfinalfuncModify = 0;
76 906 : List *sortoperatorName = NIL;
77 906 : TypeName *baseType = NULL;
78 906 : TypeName *transType = NULL;
79 906 : TypeName *mtransType = NULL;
80 906 : int32 transSpace = 0;
81 906 : int32 mtransSpace = 0;
82 906 : char *initval = NULL;
83 906 : char *minitval = NULL;
84 906 : char *parallel = NULL;
85 : int numArgs;
86 906 : int numDirectArgs = 0;
87 : oidvector *parameterTypes;
88 : ArrayType *allParameterTypes;
89 : ArrayType *parameterModes;
90 : ArrayType *parameterNames;
91 : List *parameterDefaults;
92 : Oid variadicArgType;
93 : Oid transTypeId;
94 906 : Oid mtransTypeId = InvalidOid;
95 : char transTypeType;
96 906 : char mtransTypeType = 0;
97 906 : char proparallel = PROPARALLEL_UNSAFE;
98 : ListCell *pl;
99 :
100 : /* Convert list of names to a name and namespace */
101 906 : aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
102 :
103 : /* Check we have creation rights in target namespace */
104 906 : aclresult = object_aclcheck(NamespaceRelationId, aggNamespace, GetUserId(), ACL_CREATE);
105 906 : if (aclresult != ACLCHECK_OK)
106 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
107 0 : get_namespace_name(aggNamespace));
108 :
109 : /* Deconstruct the output of the aggr_args grammar production */
110 906 : if (!oldstyle)
111 : {
112 : Assert(list_length(args) == 2);
113 544 : numDirectArgs = intVal(lsecond(args));
114 544 : if (numDirectArgs >= 0)
115 22 : aggKind = AGGKIND_ORDERED_SET;
116 : else
117 522 : numDirectArgs = 0;
118 544 : args = linitial_node(List, args);
119 : }
120 :
121 : /* Examine aggregate's definition clauses */
122 4462 : foreach(pl, parameters)
123 : {
124 3556 : DefElem *defel = lfirst_node(DefElem, pl);
125 :
126 : /*
127 : * sfunc1, stype1, and initcond1 are accepted as obsolete spellings
128 : * for sfunc, stype, initcond.
129 : */
130 3556 : if (strcmp(defel->defname, "sfunc") == 0)
131 864 : transfuncName = defGetQualifiedName(defel);
132 2692 : else if (strcmp(defel->defname, "sfunc1") == 0)
133 30 : transfuncName = defGetQualifiedName(defel);
134 2662 : else if (strcmp(defel->defname, "finalfunc") == 0)
135 388 : finalfuncName = defGetQualifiedName(defel);
136 2274 : else if (strcmp(defel->defname, "combinefunc") == 0)
137 32 : combinefuncName = defGetQualifiedName(defel);
138 2242 : else if (strcmp(defel->defname, "serialfunc") == 0)
139 36 : serialfuncName = defGetQualifiedName(defel);
140 2206 : else if (strcmp(defel->defname, "deserialfunc") == 0)
141 30 : deserialfuncName = defGetQualifiedName(defel);
142 2176 : else if (strcmp(defel->defname, "msfunc") == 0)
143 60 : mtransfuncName = defGetQualifiedName(defel);
144 2116 : else if (strcmp(defel->defname, "minvfunc") == 0)
145 60 : minvtransfuncName = defGetQualifiedName(defel);
146 2056 : else if (strcmp(defel->defname, "mfinalfunc") == 0)
147 0 : mfinalfuncName = defGetQualifiedName(defel);
148 2056 : else if (strcmp(defel->defname, "finalfunc_extra") == 0)
149 16 : finalfuncExtraArgs = defGetBoolean(defel);
150 2040 : else if (strcmp(defel->defname, "mfinalfunc_extra") == 0)
151 0 : mfinalfuncExtraArgs = defGetBoolean(defel);
152 2040 : else if (strcmp(defel->defname, "finalfunc_modify") == 0)
153 20 : finalfuncModify = extractModify(defel);
154 2020 : else if (strcmp(defel->defname, "mfinalfunc_modify") == 0)
155 0 : mfinalfuncModify = extractModify(defel);
156 2020 : else if (strcmp(defel->defname, "sortop") == 0)
157 8 : sortoperatorName = defGetQualifiedName(defel);
158 2012 : else if (strcmp(defel->defname, "basetype") == 0)
159 350 : baseType = defGetTypeName(defel);
160 1662 : else if (strcmp(defel->defname, "hypothetical") == 0)
161 : {
162 8 : if (defGetBoolean(defel))
163 : {
164 8 : if (aggKind == AGGKIND_NORMAL)
165 0 : ereport(ERROR,
166 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
167 : errmsg("only ordered-set aggregates can be hypothetical")));
168 8 : aggKind = AGGKIND_HYPOTHETICAL;
169 : }
170 : }
171 1654 : else if (strcmp(defel->defname, "stype") == 0)
172 864 : transType = defGetTypeName(defel);
173 790 : else if (strcmp(defel->defname, "stype1") == 0)
174 30 : transType = defGetTypeName(defel);
175 760 : else if (strcmp(defel->defname, "sspace") == 0)
176 8 : transSpace = defGetInt32(defel);
177 752 : else if (strcmp(defel->defname, "mstype") == 0)
178 60 : mtransType = defGetTypeName(defel);
179 692 : else if (strcmp(defel->defname, "msspace") == 0)
180 0 : mtransSpace = defGetInt32(defel);
181 692 : else if (strcmp(defel->defname, "initcond") == 0)
182 558 : initval = defGetString(defel);
183 134 : else if (strcmp(defel->defname, "initcond1") == 0)
184 18 : initval = defGetString(defel);
185 116 : else if (strcmp(defel->defname, "minitcond") == 0)
186 16 : minitval = defGetString(defel);
187 100 : else if (strcmp(defel->defname, "parallel") == 0)
188 34 : parallel = defGetString(defel);
189 : else
190 66 : ereport(WARNING,
191 : (errcode(ERRCODE_SYNTAX_ERROR),
192 : errmsg("aggregate attribute \"%s\" not recognized",
193 : defel->defname)));
194 : }
195 :
196 : /*
197 : * make sure we have our required definitions
198 : */
199 906 : if (transType == NULL)
200 12 : ereport(ERROR,
201 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
202 : errmsg("aggregate stype must be specified")));
203 894 : if (transfuncName == NIL)
204 0 : ereport(ERROR,
205 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
206 : errmsg("aggregate sfunc must be specified")));
207 :
208 : /*
209 : * if mtransType is given, mtransfuncName and minvtransfuncName must be as
210 : * well; if not, then none of the moving-aggregate options should have
211 : * been given.
212 : */
213 894 : if (mtransType != NULL)
214 : {
215 60 : if (mtransfuncName == NIL)
216 0 : ereport(ERROR,
217 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
218 : errmsg("aggregate msfunc must be specified when mstype is specified")));
219 60 : if (minvtransfuncName == NIL)
220 0 : ereport(ERROR,
221 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
222 : errmsg("aggregate minvfunc must be specified when mstype is specified")));
223 : }
224 : else
225 : {
226 834 : if (mtransfuncName != NIL)
227 0 : ereport(ERROR,
228 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
229 : errmsg("aggregate msfunc must not be specified without mstype")));
230 834 : if (minvtransfuncName != NIL)
231 0 : ereport(ERROR,
232 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
233 : errmsg("aggregate minvfunc must not be specified without mstype")));
234 834 : if (mfinalfuncName != NIL)
235 0 : ereport(ERROR,
236 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
237 : errmsg("aggregate mfinalfunc must not be specified without mstype")));
238 834 : if (mtransSpace != 0)
239 0 : ereport(ERROR,
240 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
241 : errmsg("aggregate msspace must not be specified without mstype")));
242 834 : if (minitval != NULL)
243 0 : ereport(ERROR,
244 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
245 : errmsg("aggregate minitcond must not be specified without mstype")));
246 : }
247 :
248 : /*
249 : * Default values for modify flags can only be determined once we know the
250 : * aggKind.
251 : */
252 894 : if (finalfuncModify == 0)
253 874 : finalfuncModify = (aggKind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
254 894 : if (mfinalfuncModify == 0)
255 894 : mfinalfuncModify = (aggKind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
256 :
257 : /*
258 : * look up the aggregate's input datatype(s).
259 : */
260 894 : if (oldstyle)
261 : {
262 : /*
263 : * Old style: use basetype parameter. This supports aggregates of
264 : * zero or one input, with input type ANY meaning zero inputs.
265 : *
266 : * Historically we allowed the command to look like basetype = 'ANY'
267 : * so we must do a case-insensitive comparison for the name ANY. Ugh.
268 : */
269 : Oid aggArgTypes[1];
270 :
271 356 : if (baseType == NULL)
272 6 : ereport(ERROR,
273 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
274 : errmsg("aggregate input type must be specified")));
275 :
276 350 : if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
277 : {
278 6 : numArgs = 0;
279 6 : aggArgTypes[0] = InvalidOid;
280 : }
281 : else
282 : {
283 344 : numArgs = 1;
284 344 : aggArgTypes[0] = typenameTypeId(NULL, baseType);
285 : }
286 350 : parameterTypes = buildoidvector(aggArgTypes, numArgs);
287 350 : allParameterTypes = NULL;
288 350 : parameterModes = NULL;
289 350 : parameterNames = NULL;
290 350 : parameterDefaults = NIL;
291 350 : variadicArgType = InvalidOid;
292 : }
293 : else
294 : {
295 : /*
296 : * New style: args is a list of FunctionParameters (possibly zero of
297 : * 'em). We share functioncmds.c's code for processing them.
298 : */
299 : Oid requiredResultType;
300 :
301 538 : if (baseType != NULL)
302 0 : ereport(ERROR,
303 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
304 : errmsg("basetype is redundant with aggregate input type specification")));
305 :
306 538 : numArgs = list_length(args);
307 538 : interpret_function_parameter_list(pstate,
308 : args,
309 : InvalidOid,
310 : OBJECT_AGGREGATE,
311 : ¶meterTypes,
312 : NULL,
313 : &allParameterTypes,
314 : ¶meterModes,
315 : ¶meterNames,
316 : NULL,
317 : ¶meterDefaults,
318 : &variadicArgType,
319 : &requiredResultType);
320 : /* Parameter defaults are not currently allowed by the grammar */
321 : Assert(parameterDefaults == NIL);
322 : /* There shouldn't have been any OUT parameters, either */
323 : Assert(requiredResultType == InvalidOid);
324 : }
325 :
326 : /*
327 : * look up the aggregate's transtype.
328 : *
329 : * transtype can't be a pseudo-type, since we need to be able to store
330 : * values of the transtype. However, we can allow polymorphic transtype
331 : * in some cases (AggregateCreate will check). Also, we allow "internal"
332 : * for functions that want to pass pointers to private data structures;
333 : * but allow that only to superusers, since you could crash the system (or
334 : * worse) by connecting up incompatible internal-using functions in an
335 : * aggregate.
336 : */
337 882 : transTypeId = typenameTypeId(NULL, transType);
338 882 : transTypeType = get_typtype(transTypeId);
339 882 : if (transTypeType == TYPTYPE_PSEUDO &&
340 280 : !IsPolymorphicType(transTypeId))
341 : {
342 58 : if (transTypeId == INTERNALOID && superuser())
343 : /* okay */ ;
344 : else
345 0 : ereport(ERROR,
346 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
347 : errmsg("aggregate transition data type cannot be %s",
348 : format_type_be(transTypeId))));
349 : }
350 :
351 882 : if (serialfuncName && deserialfuncName)
352 : {
353 : /*
354 : * Serialization is only needed/allowed for transtype INTERNAL.
355 : */
356 30 : if (transTypeId != INTERNALOID)
357 0 : ereport(ERROR,
358 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
359 : errmsg("serialization functions may be specified only when the aggregate transition data type is %s",
360 : format_type_be(INTERNALOID))));
361 : }
362 852 : else if (serialfuncName || deserialfuncName)
363 : {
364 : /*
365 : * Cannot specify one function without the other.
366 : */
367 6 : ereport(ERROR,
368 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
369 : errmsg("must specify both or neither of serialization and deserialization functions")));
370 : }
371 :
372 : /*
373 : * If a moving-aggregate transtype is specified, look that up. Same
374 : * restrictions as for transtype.
375 : */
376 876 : if (mtransType)
377 : {
378 60 : mtransTypeId = typenameTypeId(NULL, mtransType);
379 60 : mtransTypeType = get_typtype(mtransTypeId);
380 60 : if (mtransTypeType == TYPTYPE_PSEUDO &&
381 0 : !IsPolymorphicType(mtransTypeId))
382 : {
383 0 : if (mtransTypeId == INTERNALOID && superuser())
384 : /* okay */ ;
385 : else
386 0 : ereport(ERROR,
387 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
388 : errmsg("aggregate transition data type cannot be %s",
389 : format_type_be(mtransTypeId))));
390 : }
391 : }
392 :
393 : /*
394 : * If we have an initval, and it's not for a pseudotype (particularly a
395 : * polymorphic type), make sure it's acceptable to the type's input
396 : * function. We will store the initval as text, because the input
397 : * function isn't necessarily immutable (consider "now" for timestamp),
398 : * and we want to use the runtime not creation-time interpretation of the
399 : * value. However, if it's an incorrect value it seems much more
400 : * user-friendly to complain at CREATE AGGREGATE time.
401 : */
402 876 : if (initval && transTypeType != TYPTYPE_PSEUDO)
403 : {
404 : Oid typinput,
405 : typioparam;
406 :
407 370 : getTypeInputInfo(transTypeId, &typinput, &typioparam);
408 370 : (void) OidInputFunctionCall(typinput, initval, typioparam, -1);
409 : }
410 :
411 : /*
412 : * Likewise for moving-aggregate initval.
413 : */
414 876 : if (minitval && mtransTypeType != TYPTYPE_PSEUDO)
415 : {
416 : Oid typinput,
417 : typioparam;
418 :
419 16 : getTypeInputInfo(mtransTypeId, &typinput, &typioparam);
420 16 : (void) OidInputFunctionCall(typinput, minitval, typioparam, -1);
421 : }
422 :
423 876 : if (parallel)
424 : {
425 34 : if (strcmp(parallel, "safe") == 0)
426 28 : proparallel = PROPARALLEL_SAFE;
427 6 : else if (strcmp(parallel, "restricted") == 0)
428 0 : proparallel = PROPARALLEL_RESTRICTED;
429 6 : else if (strcmp(parallel, "unsafe") == 0)
430 0 : proparallel = PROPARALLEL_UNSAFE;
431 : else
432 6 : ereport(ERROR,
433 : (errcode(ERRCODE_SYNTAX_ERROR),
434 : errmsg("parameter \"parallel\" must be SAFE, RESTRICTED, or UNSAFE")));
435 : }
436 :
437 : /*
438 : * Most of the argument-checking is done inside of AggregateCreate
439 : */
440 870 : return AggregateCreate(aggName, /* aggregate name */
441 : aggNamespace, /* namespace */
442 : replace,
443 : aggKind,
444 : numArgs,
445 : numDirectArgs,
446 : parameterTypes,
447 : PointerGetDatum(allParameterTypes),
448 : PointerGetDatum(parameterModes),
449 : PointerGetDatum(parameterNames),
450 : parameterDefaults,
451 : variadicArgType,
452 : transfuncName, /* step function name */
453 : finalfuncName, /* final function name */
454 : combinefuncName, /* combine function name */
455 : serialfuncName, /* serial function name */
456 : deserialfuncName, /* deserial function name */
457 : mtransfuncName, /* fwd trans function name */
458 : minvtransfuncName, /* inv trans function name */
459 : mfinalfuncName, /* final function name */
460 : finalfuncExtraArgs,
461 : mfinalfuncExtraArgs,
462 : finalfuncModify,
463 : mfinalfuncModify,
464 : sortoperatorName, /* sort operator name */
465 : transTypeId, /* transition data type */
466 : transSpace, /* transition space */
467 : mtransTypeId, /* transition data type */
468 : mtransSpace, /* transition space */
469 : initval, /* initial condition */
470 : minitval, /* initial condition */
471 : proparallel); /* parallel safe? */
472 : }
473 :
474 : /*
475 : * Convert the string form of [m]finalfunc_modify to the catalog representation
476 : */
477 : static char
478 20 : extractModify(DefElem *defel)
479 : {
480 20 : char *val = defGetString(defel);
481 :
482 20 : if (strcmp(val, "read_only") == 0)
483 0 : return AGGMODIFY_READ_ONLY;
484 20 : if (strcmp(val, "shareable") == 0)
485 14 : return AGGMODIFY_SHAREABLE;
486 6 : if (strcmp(val, "read_write") == 0)
487 6 : return AGGMODIFY_READ_WRITE;
488 0 : ereport(ERROR,
489 : (errcode(ERRCODE_SYNTAX_ERROR),
490 : errmsg("parameter \"%s\" must be READ_ONLY, SHAREABLE, or READ_WRITE",
491 : defel->defname)));
492 : return 0; /* keep compiler quiet */
493 : }
|