Commit 77ec5aff authored by Tom Lane's avatar Tom Lane

Adjust handling of an ANYARRAY actual input for an ANYARRAY argument.

Ordinarily it's impossible for an actual input of a function to have
declared type ANYARRAY, since we'd resolve that to a concrete array
type before doing argument type resolution for the function.  But an
exception arises for functions applied to certain columns of pg_statistic
or pg_stats, since we abuse the "anyarray" pseudotype by using it to
declare those columns.  So parse_coerce.c has to deal with the case.

Previously we allowed an ANYARRAY actual input to match an ANYARRAY
polymorphic argument, but only if no other argument or result was
declared ANYELEMENT.  When that logic was written, those were the only
two polymorphic types, and I fear nobody thought carefully about how it
ought to extend to the other ones that came along later.  But actually
it was wrong even then, because if a function has two ANYARRAY
arguments, it should be able to expect that they have identical element
types, and we'd not be able to ensure that.

The correct generalization is that we can match an ANYARRAY actual input
to an ANYARRAY polymorphic argument only if no other argument or result
is of any polymorphic type, so that no promises are being made about
element type compatibility.  check_generic_type_consistency can't test
that condition, but it seems better anyway to accept such matches there
and then throw an error if needed in enforce_generic_type_consistency.
That way we can produce a specific error message rather than an
unintuitive "function does not exist" complaint.  (There's some risk
perhaps of getting new ambiguous-function complaints, but I think that
any set of functions for which that could happen would be ambiguous
against ordinary array columns as well.)  While we're at it, we can
improve the message that's produced in cases that the code did already
object to, as shown in the regression test changes.

Also, remove a similar test that got cargo-culted in for ANYRANGE;
there are no catalog columns of type ANYRANGE, and I hope we never
create any, so that's not needed.  (It was incomplete anyway.)

While here, update some comments and rearrange the code a bit in
preparation for upcoming additions of more polymorphic types.

In practical situations I believe this is just exchanging one error
message for another, hopefully better, one.  So it doesn't seem
needful to back-patch, even though the mistake is ancient.

