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