| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * pg_aggregate.c |
| 4 | * routines to support manipulation of the pg_aggregate relation |
| 5 | * |
| 6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 7 | * Portions Copyright (c) 1994, Regents of the University of California |
| 8 | * |
| 9 | * |
| 10 | * IDENTIFICATION |
| 11 | * src/backend/catalog/pg_aggregate.c |
| 12 | * |
| 13 | *------------------------------------------------------------------------- |
| 14 | */ |
| 15 | #include "postgres.h" |
| 16 | |
| 17 | #include "access/htup_details.h" |
| 18 | #include "access/table.h" |
| 19 | #include "catalog/dependency.h" |
| 20 | #include "catalog/indexing.h" |
| 21 | #include "catalog/pg_aggregate.h" |
| 22 | #include "catalog/pg_language.h" |
| 23 | #include "catalog/pg_operator.h" |
| 24 | #include "catalog/pg_proc.h" |
| 25 | #include "catalog/pg_type.h" |
| 26 | #include "miscadmin.h" |
| 27 | #include "parser/parse_coerce.h" |
| 28 | #include "parser/parse_func.h" |
| 29 | #include "parser/parse_oper.h" |
| 30 | #include "utils/acl.h" |
| 31 | #include "utils/builtins.h" |
| 32 | #include "utils/lsyscache.h" |
| 33 | #include "utils/rel.h" |
| 34 | #include "utils/syscache.h" |
| 35 | |
| 36 | |
| 37 | static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types, |
| 38 | Oid variadicArgType, |
| 39 | Oid *rettype); |
| 40 | |
| 41 | |
| 42 | /* |
| 43 | * AggregateCreate |
| 44 | */ |
| 45 | ObjectAddress |
| 46 | AggregateCreate(const char *aggName, |
| 47 | Oid aggNamespace, |
| 48 | bool replace, |
| 49 | char aggKind, |
| 50 | int numArgs, |
| 51 | int numDirectArgs, |
| 52 | oidvector *parameterTypes, |
| 53 | Datum allParameterTypes, |
| 54 | Datum parameterModes, |
| 55 | Datum parameterNames, |
| 56 | List *parameterDefaults, |
| 57 | Oid variadicArgType, |
| 58 | List *aggtransfnName, |
| 59 | List *aggfinalfnName, |
| 60 | List *aggcombinefnName, |
| 61 | List *aggserialfnName, |
| 62 | List *aggdeserialfnName, |
| 63 | List *aggmtransfnName, |
| 64 | List *aggminvtransfnName, |
| 65 | List *aggmfinalfnName, |
| 66 | bool , |
| 67 | bool , |
| 68 | char finalfnModify, |
| 69 | char mfinalfnModify, |
| 70 | List *aggsortopName, |
| 71 | Oid aggTransType, |
| 72 | int32 aggTransSpace, |
| 73 | Oid aggmTransType, |
| 74 | int32 aggmTransSpace, |
| 75 | const char *agginitval, |
| 76 | const char *aggminitval, |
| 77 | char proparallel) |
| 78 | { |
| 79 | Relation aggdesc; |
| 80 | HeapTuple tup; |
| 81 | HeapTuple oldtup; |
| 82 | bool nulls[Natts_pg_aggregate]; |
| 83 | Datum values[Natts_pg_aggregate]; |
| 84 | bool replaces[Natts_pg_aggregate]; |
| 85 | Form_pg_proc proc; |
| 86 | Oid transfn; |
| 87 | Oid finalfn = InvalidOid; /* can be omitted */ |
| 88 | Oid combinefn = InvalidOid; /* can be omitted */ |
| 89 | Oid serialfn = InvalidOid; /* can be omitted */ |
| 90 | Oid deserialfn = InvalidOid; /* can be omitted */ |
| 91 | Oid mtransfn = InvalidOid; /* can be omitted */ |
| 92 | Oid minvtransfn = InvalidOid; /* can be omitted */ |
| 93 | Oid mfinalfn = InvalidOid; /* can be omitted */ |
| 94 | Oid sortop = InvalidOid; /* can be omitted */ |
| 95 | Oid *aggArgTypes = parameterTypes->values; |
| 96 | bool hasPolyArg; |
| 97 | bool hasInternalArg; |
| 98 | bool mtransIsStrict = false; |
| 99 | Oid rettype; |
| 100 | Oid finaltype; |
| 101 | Oid fnArgs[FUNC_MAX_ARGS]; |
| 102 | int nargs_transfn; |
| 103 | int nargs_finalfn; |
| 104 | Oid procOid; |
| 105 | TupleDesc tupDesc; |
| 106 | int i; |
| 107 | ObjectAddress myself, |
| 108 | referenced; |
| 109 | AclResult aclresult; |
| 110 | |
| 111 | /* sanity checks (caller should have caught these) */ |
| 112 | if (!aggName) |
| 113 | elog(ERROR, "no aggregate name supplied" ); |
| 114 | |
| 115 | if (!aggtransfnName) |
| 116 | elog(ERROR, "aggregate must have a transition function" ); |
| 117 | |
| 118 | if (numDirectArgs < 0 || numDirectArgs > numArgs) |
| 119 | elog(ERROR, "incorrect number of direct arguments for aggregate" ); |
| 120 | |
| 121 | /* |
| 122 | * Aggregates can have at most FUNC_MAX_ARGS-1 args, else the transfn |
| 123 | * and/or finalfn will be unrepresentable in pg_proc. We must check now |
| 124 | * to protect fixed-size arrays here and possibly in called functions. |
| 125 | */ |
| 126 | if (numArgs < 0 || numArgs > FUNC_MAX_ARGS - 1) |
| 127 | ereport(ERROR, |
| 128 | (errcode(ERRCODE_TOO_MANY_ARGUMENTS), |
| 129 | errmsg_plural("aggregates cannot have more than %d argument" , |
| 130 | "aggregates cannot have more than %d arguments" , |
| 131 | FUNC_MAX_ARGS - 1, |
| 132 | FUNC_MAX_ARGS - 1))); |
| 133 | |
| 134 | /* check for polymorphic and INTERNAL arguments */ |
| 135 | hasPolyArg = false; |
| 136 | hasInternalArg = false; |
| 137 | for (i = 0; i < numArgs; i++) |
| 138 | { |
| 139 | if (IsPolymorphicType(aggArgTypes[i])) |
| 140 | hasPolyArg = true; |
| 141 | else if (aggArgTypes[i] == INTERNALOID) |
| 142 | hasInternalArg = true; |
| 143 | } |
| 144 | |
| 145 | /* |
| 146 | * If transtype is polymorphic, must have polymorphic argument also; else |
| 147 | * we will have no way to deduce the actual transtype. |
| 148 | */ |
| 149 | if (IsPolymorphicType(aggTransType) && !hasPolyArg) |
| 150 | ereport(ERROR, |
| 151 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 152 | errmsg("cannot determine transition data type" ), |
| 153 | errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument." ))); |
| 154 | |
| 155 | /* |
| 156 | * Likewise for moving-aggregate transtype, if any |
| 157 | */ |
| 158 | if (OidIsValid(aggmTransType) && |
| 159 | IsPolymorphicType(aggmTransType) && !hasPolyArg) |
| 160 | ereport(ERROR, |
| 161 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 162 | errmsg("cannot determine transition data type" ), |
| 163 | errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument." ))); |
| 164 | |
| 165 | /* |
| 166 | * An ordered-set aggregate that is VARIADIC must be VARIADIC ANY. In |
| 167 | * principle we could support regular variadic types, but it would make |
| 168 | * things much more complicated because we'd have to assemble the correct |
| 169 | * subsets of arguments into array values. Since no standard aggregates |
| 170 | * have use for such a case, we aren't bothering for now. |
| 171 | */ |
| 172 | if (AGGKIND_IS_ORDERED_SET(aggKind) && OidIsValid(variadicArgType) && |
| 173 | variadicArgType != ANYOID) |
| 174 | ereport(ERROR, |
| 175 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 176 | errmsg("a variadic ordered-set aggregate must use VARIADIC type ANY" ))); |
| 177 | |
| 178 | /* |
| 179 | * If it's a hypothetical-set aggregate, there must be at least as many |
| 180 | * direct arguments as aggregated ones, and the last N direct arguments |
| 181 | * must match the aggregated ones in type. (We have to check this again |
| 182 | * when the aggregate is called, in case ANY is involved, but it makes |
| 183 | * sense to reject the aggregate definition now if the declared arg types |
| 184 | * don't match up.) It's unconditionally OK if numDirectArgs == numArgs, |
| 185 | * indicating that the grammar merged identical VARIADIC entries from both |
| 186 | * lists. Otherwise, if the agg is VARIADIC, then we had VARIADIC only on |
| 187 | * the aggregated side, which is not OK. Otherwise, insist on the last N |
| 188 | * parameter types on each side matching exactly. |
| 189 | */ |
| 190 | if (aggKind == AGGKIND_HYPOTHETICAL && |
| 191 | numDirectArgs < numArgs) |
| 192 | { |
| 193 | int numAggregatedArgs = numArgs - numDirectArgs; |
| 194 | |
| 195 | if (OidIsValid(variadicArgType) || |
| 196 | numDirectArgs < numAggregatedArgs || |
| 197 | memcmp(aggArgTypes + (numDirectArgs - numAggregatedArgs), |
| 198 | aggArgTypes + numDirectArgs, |
| 199 | numAggregatedArgs * sizeof(Oid)) != 0) |
| 200 | ereport(ERROR, |
| 201 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 202 | errmsg("a hypothetical-set aggregate must have direct arguments matching its aggregated arguments" ))); |
| 203 | } |
| 204 | |
| 205 | /* |
| 206 | * Find the transfn. For ordinary aggs, it takes the transtype plus all |
| 207 | * aggregate arguments. For ordered-set aggs, it takes the transtype plus |
| 208 | * all aggregated args, but not direct args. However, we have to treat |
| 209 | * specially the case where a trailing VARIADIC item is considered to |
| 210 | * cover both direct and aggregated args. |
| 211 | */ |
| 212 | if (AGGKIND_IS_ORDERED_SET(aggKind)) |
| 213 | { |
| 214 | if (numDirectArgs < numArgs) |
| 215 | nargs_transfn = numArgs - numDirectArgs + 1; |
| 216 | else |
| 217 | { |
| 218 | /* special case with VARIADIC last arg */ |
| 219 | Assert(variadicArgType != InvalidOid); |
| 220 | nargs_transfn = 2; |
| 221 | } |
| 222 | fnArgs[0] = aggTransType; |
| 223 | memcpy(fnArgs + 1, aggArgTypes + (numArgs - (nargs_transfn - 1)), |
| 224 | (nargs_transfn - 1) * sizeof(Oid)); |
| 225 | } |
| 226 | else |
| 227 | { |
| 228 | nargs_transfn = numArgs + 1; |
| 229 | fnArgs[0] = aggTransType; |
| 230 | memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); |
| 231 | } |
| 232 | transfn = lookup_agg_function(aggtransfnName, nargs_transfn, |
| 233 | fnArgs, variadicArgType, |
| 234 | &rettype); |
| 235 | |
| 236 | /* |
| 237 | * Return type of transfn (possibly after refinement by |
| 238 | * enforce_generic_type_consistency, if transtype isn't polymorphic) must |
| 239 | * exactly match declared transtype. |
| 240 | * |
| 241 | * In the non-polymorphic-transtype case, it might be okay to allow a |
| 242 | * rettype that's binary-coercible to transtype, but I'm not quite |
| 243 | * convinced that it's either safe or useful. When transtype is |
| 244 | * polymorphic we *must* demand exact equality. |
| 245 | */ |
| 246 | if (rettype != aggTransType) |
| 247 | ereport(ERROR, |
| 248 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 249 | errmsg("return type of transition function %s is not %s" , |
| 250 | NameListToString(aggtransfnName), |
| 251 | format_type_be(aggTransType)))); |
| 252 | |
| 253 | tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn)); |
| 254 | if (!HeapTupleIsValid(tup)) |
| 255 | elog(ERROR, "cache lookup failed for function %u" , transfn); |
| 256 | proc = (Form_pg_proc) GETSTRUCT(tup); |
| 257 | |
| 258 | /* |
| 259 | * If the transfn is strict and the initval is NULL, make sure first input |
| 260 | * type and transtype are the same (or at least binary-compatible), so |
| 261 | * that it's OK to use the first input value as the initial transValue. |
| 262 | */ |
| 263 | if (proc->proisstrict && agginitval == NULL) |
| 264 | { |
| 265 | if (numArgs < 1 || |
| 266 | !IsBinaryCoercible(aggArgTypes[0], aggTransType)) |
| 267 | ereport(ERROR, |
| 268 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 269 | errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type" ))); |
| 270 | } |
| 271 | |
| 272 | ReleaseSysCache(tup); |
| 273 | |
| 274 | /* handle moving-aggregate transfn, if supplied */ |
| 275 | if (aggmtransfnName) |
| 276 | { |
| 277 | /* |
| 278 | * The arguments are the same as for the regular transfn, except that |
| 279 | * the transition data type might be different. So re-use the fnArgs |
| 280 | * values set up above, except for that one. |
| 281 | */ |
| 282 | Assert(OidIsValid(aggmTransType)); |
| 283 | fnArgs[0] = aggmTransType; |
| 284 | |
| 285 | mtransfn = lookup_agg_function(aggmtransfnName, nargs_transfn, |
| 286 | fnArgs, variadicArgType, |
| 287 | &rettype); |
| 288 | |
| 289 | /* As above, return type must exactly match declared mtranstype. */ |
| 290 | if (rettype != aggmTransType) |
| 291 | ereport(ERROR, |
| 292 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 293 | errmsg("return type of transition function %s is not %s" , |
| 294 | NameListToString(aggmtransfnName), |
| 295 | format_type_be(aggmTransType)))); |
| 296 | |
| 297 | tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(mtransfn)); |
| 298 | if (!HeapTupleIsValid(tup)) |
| 299 | elog(ERROR, "cache lookup failed for function %u" , mtransfn); |
| 300 | proc = (Form_pg_proc) GETSTRUCT(tup); |
| 301 | |
| 302 | /* |
| 303 | * If the mtransfn is strict and the minitval is NULL, check first |
| 304 | * input type and mtranstype are binary-compatible. |
| 305 | */ |
| 306 | if (proc->proisstrict && aggminitval == NULL) |
| 307 | { |
| 308 | if (numArgs < 1 || |
| 309 | !IsBinaryCoercible(aggArgTypes[0], aggmTransType)) |
| 310 | ereport(ERROR, |
| 311 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 312 | errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type" ))); |
| 313 | } |
| 314 | |
| 315 | /* Remember if mtransfn is strict; we may need this below */ |
| 316 | mtransIsStrict = proc->proisstrict; |
| 317 | |
| 318 | ReleaseSysCache(tup); |
| 319 | } |
| 320 | |
| 321 | /* handle minvtransfn, if supplied */ |
| 322 | if (aggminvtransfnName) |
| 323 | { |
| 324 | /* |
| 325 | * This must have the same number of arguments with the same types as |
| 326 | * the forward transition function, so just re-use the fnArgs data. |
| 327 | */ |
| 328 | Assert(aggmtransfnName); |
| 329 | |
| 330 | minvtransfn = lookup_agg_function(aggminvtransfnName, nargs_transfn, |
| 331 | fnArgs, variadicArgType, |
| 332 | &rettype); |
| 333 | |
| 334 | /* As above, return type must exactly match declared mtranstype. */ |
| 335 | if (rettype != aggmTransType) |
| 336 | ereport(ERROR, |
| 337 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 338 | errmsg("return type of inverse transition function %s is not %s" , |
| 339 | NameListToString(aggminvtransfnName), |
| 340 | format_type_be(aggmTransType)))); |
| 341 | |
| 342 | tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(minvtransfn)); |
| 343 | if (!HeapTupleIsValid(tup)) |
| 344 | elog(ERROR, "cache lookup failed for function %u" , minvtransfn); |
| 345 | proc = (Form_pg_proc) GETSTRUCT(tup); |
| 346 | |
| 347 | /* |
| 348 | * We require the strictness settings of the forward and inverse |
| 349 | * transition functions to agree. This saves having to handle |
| 350 | * assorted special cases at execution time. |
| 351 | */ |
| 352 | if (proc->proisstrict != mtransIsStrict) |
| 353 | ereport(ERROR, |
| 354 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 355 | errmsg("strictness of aggregate's forward and inverse transition functions must match" ))); |
| 356 | |
| 357 | ReleaseSysCache(tup); |
| 358 | } |
| 359 | |
| 360 | /* handle finalfn, if supplied */ |
| 361 | if (aggfinalfnName) |
| 362 | { |
| 363 | /* |
| 364 | * If finalfnExtraArgs is specified, the transfn takes the transtype |
| 365 | * plus all args; otherwise, it just takes the transtype plus any |
| 366 | * direct args. (Non-direct args are useless at runtime, and are |
| 367 | * actually passed as NULLs, but we may need them in the function |
| 368 | * signature to allow resolution of a polymorphic agg's result type.) |
| 369 | */ |
| 370 | Oid ffnVariadicArgType = variadicArgType; |
| 371 | |
| 372 | fnArgs[0] = aggTransType; |
| 373 | memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); |
| 374 | if (finalfnExtraArgs) |
| 375 | nargs_finalfn = numArgs + 1; |
| 376 | else |
| 377 | { |
| 378 | nargs_finalfn = numDirectArgs + 1; |
| 379 | if (numDirectArgs < numArgs) |
| 380 | { |
| 381 | /* variadic argument doesn't affect finalfn */ |
| 382 | ffnVariadicArgType = InvalidOid; |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | finalfn = lookup_agg_function(aggfinalfnName, nargs_finalfn, |
| 387 | fnArgs, ffnVariadicArgType, |
| 388 | &finaltype); |
| 389 | |
| 390 | /* |
| 391 | * When finalfnExtraArgs is specified, the finalfn will certainly be |
| 392 | * passed at least one null argument, so complain if it's strict. |
| 393 | * Nothing bad would happen at runtime (you'd just get a null result), |
| 394 | * but it's surely not what the user wants, so let's complain now. |
| 395 | */ |
| 396 | if (finalfnExtraArgs && func_strict(finalfn)) |
| 397 | ereport(ERROR, |
| 398 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 399 | errmsg("final function with extra arguments must not be declared STRICT" ))); |
| 400 | } |
| 401 | else |
| 402 | { |
| 403 | /* |
| 404 | * If no finalfn, aggregate result type is type of the state value |
| 405 | */ |
| 406 | finaltype = aggTransType; |
| 407 | } |
| 408 | Assert(OidIsValid(finaltype)); |
| 409 | |
| 410 | /* handle the combinefn, if supplied */ |
| 411 | if (aggcombinefnName) |
| 412 | { |
| 413 | Oid combineType; |
| 414 | |
| 415 | /* |
| 416 | * Combine function must have 2 arguments, each of which is the trans |
| 417 | * type. VARIADIC doesn't affect it. |
| 418 | */ |
| 419 | fnArgs[0] = aggTransType; |
| 420 | fnArgs[1] = aggTransType; |
| 421 | |
| 422 | combinefn = lookup_agg_function(aggcombinefnName, 2, |
| 423 | fnArgs, InvalidOid, |
| 424 | &combineType); |
| 425 | |
| 426 | /* Ensure the return type matches the aggregate's trans type */ |
| 427 | if (combineType != aggTransType) |
| 428 | ereport(ERROR, |
| 429 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 430 | errmsg("return type of combine function %s is not %s" , |
| 431 | NameListToString(aggcombinefnName), |
| 432 | format_type_be(aggTransType)))); |
| 433 | |
| 434 | /* |
| 435 | * A combine function to combine INTERNAL states must accept nulls and |
| 436 | * ensure that the returned state is in the correct memory context. We |
| 437 | * cannot directly check the latter, but we can check the former. |
| 438 | */ |
| 439 | if (aggTransType == INTERNALOID && func_strict(combinefn)) |
| 440 | ereport(ERROR, |
| 441 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 442 | errmsg("combine function with transition type %s must not be declared STRICT" , |
| 443 | format_type_be(aggTransType)))); |
| 444 | } |
| 445 | |
| 446 | /* |
| 447 | * Validate the serialization function, if present. |
| 448 | */ |
| 449 | if (aggserialfnName) |
| 450 | { |
| 451 | /* signature is always serialize(internal) returns bytea */ |
| 452 | fnArgs[0] = INTERNALOID; |
| 453 | |
| 454 | serialfn = lookup_agg_function(aggserialfnName, 1, |
| 455 | fnArgs, InvalidOid, |
| 456 | &rettype); |
| 457 | |
| 458 | if (rettype != BYTEAOID) |
| 459 | ereport(ERROR, |
| 460 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 461 | errmsg("return type of serialization function %s is not %s" , |
| 462 | NameListToString(aggserialfnName), |
| 463 | format_type_be(BYTEAOID)))); |
| 464 | } |
| 465 | |
| 466 | /* |
| 467 | * Validate the deserialization function, if present. |
| 468 | */ |
| 469 | if (aggdeserialfnName) |
| 470 | { |
| 471 | /* signature is always deserialize(bytea, internal) returns internal */ |
| 472 | fnArgs[0] = BYTEAOID; |
| 473 | fnArgs[1] = INTERNALOID; /* dummy argument for type safety */ |
| 474 | |
| 475 | deserialfn = lookup_agg_function(aggdeserialfnName, 2, |
| 476 | fnArgs, InvalidOid, |
| 477 | &rettype); |
| 478 | |
| 479 | if (rettype != INTERNALOID) |
| 480 | ereport(ERROR, |
| 481 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 482 | errmsg("return type of deserialization function %s is not %s" , |
| 483 | NameListToString(aggdeserialfnName), |
| 484 | format_type_be(INTERNALOID)))); |
| 485 | } |
| 486 | |
| 487 | /* |
| 488 | * If finaltype (i.e. aggregate return type) is polymorphic, inputs must |
| 489 | * be polymorphic also, else parser will fail to deduce result type. |
| 490 | * (Note: given the previous test on transtype and inputs, this cannot |
| 491 | * happen, unless someone has snuck a finalfn definition into the catalogs |
| 492 | * that itself violates the rule against polymorphic result with no |
| 493 | * polymorphic input.) |
| 494 | */ |
| 495 | if (IsPolymorphicType(finaltype) && !hasPolyArg) |
| 496 | ereport(ERROR, |
| 497 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 498 | errmsg("cannot determine result data type" ), |
| 499 | errdetail("An aggregate returning a polymorphic type " |
| 500 | "must have at least one polymorphic argument." ))); |
| 501 | |
| 502 | /* |
| 503 | * Also, the return type can't be INTERNAL unless there's at least one |
| 504 | * INTERNAL argument. This is the same type-safety restriction we enforce |
| 505 | * for regular functions, but at the level of aggregates. We must test |
| 506 | * this explicitly because we allow INTERNAL as the transtype. |
| 507 | */ |
| 508 | if (finaltype == INTERNALOID && !hasInternalArg) |
| 509 | ereport(ERROR, |
| 510 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 511 | errmsg("unsafe use of pseudo-type \"internal\"" ), |
| 512 | errdetail("A function returning \"internal\" must have at least one \"internal\" argument." ))); |
| 513 | |
| 514 | /* |
| 515 | * If a moving-aggregate implementation is supplied, look up its finalfn |
| 516 | * if any, and check that the implied aggregate result type matches the |
| 517 | * plain implementation. |
| 518 | */ |
| 519 | if (OidIsValid(aggmTransType)) |
| 520 | { |
| 521 | /* handle finalfn, if supplied */ |
| 522 | if (aggmfinalfnName) |
| 523 | { |
| 524 | /* |
| 525 | * The arguments are figured the same way as for the regular |
| 526 | * finalfn, but using aggmTransType and mfinalfnExtraArgs. |
| 527 | */ |
| 528 | Oid ffnVariadicArgType = variadicArgType; |
| 529 | |
| 530 | fnArgs[0] = aggmTransType; |
| 531 | memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); |
| 532 | if (mfinalfnExtraArgs) |
| 533 | nargs_finalfn = numArgs + 1; |
| 534 | else |
| 535 | { |
| 536 | nargs_finalfn = numDirectArgs + 1; |
| 537 | if (numDirectArgs < numArgs) |
| 538 | { |
| 539 | /* variadic argument doesn't affect finalfn */ |
| 540 | ffnVariadicArgType = InvalidOid; |
| 541 | } |
| 542 | } |
| 543 | |
| 544 | mfinalfn = lookup_agg_function(aggmfinalfnName, nargs_finalfn, |
| 545 | fnArgs, ffnVariadicArgType, |
| 546 | &rettype); |
| 547 | |
| 548 | /* As above, check strictness if mfinalfnExtraArgs is given */ |
| 549 | if (mfinalfnExtraArgs && func_strict(mfinalfn)) |
| 550 | ereport(ERROR, |
| 551 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 552 | errmsg("final function with extra arguments must not be declared STRICT" ))); |
| 553 | } |
| 554 | else |
| 555 | { |
| 556 | /* |
| 557 | * If no finalfn, aggregate result type is type of the state value |
| 558 | */ |
| 559 | rettype = aggmTransType; |
| 560 | } |
| 561 | Assert(OidIsValid(rettype)); |
| 562 | if (rettype != finaltype) |
| 563 | ereport(ERROR, |
| 564 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 565 | errmsg("moving-aggregate implementation returns type %s, but plain implementation returns type %s" , |
| 566 | format_type_be(aggmTransType), |
| 567 | format_type_be(aggTransType)))); |
| 568 | } |
| 569 | |
| 570 | /* handle sortop, if supplied */ |
| 571 | if (aggsortopName) |
| 572 | { |
| 573 | if (numArgs != 1) |
| 574 | ereport(ERROR, |
| 575 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 576 | errmsg("sort operator can only be specified for single-argument aggregates" ))); |
| 577 | sortop = LookupOperName(NULL, aggsortopName, |
| 578 | aggArgTypes[0], aggArgTypes[0], |
| 579 | false, -1); |
| 580 | } |
| 581 | |
| 582 | /* |
| 583 | * permission checks on used types |
| 584 | */ |
| 585 | for (i = 0; i < numArgs; i++) |
| 586 | { |
| 587 | aclresult = pg_type_aclcheck(aggArgTypes[i], GetUserId(), ACL_USAGE); |
| 588 | if (aclresult != ACLCHECK_OK) |
| 589 | aclcheck_error_type(aclresult, aggArgTypes[i]); |
| 590 | } |
| 591 | |
| 592 | aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE); |
| 593 | if (aclresult != ACLCHECK_OK) |
| 594 | aclcheck_error_type(aclresult, aggTransType); |
| 595 | |
| 596 | if (OidIsValid(aggmTransType)) |
| 597 | { |
| 598 | aclresult = pg_type_aclcheck(aggmTransType, GetUserId(), ACL_USAGE); |
| 599 | if (aclresult != ACLCHECK_OK) |
| 600 | aclcheck_error_type(aclresult, aggmTransType); |
| 601 | } |
| 602 | |
| 603 | aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE); |
| 604 | if (aclresult != ACLCHECK_OK) |
| 605 | aclcheck_error_type(aclresult, finaltype); |
| 606 | |
| 607 | |
| 608 | /* |
| 609 | * Everything looks okay. Try to create the pg_proc entry for the |
| 610 | * aggregate. (This could fail if there's already a conflicting entry.) |
| 611 | */ |
| 612 | |
| 613 | myself = ProcedureCreate(aggName, |
| 614 | aggNamespace, |
| 615 | replace, /* maybe replacement */ |
| 616 | false, /* doesn't return a set */ |
| 617 | finaltype, /* returnType */ |
| 618 | GetUserId(), /* proowner */ |
| 619 | INTERNALlanguageId, /* languageObjectId */ |
| 620 | InvalidOid, /* no validator */ |
| 621 | "aggregate_dummy" , /* placeholder proc */ |
| 622 | NULL, /* probin */ |
| 623 | PROKIND_AGGREGATE, |
| 624 | false, /* security invoker (currently not |
| 625 | * definable for agg) */ |
| 626 | false, /* isLeakProof */ |
| 627 | false, /* isStrict (not needed for agg) */ |
| 628 | PROVOLATILE_IMMUTABLE, /* volatility (not needed |
| 629 | * for agg) */ |
| 630 | proparallel, |
| 631 | parameterTypes, /* paramTypes */ |
| 632 | allParameterTypes, /* allParamTypes */ |
| 633 | parameterModes, /* parameterModes */ |
| 634 | parameterNames, /* parameterNames */ |
| 635 | parameterDefaults, /* parameterDefaults */ |
| 636 | PointerGetDatum(NULL), /* trftypes */ |
| 637 | PointerGetDatum(NULL), /* proconfig */ |
| 638 | InvalidOid, /* no prosupport */ |
| 639 | 1, /* procost */ |
| 640 | 0); /* prorows */ |
| 641 | procOid = myself.objectId; |
| 642 | |
| 643 | /* |
| 644 | * Okay to create the pg_aggregate entry. |
| 645 | */ |
| 646 | aggdesc = table_open(AggregateRelationId, RowExclusiveLock); |
| 647 | tupDesc = aggdesc->rd_att; |
| 648 | |
| 649 | /* initialize nulls and values */ |
| 650 | for (i = 0; i < Natts_pg_aggregate; i++) |
| 651 | { |
| 652 | nulls[i] = false; |
| 653 | values[i] = (Datum) NULL; |
| 654 | replaces[i] = true; |
| 655 | } |
| 656 | values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); |
| 657 | values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(aggKind); |
| 658 | values[Anum_pg_aggregate_aggnumdirectargs - 1] = Int16GetDatum(numDirectArgs); |
| 659 | values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); |
| 660 | values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); |
| 661 | values[Anum_pg_aggregate_aggcombinefn - 1] = ObjectIdGetDatum(combinefn); |
| 662 | values[Anum_pg_aggregate_aggserialfn - 1] = ObjectIdGetDatum(serialfn); |
| 663 | values[Anum_pg_aggregate_aggdeserialfn - 1] = ObjectIdGetDatum(deserialfn); |
| 664 | values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(mtransfn); |
| 665 | values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(minvtransfn); |
| 666 | values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(mfinalfn); |
| 667 | values[Anum_pg_aggregate_aggfinalextra - 1] = BoolGetDatum(finalfnExtraArgs); |
| 668 | values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(mfinalfnExtraArgs); |
| 669 | values[Anum_pg_aggregate_aggfinalmodify - 1] = CharGetDatum(finalfnModify); |
| 670 | values[Anum_pg_aggregate_aggmfinalmodify - 1] = CharGetDatum(mfinalfnModify); |
| 671 | values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); |
| 672 | values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); |
| 673 | values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace); |
| 674 | values[Anum_pg_aggregate_aggmtranstype - 1] = ObjectIdGetDatum(aggmTransType); |
| 675 | values[Anum_pg_aggregate_aggmtransspace - 1] = Int32GetDatum(aggmTransSpace); |
| 676 | if (agginitval) |
| 677 | values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); |
| 678 | else |
| 679 | nulls[Anum_pg_aggregate_agginitval - 1] = true; |
| 680 | if (aggminitval) |
| 681 | values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval); |
| 682 | else |
| 683 | nulls[Anum_pg_aggregate_aggminitval - 1] = true; |
| 684 | |
| 685 | if (replace) |
| 686 | oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid)); |
| 687 | else |
| 688 | oldtup = NULL; |
| 689 | |
| 690 | if (HeapTupleIsValid(oldtup)) |
| 691 | { |
| 692 | Form_pg_aggregate oldagg = (Form_pg_aggregate) GETSTRUCT(oldtup); |
| 693 | |
| 694 | /* |
| 695 | * If we're replacing an existing entry, we need to validate that |
| 696 | * we're not changing anything that would break callers. Specifically |
| 697 | * we must not change aggkind or aggnumdirectargs, which affect how an |
| 698 | * aggregate call is treated in parse analysis. |
| 699 | */ |
| 700 | if (aggKind != oldagg->aggkind) |
| 701 | ereport(ERROR, |
| 702 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| 703 | errmsg("cannot change routine kind" ), |
| 704 | (oldagg->aggkind == AGGKIND_NORMAL ? |
| 705 | errdetail("\"%s\" is an ordinary aggregate function." , aggName) : |
| 706 | oldagg->aggkind == AGGKIND_ORDERED_SET ? |
| 707 | errdetail("\"%s\" is an ordered-set aggregate." , aggName) : |
| 708 | oldagg->aggkind == AGGKIND_HYPOTHETICAL ? |
| 709 | errdetail("\"%s\" is a hypothetical-set aggregate." , aggName) : |
| 710 | 0))); |
| 711 | if (numDirectArgs != oldagg->aggnumdirectargs) |
| 712 | ereport(ERROR, |
| 713 | (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| 714 | errmsg("cannot change number of direct arguments of an aggregate function" ))); |
| 715 | |
| 716 | replaces[Anum_pg_aggregate_aggfnoid - 1] = false; |
| 717 | replaces[Anum_pg_aggregate_aggkind - 1] = false; |
| 718 | replaces[Anum_pg_aggregate_aggnumdirectargs - 1] = false; |
| 719 | |
| 720 | tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); |
| 721 | CatalogTupleUpdate(aggdesc, &tup->t_self, tup); |
| 722 | ReleaseSysCache(oldtup); |
| 723 | } |
| 724 | else |
| 725 | { |
| 726 | tup = heap_form_tuple(tupDesc, values, nulls); |
| 727 | CatalogTupleInsert(aggdesc, tup); |
| 728 | } |
| 729 | |
| 730 | table_close(aggdesc, RowExclusiveLock); |
| 731 | |
| 732 | /* |
| 733 | * Create dependencies for the aggregate (above and beyond those already |
| 734 | * made by ProcedureCreate). Note: we don't need an explicit dependency |
| 735 | * on aggTransType since we depend on it indirectly through transfn. |
| 736 | * Likewise for aggmTransType using the mtransfunc, if it exists. |
| 737 | * |
| 738 | * If we're replacing an existing definition, ProcedureCreate deleted all |
| 739 | * our existing dependencies, so we have to do the same things here either |
| 740 | * way. |
| 741 | */ |
| 742 | |
| 743 | /* Depends on transition function */ |
| 744 | referenced.classId = ProcedureRelationId; |
| 745 | referenced.objectId = transfn; |
| 746 | referenced.objectSubId = 0; |
| 747 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 748 | |
| 749 | /* Depends on final function, if any */ |
| 750 | if (OidIsValid(finalfn)) |
| 751 | { |
| 752 | referenced.classId = ProcedureRelationId; |
| 753 | referenced.objectId = finalfn; |
| 754 | referenced.objectSubId = 0; |
| 755 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 756 | } |
| 757 | |
| 758 | /* Depends on combine function, if any */ |
| 759 | if (OidIsValid(combinefn)) |
| 760 | { |
| 761 | referenced.classId = ProcedureRelationId; |
| 762 | referenced.objectId = combinefn; |
| 763 | referenced.objectSubId = 0; |
| 764 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 765 | } |
| 766 | |
| 767 | /* Depends on serialization function, if any */ |
| 768 | if (OidIsValid(serialfn)) |
| 769 | { |
| 770 | referenced.classId = ProcedureRelationId; |
| 771 | referenced.objectId = serialfn; |
| 772 | referenced.objectSubId = 0; |
| 773 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 774 | } |
| 775 | |
| 776 | /* Depends on deserialization function, if any */ |
| 777 | if (OidIsValid(deserialfn)) |
| 778 | { |
| 779 | referenced.classId = ProcedureRelationId; |
| 780 | referenced.objectId = deserialfn; |
| 781 | referenced.objectSubId = 0; |
| 782 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 783 | } |
| 784 | |
| 785 | /* Depends on forward transition function, if any */ |
| 786 | if (OidIsValid(mtransfn)) |
| 787 | { |
| 788 | referenced.classId = ProcedureRelationId; |
| 789 | referenced.objectId = mtransfn; |
| 790 | referenced.objectSubId = 0; |
| 791 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 792 | } |
| 793 | |
| 794 | /* Depends on inverse transition function, if any */ |
| 795 | if (OidIsValid(minvtransfn)) |
| 796 | { |
| 797 | referenced.classId = ProcedureRelationId; |
| 798 | referenced.objectId = minvtransfn; |
| 799 | referenced.objectSubId = 0; |
| 800 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 801 | } |
| 802 | |
| 803 | /* Depends on final function, if any */ |
| 804 | if (OidIsValid(mfinalfn)) |
| 805 | { |
| 806 | referenced.classId = ProcedureRelationId; |
| 807 | referenced.objectId = mfinalfn; |
| 808 | referenced.objectSubId = 0; |
| 809 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 810 | } |
| 811 | |
| 812 | /* Depends on sort operator, if any */ |
| 813 | if (OidIsValid(sortop)) |
| 814 | { |
| 815 | referenced.classId = OperatorRelationId; |
| 816 | referenced.objectId = sortop; |
| 817 | referenced.objectSubId = 0; |
| 818 | recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
| 819 | } |
| 820 | |
| 821 | return myself; |
| 822 | } |
| 823 | |
| 824 | /* |
| 825 | * lookup_agg_function |
| 826 | * common code for finding aggregate support functions |
| 827 | * |
| 828 | * fnName: possibly-schema-qualified function name |
| 829 | * nargs, input_types: expected function argument types |
| 830 | * variadicArgType: type of variadic argument if any, else InvalidOid |
| 831 | * |
| 832 | * Returns OID of function, and stores its return type into *rettype |
| 833 | * |
| 834 | * NB: must not scribble on input_types[], as we may re-use those |
| 835 | */ |
| 836 | static Oid |
| 837 | lookup_agg_function(List *fnName, |
| 838 | int nargs, |
| 839 | Oid *input_types, |
| 840 | Oid variadicArgType, |
| 841 | Oid *rettype) |
| 842 | { |
| 843 | Oid fnOid; |
| 844 | bool retset; |
| 845 | int nvargs; |
| 846 | Oid vatype; |
| 847 | Oid *true_oid_array; |
| 848 | FuncDetailCode fdresult; |
| 849 | AclResult aclresult; |
| 850 | int i; |
| 851 | |
| 852 | /* |
| 853 | * func_get_detail looks up the function in the catalogs, does |
| 854 | * disambiguation for polymorphic functions, handles inheritance, and |
| 855 | * returns the funcid and type and set or singleton status of the |
| 856 | * function's return value. it also returns the true argument types to |
| 857 | * the function. |
| 858 | */ |
| 859 | fdresult = func_get_detail(fnName, NIL, NIL, |
| 860 | nargs, input_types, false, false, |
| 861 | &fnOid, rettype, &retset, |
| 862 | &nvargs, &vatype, |
| 863 | &true_oid_array, NULL); |
| 864 | |
| 865 | /* only valid case is a normal function not returning a set */ |
| 866 | if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) |
| 867 | ereport(ERROR, |
| 868 | (errcode(ERRCODE_UNDEFINED_FUNCTION), |
| 869 | errmsg("function %s does not exist" , |
| 870 | func_signature_string(fnName, nargs, |
| 871 | NIL, input_types)))); |
| 872 | if (retset) |
| 873 | ereport(ERROR, |
| 874 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 875 | errmsg("function %s returns a set" , |
| 876 | func_signature_string(fnName, nargs, |
| 877 | NIL, input_types)))); |
| 878 | |
| 879 | /* |
| 880 | * If the agg is declared to take VARIADIC ANY, the underlying functions |
| 881 | * had better be declared that way too, else they may receive too many |
| 882 | * parameters; but func_get_detail would have been happy with plain ANY. |
| 883 | * (Probably nothing very bad would happen, but it wouldn't work as the |
| 884 | * user expects.) Other combinations should work without any special |
| 885 | * pushups, given that we told func_get_detail not to expand VARIADIC. |
| 886 | */ |
| 887 | if (variadicArgType == ANYOID && vatype != ANYOID) |
| 888 | ereport(ERROR, |
| 889 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 890 | errmsg("function %s must accept VARIADIC ANY to be used in this aggregate" , |
| 891 | func_signature_string(fnName, nargs, |
| 892 | NIL, input_types)))); |
| 893 | |
| 894 | /* |
| 895 | * If there are any polymorphic types involved, enforce consistency, and |
| 896 | * possibly refine the result type. It's OK if the result is still |
| 897 | * polymorphic at this point, though. |
| 898 | */ |
| 899 | *rettype = enforce_generic_type_consistency(input_types, |
| 900 | true_oid_array, |
| 901 | nargs, |
| 902 | *rettype, |
| 903 | true); |
| 904 | |
| 905 | /* |
| 906 | * func_get_detail will find functions requiring run-time argument type |
| 907 | * coercion, but nodeAgg.c isn't prepared to deal with that |
| 908 | */ |
| 909 | for (i = 0; i < nargs; i++) |
| 910 | { |
| 911 | if (!IsBinaryCoercible(input_types[i], true_oid_array[i])) |
| 912 | ereport(ERROR, |
| 913 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 914 | errmsg("function %s requires run-time type coercion" , |
| 915 | func_signature_string(fnName, nargs, |
| 916 | NIL, true_oid_array)))); |
| 917 | } |
| 918 | |
| 919 | /* Check aggregate creator has permission to call the function */ |
| 920 | aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE); |
| 921 | if (aclresult != ACLCHECK_OK) |
| 922 | aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(fnOid)); |
| 923 | |
| 924 | return fnOid; |
| 925 | } |
| 926 | |