Discussion: https://postgr.es/m/21569.1584314271@sss.pgh.pa.us
parent 5d0c2d5e
...@@ -1432,16 +1432,13 @@ coerce_to_common_type(ParseState *pstate, Node *node, ...@@ -1432,16 +1432,13 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* which must be a varlena array type. * which must be a varlena array type.
* 3) All arguments declared ANYRANGE must have the same datatype, * 3) All arguments declared ANYRANGE must have the same datatype,
* which must be a range type. * which must be a range type.
* 4) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the * 4) If there are arguments of more than one of these polymorphic types,
* actual ANYELEMENT datatype is in fact the element type for the actual * the array element type and/or range subtype must be the same as each
* ANYARRAY datatype. * other and the same as the ANYELEMENT type.
* 5) Similarly, if there are arguments of both ANYELEMENT and ANYRANGE, * 5) ANYENUM is treated the same as ANYELEMENT except that if it is used
* 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
* (alone or in combination with plain ANYELEMENT), we add the extra * (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum. * 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. * 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 * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.) * is an extra restriction if not.)
...@@ -1460,12 +1457,6 @@ coerce_to_common_type(ParseState *pstate, Node *node, ...@@ -1460,12 +1457,6 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* If we have UNKNOWN input (ie, an untyped literal) for any polymorphic * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic
* argument, assume it is okay. * 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. * We do not ereport here, but just return false if a rule is violated.
*/ */
bool bool
...@@ -1473,13 +1464,9 @@ check_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1473,13 +1464,9 @@ check_generic_type_consistency(const Oid *actual_arg_types,
const Oid *declared_arg_types, const Oid *declared_arg_types,
int nargs) int nargs)
{ {
int j;
Oid elem_typeid = InvalidOid; Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid; Oid array_typeid = InvalidOid;
Oid array_typelem;
Oid range_typeid = InvalidOid; Oid range_typeid = InvalidOid;
Oid range_typelem;
bool have_anyelement = false;
bool have_anynonarray = false; bool have_anynonarray = false;
bool have_anyenum = false; bool have_anyenum = false;
...@@ -1487,7 +1474,7 @@ check_generic_type_consistency(const Oid *actual_arg_types, ...@@ -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. * Loop through the arguments to see if we have any that are polymorphic.
* If so, require the actual types to be consistent. * 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 decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j]; Oid actual_type = actual_arg_types[j];
...@@ -1496,7 +1483,6 @@ check_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1496,7 +1483,6 @@ check_generic_type_consistency(const Oid *actual_arg_types,
decl_type == ANYNONARRAYOID || decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID) decl_type == ANYENUMOID)
{ {
have_anyelement = true;
if (decl_type == ANYNONARRAYOID) if (decl_type == ANYNONARRAYOID)
have_anynonarray = true; have_anynonarray = true;
else if (decl_type == ANYENUMOID) else if (decl_type == ANYENUMOID)
...@@ -1531,34 +1517,48 @@ check_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1531,34 +1517,48 @@ check_generic_type_consistency(const Oid *actual_arg_types,
if (OidIsValid(array_typeid)) if (OidIsValid(array_typeid))
{ {
if (array_typeid == ANYARRAYOID) 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 */ Oid array_typelem;
return false;
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 */ /* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid)) if (OidIsValid(range_typeid))
{ {
Oid range_typelem;
range_typelem = get_range_subtype(range_typeid); range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem)) if (!OidIsValid(range_typelem))
return false; /* should be a range, but isn't */ return false; /* should be a range, but isn't */
...@@ -1613,57 +1613,46 @@ check_generic_type_consistency(const Oid *actual_arg_types, ...@@ -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) * Rules are applied to the function's return type (possibly altering it)
* if it is declared as a polymorphic type: * 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. * argument's actual type as the function's return type.
* 2) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE, * 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* 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
* argument's actual type as the function's return type. * argument's actual type as the function's return type.
* 6) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument * 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE,
* is ANYARRAY or ANYRANGE, use the actual type of the argument to determine * use the argument's actual type as the function's return type.
* the function's return type, i.e. the array type's corresponding element * 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, there should be
* type or the range type's corresponding subtype (or both, in which case * at least one ANYELEMENT, ANYARRAY, or ANYRANGE input; deduce the
* they must match). * return type from those inputs, or throw error if we can't.
* 7) If return type is ANYELEMENT, no argument is ANYELEMENT, ANYARRAY, or * 5) Otherwise, if return type is ANYRANGE, throw error. (There should
* ANYRANGE, generate an error. (This condition is prevented by CREATE * be at least one ANYRANGE input, since CREATE FUNCTION enforces that.)
* FUNCTION and therefore is not expected here.) * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used
* 8) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra * (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum. * 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. * 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 * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.) * is an extra restriction if not.)
* *
* Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments, * Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
* respectively, and are immediately flattened to their base type. (In * respectively, and are immediately flattened to their base type. (In
* particular, if the return type is also ANYARRAY or ANYRANGE, we'll set it * particular, if the return type is also ANYARRAY or ANYRANGE, we'll set
* to the base type not the domain type.) * it to the base type not the domain type.)
* *
* When allow_poly is false, we are not expecting any of the actual_arg_types * 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 * 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" * 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 * arg types, and we can return a matching polymorphic type as the result.
* result. (This case is currently used only to check compatibility of an * (This case is currently used only to check compatibility of an aggregate's
* aggregate's declaration with the underlying transfn.) * declaration with the underlying transfn.)
* *
* A special case is that we could see ANYARRAY as an actual_arg_type even * 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 * 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 * columns shown as anyarray in the catalogs). We allow this to match a
* declared ANYARRAY argument, but only if there is no ANYELEMENT argument * declared ANYARRAY argument, but only if there is no other polymorphic
* or result (since we can't determine a specific element type to match to * argument that we would need to match it with, and no need to determine
* ANYELEMENT). Note this means that functions taking ANYARRAY had better * the element type to infer the result type. Note this means that functions
* behave sanely if applied to the pg_statistic columns; they can't just * taking ANYARRAY had better behave sanely if applied to the pg_statistic
* assume that successive inputs are of the same actual element type. * columns; they can't just assume that successive inputs are of the same
* actual element type.
*/ */
Oid Oid
enforce_generic_type_consistency(const Oid *actual_arg_types, enforce_generic_type_consistency(const Oid *actual_arg_types,
...@@ -1672,17 +1661,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1672,17 +1661,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid rettype, Oid rettype,
bool allow_poly) bool allow_poly)
{ {
int j; bool have_poly_unknowns = false;
bool have_generics = false;
bool have_unknowns = false;
Oid elem_typeid = InvalidOid; Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid; Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid; Oid range_typeid = InvalidOid;
Oid array_typelem; int n_poly_args = 0;
Oid range_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID); bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID); bool have_anyenum = (rettype == ANYENUMOID);
...@@ -1690,7 +1673,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -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. * Loop through the arguments to see if we have any that are polymorphic.
* If so, require the actual types to be consistent. * 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 decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j]; Oid actual_type = actual_arg_types[j];
...@@ -1699,14 +1682,14 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1699,14 +1682,14 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
decl_type == ANYNONARRAYOID || decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID) decl_type == ANYENUMOID)
{ {
have_generics = have_anyelement = true; n_poly_args++;
if (decl_type == ANYNONARRAYOID) if (decl_type == ANYNONARRAYOID)
have_anynonarray = true; have_anynonarray = true;
else if (decl_type == ANYENUMOID) else if (decl_type == ANYENUMOID)
have_anyenum = true; have_anyenum = true;
if (actual_type == UNKNOWNOID) if (actual_type == UNKNOWNOID)
{ {
have_unknowns = true; have_poly_unknowns = true;
continue; continue;
} }
if (allow_poly && decl_type == actual_type) if (allow_poly && decl_type == actual_type)
...@@ -1722,10 +1705,10 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1722,10 +1705,10 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
} }
else if (decl_type == ANYARRAYOID) else if (decl_type == ANYARRAYOID)
{ {
have_generics = true; n_poly_args++;
if (actual_type == UNKNOWNOID) if (actual_type == UNKNOWNOID)
{ {
have_unknowns = true; have_poly_unknowns = true;
continue; continue;
} }
if (allow_poly && decl_type == actual_type) if (allow_poly && decl_type == actual_type)
...@@ -1742,10 +1725,10 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1742,10 +1725,10 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
} }
else if (decl_type == ANYRANGEOID) else if (decl_type == ANYRANGEOID)
{ {
have_generics = true; n_poly_args++;
if (actual_type == UNKNOWNOID) if (actual_type == UNKNOWNOID)
{ {
have_unknowns = true; have_poly_unknowns = true;
continue; continue;
} }
if (allow_poly && decl_type == actual_type) if (allow_poly && decl_type == actual_type)
...@@ -1766,57 +1749,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1766,57 +1749,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
* Fast Track: if none of the arguments are polymorphic, return the * Fast Track: if none of the arguments are polymorphic, return the
* unmodified rettype. We assume it can't be polymorphic either. * unmodified rettype. We assume it can't be polymorphic either.
*/ */
if (!have_generics) if (n_poly_args == 0)
{
Assert(!IsPolymorphicType(rettype));
return rettype; return rettype;
}
/* Get the element type based on the array type, if we have one */ if (n_poly_args)
if (OidIsValid(array_typeid))
{ {
if (array_typeid == ANYARRAYOID && !have_anyelement) /* Get the element type based on the array type, if we have one */
{ if (OidIsValid(array_typeid))
/* Special case for ANYARRAY input: okay iff no ANYELEMENT */
array_typelem = ANYELEMENTOID;
}
else
{ {
array_typelem = get_element_type(array_typeid); Oid array_typelem;
if (!OidIsValid(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, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not an array but type %s", errmsg("argument declared %s is not consistent with argument declared %s",
"anyarray", format_type_be(array_typeid)))); "anyarray", "anyelement"),
errdetail("%s versus %s",
format_type_be(array_typeid),
format_type_be(elem_typeid))));
}
} }
if (!OidIsValid(elem_typeid)) /* Get the element type based on the range type, if we have one */
{ if (OidIsValid(range_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 */ Oid range_typelem;
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))));
}
}
/* 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); range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem)) if (!OidIsValid(range_typelem))
ereport(ERROR, ereport(ERROR,
...@@ -1824,72 +1821,80 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -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", errmsg("argument declared %s is not a range type but type %s",
"anyrange", "anyrange",
format_type_be(range_typeid)))); 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 (!OidIsValid(elem_typeid))
{ {
/* if (allow_poly)
* if we don't have an element type yet, use the one we just got {
*/ elem_typeid = ANYELEMENTOID;
elem_typeid = range_typelem; array_typeid = ANYARRAYOID;
} range_typeid = ANYRANGEOID;
else if (range_typelem != elem_typeid) }
{ else
/* otherwise, they better match */ {
ereport(ERROR, /*
(errcode(ERRCODE_DATATYPE_MISMATCH), * Only way to get here is if all the polymorphic args have
errmsg("argument declared %s is not consistent with argument declared %s", * UNKNOWN inputs
"anyrange", "anyelement"), */
errdetail("%s versus %s", ereport(ERROR,
format_type_be(range_typeid), (errcode(ERRCODE_DATATYPE_MISMATCH),
format_type_be(elem_typeid)))); errmsg("could not determine polymorphic type because input has type %s",
"unknown")));
}
} }
}
if (!OidIsValid(elem_typeid)) if (have_anynonarray && elem_typeid != ANYELEMENTOID)
{
if (allow_poly)
{ {
elem_typeid = ANYELEMENTOID; /*
array_typeid = ANYARRAYOID; * require the element type to not be an array or domain over
range_typeid = ANYRANGEOID; * 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 */ /* require the element type to be an enum */
ereport(ERROR, if (!type_is_enum(elem_typeid))
(errcode(ERRCODE_DATATYPE_MISMATCH), ereport(ERROR,
errmsg("could not determine polymorphic type because input has type %s", (errcode(ERRCODE_DATATYPE_MISMATCH),
"unknown"))); 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 decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j]; Oid actual_type = actual_arg_types[j];
...@@ -1928,6 +1933,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -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 we return ANYARRAY use the appropriate argument type */
if (rettype == ANYARRAYOID) if (rettype == ANYARRAYOID)
{ {
...@@ -1956,12 +1967,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, ...@@ -1956,12 +1967,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid; 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 */ /* we don't return a generic type; send back the original return type */
return rettype; return rettype;
} }
......
...@@ -1789,7 +1789,7 @@ select f1(array[2,4]) as int, f1(array[4.5, 7.7]) as num; ...@@ -1789,7 +1789,7 @@ select f1(array[2,4]) as int, f1(array[4.5, 7.7]) as num;
(1 row) (1 row)
select f1(stavalues1) from pg_statistic; -- fail, can't infer element type select f1(stavalues1) from pg_statistic; -- fail, can't infer element type
ERROR: argument declared anyarray is not an array but type anyarray ERROR: cannot determine element type of "anyarray" argument
drop function f1(x anyarray); drop function f1(x anyarray);
create function f1(x anyarray) returns anyarray as $$ create function f1(x anyarray) returns anyarray as $$
begin begin
......
...@@ -41,7 +41,7 @@ select polyf(array[2,4]) as int, polyf(array[4.5, 7.7]) as num; ...@@ -41,7 +41,7 @@ select polyf(array[2,4]) as int, polyf(array[4.5, 7.7]) as num;
(1 row) (1 row)
select polyf(stavalues1) from pg_statistic; -- fail, can't infer element type select polyf(stavalues1) from pg_statistic; -- fail, can't infer element type
ERROR: argument declared anyarray is not an array but type anyarray ERROR: cannot determine element type of "anyarray" argument
drop function polyf(x anyarray); drop function polyf(x anyarray);
create function polyf(x anyarray) returns anyarray as $$ create function polyf(x anyarray) returns anyarray as $$
select x select x
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment