diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/parser/parse_coerce.c | 373 |
1 files changed, 189 insertions, 184 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 4a2b463d1b7..f1afd8fca32 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -1432,16 +1432,13 @@ coerce_to_common_type(ParseState *pstate, Node *node, * which must be a varlena array type. * 3) All arguments declared ANYRANGE must have the same datatype, * which must be a range type. - * 4) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the - * actual ANYELEMENT datatype is in fact the element type for the actual - * ANYARRAY datatype. - * 5) Similarly, if there are arguments of both ANYELEMENT and ANYRANGE, - * make sure the actual ANYELEMENT datatype is in fact the subtype for - * the actual ANYRANGE type. - * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used + * 4) If there are arguments of more than one of these polymorphic types, + * the array element type and/or range subtype must be the same as each + * other and the same as the ANYELEMENT type. + * 5) ANYENUM is treated the same as ANYELEMENT except that if it is used * (alone or in combination with plain ANYELEMENT), we add the extra * condition that the ANYELEMENT type must be an enum. - * 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used, + * 6) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used, * we add the extra condition that the ANYELEMENT type must not be an array. * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but * is an extra restriction if not.) @@ -1460,12 +1457,6 @@ coerce_to_common_type(ParseState *pstate, Node *node, * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic * argument, assume it is okay. * - * If an input is of type ANYARRAY (ie, we know it's an array, but not - * what element type), we will accept it as a match to an argument declared - * ANYARRAY, so long as we don't have to determine an element type --- - * that is, so long as there is no use of ANYELEMENT. This is mostly for - * backwards compatibility with the pre-7.4 behavior of ANYARRAY. - * * We do not ereport here, but just return false if a rule is violated. */ bool @@ -1473,13 +1464,9 @@ check_generic_type_consistency(const Oid *actual_arg_types, const Oid *declared_arg_types, int nargs) { - int j; Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; - Oid array_typelem; Oid range_typeid = InvalidOid; - Oid range_typelem; - bool have_anyelement = false; bool have_anynonarray = false; bool have_anyenum = false; @@ -1487,7 +1474,7 @@ check_generic_type_consistency(const Oid *actual_arg_types, * Loop through the arguments to see if we have any that are polymorphic. * If so, require the actual types to be consistent. */ - for (j = 0; j < nargs; j++) + for (int j = 0; j < nargs; j++) { Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; @@ -1496,7 +1483,6 @@ check_generic_type_consistency(const Oid *actual_arg_types, decl_type == ANYNONARRAYOID || decl_type == ANYENUMOID) { - have_anyelement = true; if (decl_type == ANYNONARRAYOID) have_anynonarray = true; else if (decl_type == ANYENUMOID) @@ -1532,33 +1518,47 @@ check_generic_type_consistency(const Oid *actual_arg_types, { if (array_typeid == ANYARRAYOID) { - /* Special case for ANYARRAY input: okay iff no ANYELEMENT */ - if (have_anyelement) - return false; - return true; - } - - array_typelem = get_element_type(array_typeid); - if (!OidIsValid(array_typelem)) - return false; /* should be an array, but isn't */ - - if (!OidIsValid(elem_typeid)) - { /* - * if we don't have an element type yet, use the one we just got + * Special case for matching ANYARRAY input to an ANYARRAY + * argument: allow it for now. enforce_generic_type_consistency() + * might complain later, depending on the presence of other + * polymorphic arguments or results, but it will deliver a less + * surprising error message than "function does not exist". + * + * (If you think to change this, note that can_coerce_type will + * consider such a situation as a match, so that we might not even + * get here.) */ - elem_typeid = array_typelem; } - else if (array_typelem != elem_typeid) + else { - /* otherwise, they better match */ - return false; + Oid array_typelem; + + array_typelem = get_element_type(array_typeid); + if (!OidIsValid(array_typelem)) + return false; /* should be an array, but isn't */ + + if (!OidIsValid(elem_typeid)) + { + /* + * if we don't have an element type yet, use the one we just + * got + */ + elem_typeid = array_typelem; + } + else if (array_typelem != elem_typeid) + { + /* otherwise, they better match */ + return false; + } } } /* Get the element type based on the range type, if we have one */ if (OidIsValid(range_typeid)) { + Oid range_typelem; + range_typelem = get_range_subtype(range_typeid); if (!OidIsValid(range_typelem)) return false; /* should be a range, but isn't */ @@ -1613,57 +1613,46 @@ check_generic_type_consistency(const Oid *actual_arg_types, * Rules are applied to the function's return type (possibly altering it) * if it is declared as a polymorphic type: * - * 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the + * 1) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the * argument's actual type as the function's return type. - * 2) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE, - * use the argument's actual type as the function's return type. - * 3) If return type is ANYARRAY, no argument is ANYARRAY, but any argument is - * ANYELEMENT, use the actual type of the argument to determine the - * function's return type, i.e. the element type's corresponding array - * type. (Note: similar behavior does not exist for ANYRANGE, because it's - * impossible to determine the range type from the subtype alone.) - * 4) If return type is ANYARRAY, but no argument is ANYARRAY or ANYELEMENT, - * generate an error. Similarly, if return type is ANYRANGE, but no - * argument is ANYRANGE, generate an error. (These conditions are - * prevented by CREATE FUNCTION and therefore are not expected here.) - * 5) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the + * 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the * argument's actual type as the function's return type. - * 6) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument - * is ANYARRAY or ANYRANGE, use the actual type of the argument to determine - * the function's return type, i.e. the array type's corresponding element - * type or the range type's corresponding subtype (or both, in which case - * they must match). - * 7) If return type is ANYELEMENT, no argument is ANYELEMENT, ANYARRAY, or - * ANYRANGE, generate an error. (This condition is prevented by CREATE - * FUNCTION and therefore is not expected here.) - * 8) ANYENUM is treated the same as ANYELEMENT except that if it is used + * 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE, + * use the argument's actual type as the function's return type. + * 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, there should be + * at least one ANYELEMENT, ANYARRAY, or ANYRANGE input; deduce the + * return type from those inputs, or throw error if we can't. + * 5) Otherwise, if return type is ANYRANGE, throw error. (There should + * be at least one ANYRANGE input, since CREATE FUNCTION enforces that.) + * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used * (alone or in combination with plain ANYELEMENT), we add the extra * condition that the ANYELEMENT type must be an enum. - * 9) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used, + * 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used, * we add the extra condition that the ANYELEMENT type must not be an array. * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but * is an extra restriction if not.) * * Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments, - * respectively, and are immediately flattened to their base type. (In - * particular, if the return type is also ANYARRAY or ANYRANGE, we'll set it - * to the base type not the domain type.) + * respectively, and are immediately flattened to their base type. (In + * particular, if the return type is also ANYARRAY or ANYRANGE, we'll set + * it to the base type not the domain type.) * * When allow_poly is false, we are not expecting any of the actual_arg_types * to be polymorphic, and we should not return a polymorphic result type * either. When allow_poly is true, it is okay to have polymorphic "actual" - * arg types, and we can return ANYARRAY, ANYRANGE, or ANYELEMENT as the - * result. (This case is currently used only to check compatibility of an - * aggregate's declaration with the underlying transfn.) + * arg types, and we can return a matching polymorphic type as the result. + * (This case is currently used only to check compatibility of an aggregate's + * declaration with the underlying transfn.) * * A special case is that we could see ANYARRAY as an actual_arg_type even * when allow_poly is false (this is possible only because pg_statistic has * columns shown as anyarray in the catalogs). We allow this to match a - * declared ANYARRAY argument, but only if there is no ANYELEMENT argument - * or result (since we can't determine a specific element type to match to - * ANYELEMENT). Note this means that functions taking ANYARRAY had better - * behave sanely if applied to the pg_statistic columns; they can't just - * assume that successive inputs are of the same actual element type. + * declared ANYARRAY argument, but only if there is no other polymorphic + * argument that we would need to match it with, and no need to determine + * the element type to infer the result type. Note this means that functions + * taking ANYARRAY had better behave sanely if applied to the pg_statistic + * columns; they can't just assume that successive inputs are of the same + * actual element type. */ Oid enforce_generic_type_consistency(const Oid *actual_arg_types, @@ -1672,17 +1661,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, Oid rettype, bool allow_poly) { - int j; - bool have_generics = false; - bool have_unknowns = false; + bool have_poly_unknowns = false; Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; Oid range_typeid = InvalidOid; - Oid array_typelem; - Oid range_typelem; - bool have_anyelement = (rettype == ANYELEMENTOID || - rettype == ANYNONARRAYOID || - rettype == ANYENUMOID); + int n_poly_args = 0; bool have_anynonarray = (rettype == ANYNONARRAYOID); bool have_anyenum = (rettype == ANYENUMOID); @@ -1690,7 +1673,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, * Loop through the arguments to see if we have any that are polymorphic. * If so, require the actual types to be consistent. */ - for (j = 0; j < nargs; j++) + for (int j = 0; j < nargs; j++) { Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; @@ -1699,14 +1682,14 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, decl_type == ANYNONARRAYOID || decl_type == ANYENUMOID) { - have_generics = have_anyelement = true; + n_poly_args++; if (decl_type == ANYNONARRAYOID) have_anynonarray = true; else if (decl_type == ANYENUMOID) have_anyenum = true; if (actual_type == UNKNOWNOID) { - have_unknowns = true; + have_poly_unknowns = true; continue; } if (allow_poly && decl_type == actual_type) @@ -1722,10 +1705,10 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, } else if (decl_type == ANYARRAYOID) { - have_generics = true; + n_poly_args++; if (actual_type == UNKNOWNOID) { - have_unknowns = true; + have_poly_unknowns = true; continue; } if (allow_poly && decl_type == actual_type) @@ -1742,10 +1725,10 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, } else if (decl_type == ANYRANGEOID) { - have_generics = true; + n_poly_args++; if (actual_type == UNKNOWNOID) { - have_unknowns = true; + have_poly_unknowns = true; continue; } if (allow_poly && decl_type == actual_type) @@ -1766,57 +1749,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, * Fast Track: if none of the arguments are polymorphic, return the * unmodified rettype. We assume it can't be polymorphic either. */ - if (!have_generics) + if (n_poly_args == 0) + { + Assert(!IsPolymorphicType(rettype)); return rettype; + } - /* Get the element type based on the array type, if we have one */ - if (OidIsValid(array_typeid)) + if (n_poly_args) { - if (array_typeid == ANYARRAYOID && !have_anyelement) - { - /* Special case for ANYARRAY input: okay iff no ANYELEMENT */ - array_typelem = ANYELEMENTOID; - } - else + /* Get the element type based on the array type, if we have one */ + if (OidIsValid(array_typeid)) { - array_typelem = get_element_type(array_typeid); - if (!OidIsValid(array_typelem)) + Oid array_typelem; + + if (array_typeid == ANYARRAYOID) + { + /* + * Special case for matching ANYARRAY input to an ANYARRAY + * argument: allow it iff no other arguments are polymorphic + * (otherwise we couldn't be sure whether the array element + * type matches up) and the result type doesn't require us to + * infer a specific element type. + */ + if (n_poly_args != 1 || + (rettype != ANYARRAYOID && IsPolymorphicType(rettype))) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot determine element type of \"anyarray\" argument"))); + array_typelem = ANYELEMENTOID; + } + else + { + array_typelem = get_element_type(array_typeid); + if (!OidIsValid(array_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not an array but type %s", + "anyarray", format_type_be(array_typeid)))); + } + + if (!OidIsValid(elem_typeid)) + { + /* + * if we don't have an element type yet, use the one we just + * got + */ + elem_typeid = array_typelem; + } + else if (array_typelem != elem_typeid) + { + /* otherwise, they better match */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared %s is not an array but type %s", - "anyarray", format_type_be(array_typeid)))); + errmsg("argument declared %s is not consistent with argument declared %s", + "anyarray", "anyelement"), + errdetail("%s versus %s", + format_type_be(array_typeid), + format_type_be(elem_typeid)))); + } } - if (!OidIsValid(elem_typeid)) - { - /* - * if we don't have an element type yet, use the one we just got - */ - elem_typeid = array_typelem; - } - else if (array_typelem != elem_typeid) + /* Get the element type based on the range type, if we have one */ + if (OidIsValid(range_typeid)) { - /* otherwise, they better match */ - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared %s is not consistent with argument declared %s", - "anyarray", "anyelement"), - errdetail("%s versus %s", - format_type_be(array_typeid), - format_type_be(elem_typeid)))); - } - } + Oid range_typelem; - /* Get the element type based on the range type, if we have one */ - if (OidIsValid(range_typeid)) - { - if (range_typeid == ANYRANGEOID && !have_anyelement) - { - /* Special case for ANYRANGE input: okay iff no ANYELEMENT */ - range_typelem = ANYELEMENTOID; - } - else - { range_typelem = get_range_subtype(range_typeid); if (!OidIsValid(range_typelem)) ereport(ERROR, @@ -1824,72 +1821,80 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, errmsg("argument declared %s is not a range type but type %s", "anyrange", format_type_be(range_typeid)))); + + if (!OidIsValid(elem_typeid)) + { + /* + * if we don't have an element type yet, use the one we just + * got + */ + elem_typeid = range_typelem; + } + else if (range_typelem != elem_typeid) + { + /* otherwise, they better match */ + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not consistent with argument declared %s", + "anyrange", "anyelement"), + errdetail("%s versus %s", + format_type_be(range_typeid), + format_type_be(elem_typeid)))); + } } if (!OidIsValid(elem_typeid)) { - /* - * if we don't have an element type yet, use the one we just got - */ - elem_typeid = range_typelem; - } - else if (range_typelem != elem_typeid) - { - /* otherwise, they better match */ - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared %s is not consistent with argument declared %s", - "anyrange", "anyelement"), - errdetail("%s versus %s", - format_type_be(range_typeid), - format_type_be(elem_typeid)))); + if (allow_poly) + { + elem_typeid = ANYELEMENTOID; + array_typeid = ANYARRAYOID; + range_typeid = ANYRANGEOID; + } + else + { + /* + * Only way to get here is if all the polymorphic args have + * UNKNOWN inputs + */ + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not determine polymorphic type because input has type %s", + "unknown"))); + } } - } - if (!OidIsValid(elem_typeid)) - { - if (allow_poly) + if (have_anynonarray && elem_typeid != ANYELEMENTOID) { - elem_typeid = ANYELEMENTOID; - array_typeid = ANYARRAYOID; - range_typeid = ANYRANGEOID; + /* + * require the element type to not be an array or domain over + * array + */ + if (type_is_array_domain(elem_typeid)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type matched to anynonarray is an array type: %s", + format_type_be(elem_typeid)))); } - else + + if (have_anyenum && elem_typeid != ANYELEMENTOID) { - /* Only way to get here is if all the generic args are UNKNOWN */ - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("could not determine polymorphic type because input has type %s", - "unknown"))); + /* require the element type to be an enum */ + if (!type_is_enum(elem_typeid)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type matched to anyenum is not an enum type: %s", + format_type_be(elem_typeid)))); } } - if (have_anynonarray && elem_typeid != ANYELEMENTOID) - { - /* require the element type to not be an array or domain over array */ - if (type_is_array_domain(elem_typeid)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("type matched to anynonarray is an array type: %s", - format_type_be(elem_typeid)))); - } - - if (have_anyenum && elem_typeid != ANYELEMENTOID) - { - /* require the element type to be an enum */ - if (!type_is_enum(elem_typeid)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("type matched to anyenum is not an enum type: %s", - format_type_be(elem_typeid)))); - } - /* - * If we had any unknown inputs, re-scan to assign correct types + * If we had any UNKNOWN inputs for polymorphic arguments, re-scan to + * assign correct types to them. */ - if (have_unknowns) + if (have_poly_unknowns) { - for (j = 0; j < nargs; j++) + for (int j = 0; j < nargs; j++) { Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; @@ -1928,6 +1933,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, } } + /* if we return ANYELEMENT use the appropriate argument type */ + if (rettype == ANYELEMENTOID || + rettype == ANYNONARRAYOID || + rettype == ANYENUMOID) + return elem_typeid; + /* if we return ANYARRAY use the appropriate argument type */ if (rettype == ANYARRAYOID) { @@ -1956,12 +1967,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, return range_typeid; } - /* if we return ANYELEMENT use the appropriate argument type */ - if (rettype == ANYELEMENTOID || - rettype == ANYNONARRAYOID || - rettype == ANYENUMOID) - return elem_typeid; - /* we don't return a generic type; send back the original return type */ return rettype; } |