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 | |
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 |
57 | DefineAggregate(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 = false; |
77 | bool = 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 | ¶meterTypes, |
316 | &allParameterTypes, |
317 | ¶meterModes, |
318 | ¶meterNames, |
319 | ¶meterDefaults, |
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 | */ |
479 | static char |
480 | (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 | |