diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/pg_aggregate.c | 53 | ||||
-rw-r--r-- | src/backend/catalog/pg_proc.c | 115 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 71 |
3 files changed, 143 insertions, 96 deletions
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 0b7face4cc2..7d887ea24a2 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -93,8 +93,6 @@ AggregateCreate(const char *aggName, Oid mfinalfn = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */ Oid *aggArgTypes = parameterTypes->values; - bool hasPolyArg; - bool hasInternalArg; bool mtransIsStrict = false; Oid rettype; Oid finaltype; @@ -103,6 +101,7 @@ AggregateCreate(const char *aggName, int nargs_finalfn; Oid procOid; TupleDesc tupDesc; + char *detailmsg; int i; ObjectAddress myself, referenced; @@ -131,36 +130,33 @@ AggregateCreate(const char *aggName, FUNC_MAX_ARGS - 1, FUNC_MAX_ARGS - 1))); - /* check for polymorphic and INTERNAL arguments */ - hasPolyArg = false; - hasInternalArg = false; - for (i = 0; i < numArgs; i++) - { - if (IsPolymorphicType(aggArgTypes[i])) - hasPolyArg = true; - else if (aggArgTypes[i] == INTERNALOID) - hasInternalArg = true; - } - /* * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ - if (IsPolymorphicType(aggTransType) && !hasPolyArg) + detailmsg = check_valid_polymorphic_signature(aggTransType, + aggArgTypes, + numArgs); + if (detailmsg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), - errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument."))); + errdetail_internal("%s", detailmsg))); /* * Likewise for moving-aggregate transtype, if any */ - if (OidIsValid(aggmTransType) && - IsPolymorphicType(aggmTransType) && !hasPolyArg) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("cannot determine transition data type"), - errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument."))); + if (OidIsValid(aggmTransType)) + { + detailmsg = check_valid_polymorphic_signature(aggmTransType, + aggArgTypes, + numArgs); + if (detailmsg) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("cannot determine transition data type"), + errdetail_internal("%s", detailmsg))); + } /* * An ordered-set aggregate that is VARIADIC must be VARIADIC ANY. In @@ -492,12 +488,14 @@ AggregateCreate(const char *aggName, * that itself violates the rule against polymorphic result with no * polymorphic input.) */ - if (IsPolymorphicType(finaltype) && !hasPolyArg) + detailmsg = check_valid_polymorphic_signature(finaltype, + aggArgTypes, + numArgs); + if (detailmsg) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), - errdetail("An aggregate returning a polymorphic type " - "must have at least one polymorphic argument."))); + errdetail_internal("%s", detailmsg))); /* * Also, the return type can't be INTERNAL unless there's at least one @@ -505,11 +503,14 @@ AggregateCreate(const char *aggName, * for regular functions, but at the level of aggregates. We must test * this explicitly because we allow INTERNAL as the transtype. */ - if (finaltype == INTERNALOID && !hasInternalArg) + detailmsg = check_valid_internal_signature(finaltype, + aggArgTypes, + numArgs); + if (detailmsg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), - errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); + errdetail_internal("%s", detailmsg))); /* * If a moving-aggregate implementation is supplied, look up its finalfn diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 423fd79d945..0cac936cef8 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -32,6 +32,7 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_coerce.h" #include "parser/parse_type.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" @@ -97,12 +98,6 @@ ProcedureCreate(const char *procedureName, int allParamCount; Oid *allParams; char *paramModes = NULL; - bool genericInParam = false; - bool genericOutParam = false; - bool anyrangeInParam = false; - bool anyrangeOutParam = false; - bool internalInParam = false; - bool internalOutParam = false; Oid variadicType = InvalidOid; Acl *proacl = NULL; Relation rel; @@ -116,6 +111,7 @@ ProcedureCreate(const char *procedureName, bool is_update; ObjectAddress myself, referenced; + char *detailmsg; int i; Oid trfid; @@ -178,29 +174,34 @@ ProcedureCreate(const char *procedureName, } /* - * Detect whether we have polymorphic or INTERNAL arguments. The first - * loop checks input arguments, the second output arguments. + * Do not allow polymorphic return type unless there is a polymorphic + * input argument that we can use to deduce the actual return type. */ - for (i = 0; i < parameterCount; i++) - { - switch (parameterTypes->values[i]) - { - case ANYARRAYOID: - case ANYELEMENTOID: - case ANYNONARRAYOID: - case ANYENUMOID: - genericInParam = true; - break; - case ANYRANGEOID: - genericInParam = true; - anyrangeInParam = true; - break; - case INTERNALOID: - internalInParam = true; - break; - } - } + detailmsg = check_valid_polymorphic_signature(returnType, + parameterTypes->values, + parameterCount); + if (detailmsg) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("cannot determine result data type"), + errdetail_internal("%s", detailmsg))); + /* + * Also, do not allow return type INTERNAL unless at least one input + * argument is INTERNAL. + */ + detailmsg = check_valid_internal_signature(returnType, + parameterTypes->values, + parameterCount); + if (detailmsg) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("unsafe use of pseudo-type \"internal\""), + errdetail_internal("%s", detailmsg))); + + /* + * Apply the same tests to any OUT arguments. + */ if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) @@ -210,52 +211,26 @@ ProcedureCreate(const char *procedureName, paramModes[i] == PROARGMODE_VARIADIC) continue; /* ignore input-only params */ - switch (allParams[i]) - { - case ANYARRAYOID: - case ANYELEMENTOID: - case ANYNONARRAYOID: - case ANYENUMOID: - genericOutParam = true; - break; - case ANYRANGEOID: - genericOutParam = true; - anyrangeOutParam = true; - break; - case INTERNALOID: - internalOutParam = true; - break; - } + detailmsg = check_valid_polymorphic_signature(allParams[i], + parameterTypes->values, + parameterCount); + if (detailmsg) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("cannot determine result data type"), + errdetail_internal("%s", detailmsg))); + detailmsg = check_valid_internal_signature(allParams[i], + parameterTypes->values, + parameterCount); + if (detailmsg) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("unsafe use of pseudo-type \"internal\""), + errdetail_internal("%s", detailmsg))); } } - /* - * Do not allow polymorphic return type unless at least one input argument - * is polymorphic. ANYRANGE return type is even stricter: must have an - * ANYRANGE input (since we can't deduce the specific range type from - * ANYELEMENT). Also, do not allow return type INTERNAL unless at least - * one input argument is INTERNAL. - */ - if ((IsPolymorphicType(returnType) || genericOutParam) - && !genericInParam) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("cannot determine result data type"), - errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); - - if ((returnType == ANYRANGEOID || anyrangeOutParam) && - !anyrangeInParam) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("cannot determine result data type"), - errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument."))); - - if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("unsafe use of pseudo-type \"internal\""), - errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); - + /* Identify variadic argument type, if any */ if (paramModes != NULL) { /* diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index f1afd8fca32..c3fb51d35d9 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -1971,6 +1971,77 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, return rettype; } +/* + * check_valid_polymorphic_signature() + * Is a proposed function signature valid per polymorphism rules? + * + * Returns NULL if the signature is valid (either ret_type is not polymorphic, + * or it can be deduced from the given declared argument types). Otherwise, + * returns a palloc'd, already translated errdetail string saying why not. + */ +char * +check_valid_polymorphic_signature(Oid ret_type, + const Oid *declared_arg_types, + int nargs) +{ + if (ret_type == ANYRANGEOID) + { + /* + * ANYRANGE requires an ANYRANGE input, else we can't tell which of + * several range types with the same element type to use. + */ + for (int i = 0; i < nargs; i++) + { + if (declared_arg_types[i] == ret_type) + return NULL; /* OK */ + } + return psprintf(_("A result of type %s requires at least one input of type %s."), + format_type_be(ret_type), format_type_be(ret_type)); + } + else if (IsPolymorphicType(ret_type)) + { + /* Otherwise, any polymorphic type can be deduced from any other */ + for (int i = 0; i < nargs; i++) + { + if (IsPolymorphicType(declared_arg_types[i])) + return NULL; /* OK */ + } + return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."), + format_type_be(ret_type)); + } + else + return NULL; /* OK, ret_type is not polymorphic */ +} + +/* + * check_valid_internal_signature() + * Is a proposed function signature valid per INTERNAL safety rules? + * + * Returns NULL if OK, or a suitable error message if ret_type is INTERNAL but + * none of the declared arg types are. (It's unsafe to create such a function + * since it would allow invocation of INTERNAL-consuming functions directly + * from SQL.) It's overkill to return the error detail message, since there + * is only one possibility, but we do it like this to keep the API similar to + * check_valid_polymorphic_signature(). + */ +char * +check_valid_internal_signature(Oid ret_type, + const Oid *declared_arg_types, + int nargs) +{ + if (ret_type == INTERNALOID) + { + for (int i = 0; i < nargs; i++) + { + if (declared_arg_types[i] == ret_type) + return NULL; /* OK */ + } + return pstrdup(_("A result of type internal requires at least one input of type internal.")); + } + else + return NULL; /* OK, ret_type is not INTERNAL */ +} + /* TypeCategory() * Assign a category to the specified type OID. |