| 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 | |