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