aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/index.c8
-rw-r--r--src/backend/catalog/pg_proc.c3
-rw-r--r--src/backend/commands/functioncmds.c1
-rw-r--r--src/backend/parser/parse_coerce.c527
-rw-r--r--src/backend/utils/adt/json.c2
-rw-r--r--src/backend/utils/adt/jsonb.c2
-rw-r--r--src/backend/utils/adt/pseudotypes.c35
-rw-r--r--src/backend/utils/fmgr/funcapi.c196
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.dat45
-rw-r--r--src/include/catalog/pg_type.dat30
-rw-r--r--src/include/catalog/pg_type.h11
-rw-r--r--src/pl/plpgsql/src/pl_comp.c12
-rw-r--r--src/test/regress/expected/aggregates.out24
-rw-r--r--src/test/regress/expected/create_aggregate.out28
-rw-r--r--src/test/regress/expected/opr_sanity.out52
-rw-r--r--src/test/regress/expected/plpgsql.out101
-rw-r--r--src/test/regress/expected/polymorphism.out311
-rw-r--r--src/test/regress/expected/rangefuncs.out53
-rw-r--r--src/test/regress/expected/rangetypes.out26
-rw-r--r--src/test/regress/expected/type_sanity.out10
-rw-r--r--src/test/regress/sql/aggregates.sql4
-rw-r--r--src/test/regress/sql/create_aggregate.sql34
-rw-r--r--src/test/regress/sql/opr_sanity.sql33
-rw-r--r--src/test/regress/sql/plpgsql.sql68
-rw-r--r--src/test/regress/sql/polymorphism.sql159
-rw-r--r--src/test/regress/sql/rangefuncs.sql21
-rw-r--r--src/test/regress/sql/rangetypes.sql16
-rw-r--r--src/test/regress/sql/type_sanity.sql10
29 files changed, 1742 insertions, 82 deletions
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 023ec7e6188..2d81bc3cbc9 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -404,10 +404,6 @@ ConstructTupleDescriptor(Relation heapRelation,
*/
keyType = amroutine->amkeytype;
- /*
- * Code below is concerned to the opclasses which are not used with
- * the included columns.
- */
if (i < indexInfo->ii_NumIndexKeyAttrs)
{
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(classObjectId[i]));
@@ -422,6 +418,10 @@ ConstructTupleDescriptor(Relation heapRelation,
* If keytype is specified as ANYELEMENT, and opcintype is
* ANYARRAY, then the attribute type must be an array (else it'd
* not have matched this opclass); use its element type.
+ *
+ * We could also allow ANYCOMPATIBLE/ANYCOMPATIBLEARRAY here, but
+ * there seems no need to do so; there's no reason to declare an
+ * opclass as taking ANYCOMPATIBLEARRAY rather than ANYARRAY.
*/
if (keyType == ANYELEMENTOID && opclassTup->opcintype == ANYARRAYOID)
{
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 0cac936cef8..6cdda35d1c9 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -262,6 +262,9 @@ ProcedureCreate(const char *procedureName,
case ANYARRAYOID:
variadicType = ANYELEMENTOID;
break;
+ case ANYCOMPATIBLEARRAYOID:
+ variadicType = ANYCOMPATIBLEOID;
+ break;
default:
variadicType = get_element_type(allParams[i]);
if (!OidIsValid(variadicType))
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 5eac55aaca1..694114adedc 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -320,6 +320,7 @@ interpret_function_parameter_list(ParseState *pstate,
switch (toid)
{
case ANYARRAYOID:
+ case ANYCOMPATIBLEARRAYOID:
case ANYOID:
/* okay */
break;
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index c3fb51d35d9..645e4aa4ceb 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -167,15 +167,17 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYOID ||
targetTypeId == ANYELEMENTOID ||
- targetTypeId == ANYNONARRAYOID)
+ targetTypeId == ANYNONARRAYOID ||
+ targetTypeId == ANYCOMPATIBLEOID ||
+ targetTypeId == ANYCOMPATIBLENONARRAYOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* Note: by returning the unmodified node here, we are saying that
* it's OK to treat an UNKNOWN constant as a valid input for a
- * function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be
- * all right, since an UNKNOWN value is still a perfectly valid Datum.
+ * function accepting one of these pseudotypes. This should be all
+ * right, since an UNKNOWN value is still a perfectly valid Datum.
*
* NB: we do NOT want a RelabelType here: the exposed type of the
* function argument must be its actual type, not the polymorphic
@@ -185,7 +187,9 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYCOMPATIBLEARRAYOID ||
+ targetTypeId == ANYCOMPATIBLERANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
@@ -193,10 +197,10 @@ coerce_type(ParseState *pstate, Node *node,
* These cases are unlike the ones above because the exposed type of
* the argument must be an actual array, enum, or range type. In
* particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * is, we just fall through; below, we'll call the pseudotype's input
+ * function, which will produce an error. Also, if what we have is a
+ * domain over array, enum, or range, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1387,6 +1391,103 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * select_common_type_from_oids()
+ * Determine the common supertype of an array of type OIDs.
+ *
+ * This is the same logic as select_common_type(), but working from
+ * an array of type OIDs not a list of expressions. As in that function,
+ * earlier entries in the array have some preference over later ones.
+ * On failure, return InvalidOid if noerror is true, else throw an error.
+ *
+ * Note: neither caller will pass any UNKNOWNOID entries, so the tests
+ * for that in this function are dead code. However, they don't cost much,
+ * and it seems better to keep this logic as close to select_common_type()
+ * as possible.
+ */
+static Oid
+select_common_type_from_oids(int nargs, const Oid *typeids, bool noerror)
+{
+ Oid ptype;
+ TYPCATEGORY pcategory;
+ bool pispreferred;
+ int i = 1;
+
+ Assert(nargs > 0);
+ ptype = typeids[0];
+
+ /* If all input types are valid and exactly the same, pick that type. */
+ if (ptype != UNKNOWNOID)
+ {
+ for (; i < nargs; i++)
+ {
+ if (typeids[i] != ptype)
+ break;
+ }
+ if (i == nargs)
+ return ptype;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm. Note that at this point, we
+ * can skip array entries before "i"; they are all equal to ptype.
+ */
+ ptype = getBaseType(ptype);
+ get_type_category_preferred(ptype, &pcategory, &pispreferred);
+
+ for (; i < nargs; i++)
+ {
+ Oid ntype = getBaseType(typeids[i]);
+
+ /* move on to next one if no new information... */
+ if (ntype != UNKNOWNOID && ntype != ptype)
+ {
+ TYPCATEGORY ncategory;
+ bool nispreferred;
+
+ get_type_category_preferred(ntype, &ncategory, &nispreferred);
+ if (ptype == UNKNOWNOID)
+ {
+ /* so far, only unknowns so take anything... */
+ ptype = ntype;
+ pcategory = ncategory;
+ pispreferred = nispreferred;
+ }
+ else if (ncategory != pcategory)
+ {
+ /*
+ * both types in different categories? then not much hope...
+ */
+ if (noerror)
+ return InvalidOid;
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument types %s and %s cannot be matched",
+ format_type_be(ptype),
+ format_type_be(ntype))));
+ }
+ else if (!pispreferred &&
+ can_coerce_type(1, &ptype, &ntype, COERCION_IMPLICIT) &&
+ !can_coerce_type(1, &ntype, &ptype, COERCION_IMPLICIT))
+ {
+ /*
+ * take new type if can coerce to it implicitly but not the
+ * other way; but if we have a preferred type, stay on it.
+ */
+ ptype = ntype;
+ pcategory = ncategory;
+ pispreferred = nispreferred;
+ }
+ }
+ }
+
+ /* Like select_common_type(), choose TEXT if all inputs were UNKNOWN */
+ if (ptype == UNKNOWNOID)
+ ptype = TEXTOID;
+
+ return ptype;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
@@ -1442,14 +1543,28 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 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.)
+ * 7) All arguments declared ANYCOMPATIBLE must be implicitly castable
+ * to a common supertype (chosen as per select_common_type's rules).
+ * ANYCOMPATIBLENONARRAY works like ANYCOMPATIBLE but also requires the
+ * common supertype to not be an array. If there are ANYCOMPATIBLEARRAY
+ * or ANYCOMPATIBLERANGE arguments, their element types or subtypes are
+ * included while making the choice of common supertype.
+ * 8) The resolved type of ANYCOMPATIBLEARRAY arguments will be the array
+ * type over the common supertype (which might not be the same array type
+ * as any of the original arrays).
+ * 9) All ANYCOMPATIBLERANGE arguments must be the exact same range type
+ * (after domain flattening), since we have no preference rule that would
+ * let us choose one over another. Furthermore, that range's subtype
+ * must exactly match the common supertype chosen by rule 7.
*
* Domains over arrays match ANYARRAY, and are immediately flattened to their
* base type. (Thus, for example, we will consider it a match if one ANYARRAY
* argument is a domain over int4[] while another one is just int4[].) Also
- * notice that such a domain does *not* match ANYNONARRAY.
+ * notice that such a domain does *not* match ANYNONARRAY. The same goes
+ * for ANYCOMPATIBLEARRAY and ANYCOMPATIBLENONARRAY.
*
- * Similarly, domains over ranges match ANYRANGE, and are immediately
- * flattened to their base type.
+ * Similarly, domains over ranges match ANYRANGE or ANYCOMPATIBLERANGE,
+ * and are immediately flattened to their base type.
*
* Note that domains aren't currently considered to match ANYENUM,
* even if their base type would match.
@@ -1467,13 +1582,19 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid anycompatible_range_typeid = InvalidOid;
+ Oid anycompatible_range_typelem = InvalidOid;
bool have_anynonarray = false;
bool have_anyenum = false;
+ bool have_anycompatible_nonarray = false;
+ int n_anycompatible_args = 0;
+ Oid anycompatible_actual_types[FUNC_MAX_ARGS];
/*
* Loop through the arguments to see if we have any that are polymorphic.
* If so, require the actual types to be consistent.
*/
+ Assert(nargs <= FUNC_MAX_ARGS);
for (int j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
@@ -1511,6 +1632,50 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYCOMPATIBLEOID ||
+ decl_type == ANYCOMPATIBLENONARRAYOID)
+ {
+ if (decl_type == ANYCOMPATIBLENONARRAYOID)
+ have_anycompatible_nonarray = true;
+ if (actual_type == UNKNOWNOID)
+ continue;
+ /* collect the actual types of non-unknown COMPATIBLE args */
+ anycompatible_actual_types[n_anycompatible_args++] = actual_type;
+ }
+ else if (decl_type == ANYCOMPATIBLEARRAYOID)
+ {
+ Oid elem_type;
+
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ elem_type = get_element_type(actual_type);
+ if (!OidIsValid(elem_type))
+ return false; /* not an array */
+ /* collect the element type for common-supertype choice */
+ anycompatible_actual_types[n_anycompatible_args++] = elem_type;
+ }
+ else if (decl_type == ANYCOMPATIBLERANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(anycompatible_range_typeid))
+ {
+ /* All ANYCOMPATIBLERANGE arguments must be the same type */
+ if (anycompatible_range_typeid != actual_type)
+ return false;
+ }
+ else
+ {
+ anycompatible_range_typeid = actual_type;
+ anycompatible_range_typelem = get_range_subtype(actual_type);
+ if (!OidIsValid(anycompatible_range_typelem))
+ return false; /* not a range type */
+ /* collect the subtype for common-supertype choice */
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+ }
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1591,6 +1756,38 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
}
+ /* Check matching of ANYCOMPATIBLE-family arguments, if any */
+ if (n_anycompatible_args > 0)
+ {
+ Oid anycompatible_typeid;
+
+ anycompatible_typeid =
+ select_common_type_from_oids(n_anycompatible_args,
+ anycompatible_actual_types,
+ true);
+
+ if (!OidIsValid(anycompatible_typeid))
+ return false; /* there's no common supertype */
+
+ if (have_anycompatible_nonarray)
+ {
+ /*
+ * require the anycompatible type to not be an array or domain
+ * over array
+ */
+ if (type_is_array_domain(anycompatible_typeid))
+ return false;
+ }
+
+ /*
+ * the anycompatible type must exactly match the range element type,
+ * if we were able to identify one
+ */
+ if (OidIsValid(anycompatible_range_typelem) &&
+ anycompatible_range_typelem != anycompatible_typeid)
+ return false;
+ }
+
/* Looks valid */
return true;
}
@@ -1610,6 +1807,11 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* successful, we alter that position of declared_arg_types[] so that
* make_fn_arguments will coerce the literal to the right thing.
*
+ * If we have polymorphic arguments of the ANYCOMPATIBLE family,
+ * we similarly alter declared_arg_types[] entries to show the resolved
+ * common supertype, so that make_fn_arguments will coerce the actual
+ * arguments to the proper type.
+ *
* Rules are applied to the function's return type (possibly altering it)
* if it is declared as a polymorphic type:
*
@@ -1631,11 +1833,20 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* 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.)
+ * 8) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
+ * ANYCOMPATIBLERANGE are handled by resolving the common supertype
+ * of those arguments (or their element types/subtypes, for array and range
+ * inputs), and then coercing all those arguments to the common supertype,
+ * or the array type over the common supertype for ANYCOMPATIBLEARRAY.
+ * For ANYCOMPATIBLERANGE, there must be at least one non-UNKNOWN input,
+ * all such inputs must be the same range type, and that type's subtype
+ * must equal the common supertype.
*
* 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.)
+ * it to the base type not the domain type.) The same is true for
+ * ANYCOMPATIBLEARRAY and ANYCOMPATIBLERANGE.
*
* 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
@@ -1652,7 +1863,12 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* 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.
+ * actual element type. There is no similar logic for ANYCOMPATIBLEARRAY;
+ * there isn't a need for it since there are no catalog columns of that type,
+ * so we won't see it as input. We could consider matching an actual ANYARRAY
+ * input to an ANYCOMPATIBLEARRAY argument, but at present that seems useless
+ * as well, since there's no value in using ANYCOMPATIBLEARRAY unless there's
+ * at least one other ANYCOMPATIBLE-family argument or result.
*/
Oid
enforce_generic_type_consistency(const Oid *actual_arg_types,
@@ -1661,18 +1877,29 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid rettype,
bool allow_poly)
{
+ bool have_poly_anycompatible = false;
bool have_poly_unknowns = false;
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
- int n_poly_args = 0;
+ Oid anycompatible_typeid = InvalidOid;
+ Oid anycompatible_array_typeid = InvalidOid;
+ Oid anycompatible_range_typeid = InvalidOid;
+ Oid anycompatible_range_typelem = InvalidOid;
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
+ bool have_anycompatible_array = (rettype == ANYCOMPATIBLEARRAYOID);
+ bool have_anycompatible_range = (rettype == ANYCOMPATIBLERANGEOID);
+ int n_poly_args = 0; /* this counts all family-1 arguments */
+ int n_anycompatible_args = 0; /* this counts only non-unknowns */
+ Oid anycompatible_actual_types[FUNC_MAX_ARGS];
/*
* Loop through the arguments to see if we have any that are polymorphic.
* If so, require the actual types to be consistent.
*/
+ Assert(nargs <= FUNC_MAX_ARGS);
for (int j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
@@ -1743,18 +1970,87 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYCOMPATIBLEOID ||
+ decl_type == ANYCOMPATIBLENONARRAYOID)
+ {
+ have_poly_anycompatible = true;
+ if (decl_type == ANYCOMPATIBLENONARRAYOID)
+ have_anycompatible_nonarray = true;
+ if (actual_type == UNKNOWNOID)
+ continue;
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ /* collect the actual types of non-unknown COMPATIBLE args */
+ anycompatible_actual_types[n_anycompatible_args++] = actual_type;
+ }
+ else if (decl_type == ANYCOMPATIBLEARRAYOID)
+ {
+ Oid anycompatible_elem_type;
+
+ have_poly_anycompatible = true;
+ have_anycompatible_array = true;
+ if (actual_type == UNKNOWNOID)
+ continue;
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ anycompatible_elem_type = get_element_type(actual_type);
+ if (!OidIsValid(anycompatible_elem_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not an array but type %s",
+ "anycompatiblearray",
+ format_type_be(actual_type))));
+ /* collect the element type for common-supertype choice */
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type;
+ }
+ else if (decl_type == ANYCOMPATIBLERANGEOID)
+ {
+ have_poly_anycompatible = true;
+ have_anycompatible_range = true;
+ if (actual_type == UNKNOWNOID)
+ continue;
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(anycompatible_range_typeid))
+ {
+ /* All ANYCOMPATIBLERANGE arguments must be the same type */
+ if (anycompatible_range_typeid != actual_type)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anycompatiblerange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(anycompatible_range_typeid),
+ format_type_be(actual_type))));
+ }
+ else
+ {
+ anycompatible_range_typeid = actual_type;
+ anycompatible_range_typelem = get_range_subtype(actual_type);
+ if (!OidIsValid(anycompatible_range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anycompatiblerange",
+ format_type_be(actual_type))));
+ /* collect the subtype for common-supertype choice */
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+ }
+ }
}
/*
* Fast Track: if none of the arguments are polymorphic, return the
* unmodified rettype. We assume it can't be polymorphic either.
*/
- if (n_poly_args == 0)
+ if (n_poly_args == 0 && !have_poly_anycompatible)
{
Assert(!IsPolymorphicType(rettype));
return rettype;
}
+ /* Check matching of family-1 polymorphic arguments, if any */
if (n_poly_args)
{
/* Get the element type based on the array type, if we have one */
@@ -1766,13 +2062,14 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
{
/*
* 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.
+ * argument: allow it iff no other arguments are family-1
+ * polymorphics (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)))
+ (rettype != ANYARRAYOID &&
+ IsPolymorphicTypeFamily1(rettype)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot determine element type of \"anyarray\" argument")));
@@ -1888,9 +2185,108 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Check matching of family-2 polymorphic arguments, if any */
+ if (have_poly_anycompatible)
+ {
+ if (n_anycompatible_args > 0)
+ {
+ anycompatible_typeid =
+ select_common_type_from_oids(n_anycompatible_args,
+ anycompatible_actual_types,
+ false);
+
+ if (have_anycompatible_array)
+ {
+ anycompatible_array_typeid = get_array_type(anycompatible_typeid);
+ if (!OidIsValid(anycompatible_array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(anycompatible_typeid))));
+ }
+
+ if (have_anycompatible_range)
+ {
+ /* we can't infer a range type from the others */
+ if (!OidIsValid(anycompatible_range_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine polymorphic type %s because input has type %s",
+ "anycompatiblerange", "unknown")));
+
+ /*
+ * the anycompatible type must exactly match the range element
+ * type
+ */
+ if (anycompatible_range_typelem != anycompatible_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("anycompatiblerange type %s does not match anycompatible type %s",
+ format_type_be(anycompatible_range_typeid),
+ format_type_be(anycompatible_typeid))));
+ }
+
+ if (have_anycompatible_nonarray)
+ {
+ /*
+ * require the element type to not be an array or domain over
+ * array
+ */
+ if (type_is_array_domain(anycompatible_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type matched to anycompatiblenonarray is an array type: %s",
+ format_type_be(anycompatible_typeid))));
+ }
+ }
+ else
+ {
+ if (allow_poly)
+ {
+ anycompatible_typeid = ANYCOMPATIBLEOID;
+ anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
+ anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
+ }
+ else
+ {
+ /*
+ * Only way to get here is if all the ANYCOMPATIBLE args have
+ * UNKNOWN inputs. Resolve to TEXT as select_common_type()
+ * would do. That doesn't license us to use TEXTRANGE,
+ * though.
+ */
+ anycompatible_typeid = TEXTOID;
+ anycompatible_array_typeid = TEXTARRAYOID;
+ if (have_anycompatible_range)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine polymorphic type %s because input has type %s",
+ "anycompatiblerange", "unknown")));
+ }
+ }
+
+ /* replace polymorphic types by selected types */
+ for (int j = 0; j < nargs; j++)
+ {
+ Oid decl_type = declared_arg_types[j];
+
+ if (decl_type == ANYCOMPATIBLEOID ||
+ decl_type == ANYCOMPATIBLENONARRAYOID)
+ declared_arg_types[j] = anycompatible_typeid;
+ else if (decl_type == ANYCOMPATIBLEARRAYOID)
+ declared_arg_types[j] = anycompatible_array_typeid;
+ else if (decl_type == ANYCOMPATIBLERANGEOID)
+ declared_arg_types[j] = anycompatible_range_typeid;
+ }
+ }
+
/*
* If we had any UNKNOWN inputs for polymorphic arguments, re-scan to
* assign correct types to them.
+ *
+ * Note: we don't have to consider unknown inputs that were matched to
+ * ANYCOMPATIBLE-family arguments, because we forcibly updated their
+ * declared_arg_types[] positions just above.
*/
if (have_poly_unknowns)
{
@@ -1923,10 +2319,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
{
if (!OidIsValid(range_typeid))
{
+ /* we can't infer a range type from the others */
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find range type for data type %s",
- format_type_be(elem_typeid))));
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine polymorphic type %s because input has type %s",
+ "anyrange", "unknown")));
}
declared_arg_types[j] = range_typeid;
}
@@ -1957,16 +2354,49 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* if we return ANYRANGE use the appropriate argument type */
if (rettype == ANYRANGEOID)
{
+ /* this error is unreachable if the function signature is valid: */
if (!OidIsValid(range_typeid))
- {
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find range type for data type %s",
- format_type_be(elem_typeid))));
- }
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine polymorphic type %s because input has type %s",
+ "anyrange", "unknown")));
return range_typeid;
}
+ /* if we return ANYCOMPATIBLE use the appropriate type */
+ if (rettype == ANYCOMPATIBLEOID ||
+ rettype == ANYCOMPATIBLENONARRAYOID)
+ {
+ /* this error is unreachable if the function signature is valid: */
+ if (!OidIsValid(anycompatible_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg_internal("could not identify anycompatible type")));
+ return anycompatible_typeid;
+ }
+
+ /* if we return ANYCOMPATIBLEARRAY use the appropriate type */
+ if (rettype == ANYCOMPATIBLEARRAYOID)
+ {
+ /* this error is unreachable if the function signature is valid: */
+ if (!OidIsValid(anycompatible_array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg_internal("could not identify anycompatiblearray type")));
+ return anycompatible_array_typeid;
+ }
+
+ /* if we return ANYCOMPATIBLERANGE use the appropriate argument type */
+ if (rettype == ANYCOMPATIBLERANGEOID)
+ {
+ /* this error is unreachable if the function signature is valid: */
+ if (!OidIsValid(anycompatible_range_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg_internal("could not identify anycompatiblerange type")));
+ return anycompatible_range_typeid;
+ }
+
/* we don't return a generic type; send back the original return type */
return rettype;
}
@@ -1984,11 +2414,12 @@ check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
- if (ret_type == ANYRANGEOID)
+ if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
{
/*
* ANYRANGE requires an ANYRANGE input, else we can't tell which of
- * several range types with the same element type to use.
+ * several range types with the same element type to use. Likewise
+ * for ANYCOMPATIBLERANGE.
*/
for (int i = 0; i < nargs; i++)
{
@@ -1998,17 +2429,30 @@ check_valid_polymorphic_signature(Oid ret_type,
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))
+ else if (IsPolymorphicTypeFamily1(ret_type))
{
- /* Otherwise, any polymorphic type can be deduced from any other */
+ /* Otherwise, any family-1 type can be deduced from any other */
for (int i = 0; i < nargs; i++)
{
- if (IsPolymorphicType(declared_arg_types[i]))
+ if (IsPolymorphicTypeFamily1(declared_arg_types[i]))
return NULL; /* OK */
}
+ /* Keep this list in sync with IsPolymorphicTypeFamily1! */
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 if (IsPolymorphicTypeFamily2(ret_type))
+ {
+ /* Otherwise, any family-2 type can be deduced from any other */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (IsPolymorphicTypeFamily2(declared_arg_types[i]))
+ return NULL; /* OK */
+ }
+ /* Keep this list in sync with IsPolymorphicTypeFamily2! */
+ return psprintf(_("A result of type %s requires at least one input of type anycompatible, anycompatiblearray, anycompatiblenonarray, or anycompatiblerange."),
+ format_type_be(ret_type));
+ }
else
return NULL; /* OK, ret_type is not polymorphic */
}
@@ -2113,8 +2557,9 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (srctype == targettype)
return true;
- /* Anything is coercible to ANY or ANYELEMENT */
- if (targettype == ANYOID || targettype == ANYELEMENTOID)
+ /* Anything is coercible to ANY or ANYELEMENT or ANYCOMPATIBLE */
+ if (targettype == ANYOID || targettype == ANYELEMENTOID ||
+ targettype == ANYCOMPATIBLEOID)
return true;
/* If srctype is a domain, reduce to its base type */
@@ -2125,13 +2570,13 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (srctype == targettype)
return true;
- /* Also accept any array type as coercible to ANYARRAY */
- if (targettype == ANYARRAYOID)
+ /* Also accept any array type as coercible to ANY[COMPATIBLE]ARRAY */
+ if (targettype == ANYARRAYOID || targettype == ANYCOMPATIBLEARRAYOID)
if (type_is_array(srctype))
return true;
- /* Also accept any non-array type as coercible to ANYNONARRAY */
- if (targettype == ANYNONARRAYOID)
+ /* Also accept any non-array type as coercible to ANY[COMPATIBLE]NONARRAY */
+ if (targettype == ANYNONARRAYOID || targettype == ANYCOMPATIBLENONARRAYOID)
if (!type_is_array(srctype))
return true;
@@ -2140,8 +2585,8 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_enum(srctype))
return true;
- /* Also accept any range type as coercible to ANYRANGE */
- if (targettype == ANYRANGEOID)
+ /* Also accept any range type as coercible to ANY[COMPATIBLE]RANGE */
+ if (targettype == ANYRANGEOID || targettype == ANYCOMPATIBLERANGEOID)
if (type_is_range(srctype))
return true;
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index f78420e22dc..8bb00abb6b3 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -195,7 +195,7 @@ json_categorize_type(Oid typoid,
default:
/* Check for arrays and composites */
if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
- || typoid == RECORDARRAYOID)
+ || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
*tcategory = JSONTYPE_ARRAY;
else if (type_is_rowtype(typoid)) /* includes RECORDOID */
*tcategory = JSONTYPE_COMPOSITE;
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index b961d294723..1e9ca046c69 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -677,7 +677,7 @@ jsonb_categorize_type(Oid typoid,
default:
/* Check for arrays and composites */
if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
- || typoid == RECORDARRAYOID)
+ || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
*tcategory = JSONBTYPE_ARRAY;
else if (type_is_rowtype(typoid)) /* includes RECORDOID */
*tcategory = JSONBTYPE_COMPOSITE;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 9eee03c143a..3d6b2f90935 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -169,6 +169,26 @@ anyarray_send(PG_FUNCTION_ARGS)
}
/*
+ * anycompatiblearray
+ *
+ * We may as well allow output, since we do for anyarray.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblearray);
+PSEUDOTYPE_DUMMY_RECEIVE_FUNC(anycompatiblearray);
+
+Datum
+anycompatiblearray_out(PG_FUNCTION_ARGS)
+{
+ return array_out(fcinfo);
+}
+
+Datum
+anycompatiblearray_send(PG_FUNCTION_ARGS)
+{
+ return array_send(fcinfo);
+}
+
+/*
* anyenum
*
* We may as well allow output, since enum_out will in fact work.
@@ -195,6 +215,19 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anycompatiblerange
+ *
+ * We may as well allow output, since range_out will in fact work.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblerange);
+
+Datum
+anycompatiblerange_out(PG_FUNCTION_ARGS)
+{
+ return range_out(fcinfo);
+}
+
+/*
* void
*
* We support void_in so that PL functions can return VOID without any
@@ -316,3 +349,5 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler);
PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatible);
+PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblenonarray);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 4e9d21bd541..78ed8572038 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -552,7 +552,9 @@ resolve_anyrange_from_others(polymorphic_actuals *actuals)
* with concrete data types deduced from the input arguments.
* declared_args is an oidvector of the function's declared input arg types
* (showing which are polymorphic), and call_expr is the call expression.
- * Returns true if able to deduce all types, false if not.
+ *
+ * Returns true if able to deduce all types, false if necessary information
+ * is not provided (call_expr is NULL or arg types aren't identifiable).
*/
static bool
resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
@@ -564,8 +566,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anycompatible_result = false;
+ bool have_anycompatible_array_result = false;
+ bool have_anycompatible_range_result = false;
polymorphic_actuals poly_actuals;
+ polymorphic_actuals anyc_actuals;
Oid anycollation = InvalidOid;
+ Oid anycompatcollation = InvalidOid;
int i;
/* See if there are any polymorphic outputs; quick out if not */
@@ -587,6 +594,19 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ have_polymorphic_result = true;
+ have_anycompatible_result = true;
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ have_polymorphic_result = true;
+ have_anycompatible_array_result = true;
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ have_polymorphic_result = true;
+ have_anycompatible_range_result = true;
+ break;
default:
break;
}
@@ -596,12 +616,16 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/*
* Otherwise, extract actual datatype(s) from input arguments. (We assume
- * the parser already validated consistency of the arguments.)
+ * the parser already validated consistency of the arguments. Also, for
+ * the ANYCOMPATIBLE pseudotype family, we expect that all matching
+ * arguments were coerced to the selected common supertype, so that it
+ * doesn't matter which one's exposed type we look at.)
*/
if (!call_expr)
return false; /* no hope */
memset(&poly_actuals, 0, sizeof(poly_actuals));
+ memset(&anyc_actuals, 0, sizeof(anyc_actuals));
for (i = 0; i < nargs; i++)
{
@@ -636,6 +660,34 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ if (!OidIsValid(anyc_actuals.anyelement_type))
+ {
+ anyc_actuals.anyelement_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(anyc_actuals.anyelement_type))
+ return false;
+ }
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ if (!OidIsValid(anyc_actuals.anyarray_type))
+ {
+ anyc_actuals.anyarray_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(anyc_actuals.anyarray_type))
+ return false;
+ }
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ if (!OidIsValid(anyc_actuals.anyrange_type))
+ {
+ anyc_actuals.anyrange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(anyc_actuals.anyrange_type))
+ return false;
+ }
+ break;
default:
break;
}
@@ -651,18 +703,33 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
+ resolve_anyelement_from_others(&anyc_actuals);
+
+ if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
+ resolve_anyarray_from_others(&anyc_actuals);
+
+ if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
+ resolve_anyrange_from_others(&anyc_actuals);
+
/*
* Identify the collation to use for polymorphic OUT parameters. (It'll
- * necessarily be the same for both anyelement and anyarray.) Note that
- * range types are not collatable, so any possible internal collation of a
- * range type is not considered here.
+ * necessarily be the same for both anyelement and anyarray, likewise for
+ * anycompatible and anycompatiblearray.) Note that range types are not
+ * collatable, so any possible internal collation of a range type is not
+ * considered here.
*/
if (OidIsValid(poly_actuals.anyelement_type))
anycollation = get_typcollation(poly_actuals.anyelement_type);
else if (OidIsValid(poly_actuals.anyarray_type))
anycollation = get_typcollation(poly_actuals.anyarray_type);
- if (OidIsValid(anycollation))
+ if (OidIsValid(anyc_actuals.anyelement_type))
+ anycompatcollation = get_typcollation(anyc_actuals.anyelement_type);
+ else if (OidIsValid(anyc_actuals.anyarray_type))
+ anycompatcollation = get_typcollation(anyc_actuals.anyarray_type);
+
+ if (OidIsValid(anycollation) || OidIsValid(anycompatcollation))
{
/*
* The types are collatable, so consider whether to use a nondefault
@@ -672,7 +739,12 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
Oid inputcollation = exprInputCollation(call_expr);
if (OidIsValid(inputcollation))
- anycollation = inputcollation;
+ {
+ if (OidIsValid(anycollation))
+ anycollation = inputcollation;
+ if (OidIsValid(anycompatcollation))
+ anycompatcollation = inputcollation;
+ }
}
/* And finally replace the tuple column types as needed */
@@ -708,6 +780,31 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anyc_actuals.anyelement_type,
+ -1,
+ 0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anyc_actuals.anyarray_type,
+ -1,
+ 0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anyc_actuals.anyrange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a range type */
+ break;
default:
break;
}
@@ -720,7 +817,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
* Given the declared argument types and modes for a function, replace any
* polymorphic types (ANYELEMENT etc) in argtypes[] with concrete data types
* deduced from the input arguments found in call_expr.
- * Returns true if able to deduce all types, false if not.
+ *
+ * Returns true if able to deduce all types, false if necessary information
+ * is not provided (call_expr is NULL or arg types aren't identifiable).
*
* This is the same logic as resolve_polymorphic_tupdesc, but with a different
* argument representation, and slightly different output responsibilities.
@@ -735,16 +834,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anycompatible_result = false;
+ bool have_anycompatible_array_result = false;
+ bool have_anycompatible_range_result = false;
polymorphic_actuals poly_actuals;
+ polymorphic_actuals anyc_actuals;
int inargno;
int i;
/*
* First pass: resolve polymorphic inputs, check for outputs. As in
* resolve_polymorphic_tupdesc, we rely on the parser to have enforced
- * type consistency.
+ * type consistency and coerced ANYCOMPATIBLE args to a common supertype.
*/
memset(&poly_actuals, 0, sizeof(poly_actuals));
+ memset(&anyc_actuals, 0, sizeof(anyc_actuals));
inargno = 0;
for (i = 0; i < numargs; i++)
{
@@ -808,6 +912,61 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anycompatible_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(anyc_actuals.anyelement_type))
+ {
+ anyc_actuals.anyelement_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(anyc_actuals.anyelement_type))
+ return false;
+ }
+ argtypes[i] = anyc_actuals.anyelement_type;
+ }
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anycompatible_array_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(anyc_actuals.anyarray_type))
+ {
+ anyc_actuals.anyarray_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(anyc_actuals.anyarray_type))
+ return false;
+ }
+ argtypes[i] = anyc_actuals.anyarray_type;
+ }
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anycompatible_range_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(anyc_actuals.anyrange_type))
+ {
+ anyc_actuals.anyrange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(anyc_actuals.anyrange_type))
+ return false;
+ }
+ argtypes[i] = anyc_actuals.anyrange_type;
+ }
+ break;
default:
break;
}
@@ -829,6 +988,15 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
+ resolve_anyelement_from_others(&anyc_actuals);
+
+ if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
+ resolve_anyarray_from_others(&anyc_actuals);
+
+ if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
+ resolve_anyrange_from_others(&anyc_actuals);
+
/* And finally replace the output column types as needed */
for (i = 0; i < numargs; i++)
{
@@ -845,6 +1013,16 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ argtypes[i] = anyc_actuals.anyelement_type;
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ argtypes[i] = anyc_actuals.anyarray_type;
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ argtypes[i] = anyc_actuals.anyrange_type;
+ break;
default:
break;
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 3fee9342cc1..50069bea0e1 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202003181
+#define CATALOG_VERSION_NO 202003191
#endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a64378b0027..87d25d4a4bd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6673,8 +6673,9 @@
proname => 'regcollationout', provolatile => 's', prorettype => 'cstring',
proargtypes => 'regcollation', prosrc => 'regcollationout' },
{ oid => '4195', descr => 'convert classname to regcollation',
- proname => 'to_regcollation', provolatile => 's', prorettype => 'regcollation',
- proargtypes => 'text', prosrc => 'to_regcollation' },
+ proname => 'to_regcollation', provolatile => 's',
+ prorettype => 'regcollation', proargtypes => 'text',
+ prosrc => 'to_regcollation' },
{ oid => '2220', descr => 'I/O',
proname => 'regtypein', provolatile => 's', prorettype => 'regtype',
proargtypes => 'cstring', prosrc => 'regtypein' },
@@ -7115,6 +7116,42 @@
{ oid => '268', descr => 'I/O',
proname => 'table_am_handler_out', prorettype => 'cstring',
proargtypes => 'table_am_handler', prosrc => 'table_am_handler_out' },
+{ oid => '9559', descr => 'I/O',
+ proname => 'anycompatible_in', prorettype => 'anycompatible',
+ proargtypes => 'cstring', prosrc => 'anycompatible_in' },
+{ oid => '9560', descr => 'I/O',
+ proname => 'anycompatible_out', prorettype => 'cstring',
+ proargtypes => 'anycompatible', prosrc => 'anycompatible_out' },
+{ oid => '9561', descr => 'I/O',
+ proname => 'anycompatiblearray_in', prorettype => 'anycompatiblearray',
+ proargtypes => 'cstring', prosrc => 'anycompatiblearray_in' },
+{ oid => '9562', descr => 'I/O',
+ proname => 'anycompatiblearray_out', provolatile => 's',
+ prorettype => 'cstring', proargtypes => 'anycompatiblearray',
+ prosrc => 'anycompatiblearray_out' },
+{ oid => '9563', descr => 'I/O',
+ proname => 'anycompatiblearray_recv', provolatile => 's',
+ prorettype => 'anycompatiblearray', proargtypes => 'internal',
+ prosrc => 'anycompatiblearray_recv' },
+{ oid => '9564', descr => 'I/O',
+ proname => 'anycompatiblearray_send', provolatile => 's',
+ prorettype => 'bytea', proargtypes => 'anycompatiblearray',
+ prosrc => 'anycompatiblearray_send' },
+{ oid => '9565', descr => 'I/O',
+ proname => 'anycompatiblenonarray_in', prorettype => 'anycompatiblenonarray',
+ proargtypes => 'cstring', prosrc => 'anycompatiblenonarray_in' },
+{ oid => '9566', descr => 'I/O',
+ proname => 'anycompatiblenonarray_out', prorettype => 'cstring',
+ proargtypes => 'anycompatiblenonarray',
+ prosrc => 'anycompatiblenonarray_out' },
+{ oid => '9567', descr => 'I/O',
+ proname => 'anycompatiblerange_in', provolatile => 's',
+ prorettype => 'anycompatiblerange', proargtypes => 'cstring oid int4',
+ prosrc => 'anycompatiblerange_in' },
+{ oid => '9568', descr => 'I/O',
+ proname => 'anycompatiblerange_out', provolatile => 's',
+ prorettype => 'cstring', proargtypes => 'anycompatiblerange',
+ prosrc => 'anycompatiblerange_out' },
# tablesample method handlers
{ oid => '3313', descr => 'BERNOULLI tablesample method handler',
@@ -7459,8 +7496,8 @@
proname => 'regcollationrecv', prorettype => 'regcollation',
proargtypes => 'internal', prosrc => 'regcollationrecv' },
{ oid => '4197', descr => 'I/O',
- proname => 'regcollationsend', prorettype => 'bytea', proargtypes => 'regcollation',
- prosrc => 'regcollationsend' },
+ proname => 'regcollationsend', prorettype => 'bytea',
+ proargtypes => 'regcollation', prosrc => 'regcollationsend' },
{ oid => '2454', descr => 'I/O',
proname => 'regtyperecv', prorettype => 'regtype', proargtypes => 'internal',
prosrc => 'regtyperecv' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index bb21cf166db..2e6110e3f2c 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -382,7 +382,8 @@
{ oid => '4191', array_type_oid => '4192', descr => 'registered collation',
typname => 'regcollation', typlen => '4', typbyval => 't', typcategory => 'N',
typinput => 'regcollationin', typoutput => 'regcollationout',
- typreceive => 'regcollationrecv', typsend => 'regcollationsend', typalign => 'i' },
+ typreceive => 'regcollationrecv', typsend => 'regcollationsend',
+ typalign => 'i' },
{ oid => '2206', array_type_oid => '2211', descr => 'registered type',
typname => 'regtype', typlen => '4', typbyval => 't', typcategory => 'N',
typinput => 'regtypein', typoutput => 'regtypeout',
@@ -590,9 +591,34 @@
typoutput => 'table_am_handler_out', typreceive => '-', typsend => '-',
typalign => 'i' },
{ oid => '3831',
- descr => 'pseudo-type representing a polymorphic base type that is a range',
+ descr => 'pseudo-type representing a range over a polymorphic base type',
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '9550',
+ descr => 'pseudo-type representing a polymorphic common type',
+ typname => 'anycompatible', typlen => '4', typbyval => 't', typtype => 'p',
+ typcategory => 'P', typinput => 'anycompatible_in',
+ typoutput => 'anycompatible_out', typreceive => '-', typsend => '-',
+ typalign => 'i' },
+{ oid => '9551',
+ descr => 'pseudo-type representing an array of polymorphic common type elements',
+ typname => 'anycompatiblearray', typlen => '-1', typbyval => 'f',
+ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblearray_in',
+ typoutput => 'anycompatiblearray_out',
+ typreceive => 'anycompatiblearray_recv', typsend => 'anycompatiblearray_send',
+ typalign => 'd', typstorage => 'x' },
+{ oid => '9552',
+ descr => 'pseudo-type representing a polymorphic common type that is not an array',
+ typname => 'anycompatiblenonarray', typlen => '4', typbyval => 't',
+ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblenonarray_in',
+ typoutput => 'anycompatiblenonarray_out', typreceive => '-', typsend => '-',
+ typalign => 'i' },
+{ oid => '9553',
+ descr => 'pseudo-type representing a range over a polymorphic common type',
+ typname => 'anycompatiblerange', typlen => '-1', typbyval => 'f',
+ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
+ typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
+ typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 97890946c5d..7b375626484 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -295,12 +295,23 @@ typedef FormData_pg_type *Form_pg_type;
/* Is a type OID a polymorphic pseudotype? (Beware of multiple evaluation) */
#define IsPolymorphicType(typid) \
+ (IsPolymorphicTypeFamily1(typid) || \
+ IsPolymorphicTypeFamily2(typid))
+
+/* Code not part of polymorphic type resolution should not use these macros: */
+#define IsPolymorphicTypeFamily1(typid) \
((typid) == ANYELEMENTOID || \
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
(typid) == ANYRANGEOID)
+#define IsPolymorphicTypeFamily2(typid) \
+ ((typid) == ANYCOMPATIBLEOID || \
+ (typid) == ANYCOMPATIBLEARRAYOID || \
+ (typid) == ANYCOMPATIBLENONARRAYOID || \
+ (typid) == ANYCOMPATIBLERANGEOID)
+
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index c8e43e684f5..828ff5a288f 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -507,11 +507,13 @@ do_compile(FunctionCallInfo fcinfo,
{
if (forValidator)
{
- if (rettypeid == ANYARRAYOID)
+ if (rettypeid == ANYARRAYOID ||
+ rettypeid == ANYCOMPATIBLEARRAYOID)
rettypeid = INT4ARRAYOID;
- else if (rettypeid == ANYRANGEOID)
+ else if (rettypeid == ANYRANGEOID ||
+ rettypeid == ANYCOMPATIBLERANGEOID)
rettypeid = INT4RANGEOID;
- else /* ANYELEMENT or ANYNONARRAY */
+ else /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
}
@@ -2493,12 +2495,16 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID: /* XXX dubious */
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
argtypes[i] = INT4OID;
break;
case ANYARRAYOID:
+ case ANYCOMPATIBLEARRAYOID:
argtypes[i] = INT4ARRAYOID;
break;
case ANYRANGEOID:
+ case ANYCOMPATIBLERANGEOID:
argtypes[i] = INT4RANGEOID;
break;
default:
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 0073072a368..3259a22516e 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -1950,6 +1950,30 @@ select least_agg(variadic array[q1,q2]) from int8_tbl;
-4567890123456789
(1 row)
+select cleast_agg(q1,q2) from int8_tbl;
+ cleast_agg
+-------------------
+ -4567890123456789
+(1 row)
+
+select cleast_agg(4.5,f1) from int4_tbl;
+ cleast_agg
+-------------
+ -2147483647
+(1 row)
+
+select cleast_agg(variadic array[4.5,f1]) from int4_tbl;
+ cleast_agg
+-------------
+ -2147483647
+(1 row)
+
+select pg_typeof(cleast_agg(variadic array[4.5,f1])) from int4_tbl;
+ pg_typeof
+-----------
+ numeric
+(1 row)
+
-- test aggregates with common transition functions share the same states
begin work;
create type avg_state as (total bigint, count bigint);
diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out
index a2eb9996e15..dcf69094237 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -59,13 +59,39 @@ create aggregate aggfns(integer,integer,text) (
sfunc = aggfns_trans, stype = aggtype[], sspace = 10000,
initcond = '{}'
);
--- variadic aggregate
+-- check error cases that would require run-time type coercion
+create function least_accum(int8, int8) returns int8 language sql as
+ 'select least($1, $2)';
+create aggregate least_agg(int4) (
+ stype = int8, sfunc = least_accum
+); -- fails
+ERROR: function least_accum(bigint, bigint) requires run-time type coercion
+drop function least_accum(int8, int8);
+create function least_accum(anycompatible, anycompatible)
+returns anycompatible language sql as
+ 'select least($1, $2)';
+create aggregate least_agg(int4) (
+ stype = int8, sfunc = least_accum
+); -- fails
+ERROR: function least_accum(bigint, bigint) requires run-time type coercion
+create aggregate least_agg(int8) (
+ stype = int8, sfunc = least_accum
+);
+drop function least_accum(anycompatible, anycompatible) cascade;
+NOTICE: drop cascades to function least_agg(bigint)
+-- variadic aggregates
create function least_accum(anyelement, variadic anyarray)
returns anyelement language sql as
'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
create aggregate least_agg(variadic items anyarray) (
stype = anyelement, sfunc = least_accum
);
+create function cleast_accum(anycompatible, variadic anycompatiblearray)
+returns anycompatible language sql as
+ 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
+create aggregate cleast_agg(variadic items anycompatiblearray) (
+ stype = anycompatible, sfunc = cleast_accum
+);
-- test ordered-set aggs using built-in support functions
create aggregate my_percentile_disc(float8 ORDER BY anyelement) (
stype = internal,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 40468e8f497..3c0b21d633e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -329,7 +329,7 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
@@ -337,22 +337,64 @@ WHERE p1.prorettype IN
'anyenum'::regtype = ANY (p1.proargtypes) OR
'anyrange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
- oid | proname
-------+------------------
+ oid | proname
+------+----------------
2296 | anyarray_in
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
2777 | anynonarray_in
- 3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+(9 rows)
+
+-- anyrange is tighter than the rest, can only resolve from anyrange input
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype = 'anyrange'::regtype
+ AND NOT
+ 'anyrange'::regtype = ANY (p1.proargtypes)
+ORDER BY 2;
+ oid | proname
+------+------------------
+ 3832 | anyrange_in
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(4 rows)
+
+-- similarly for the anycompatible family
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype IN
+ ('anycompatible'::regtype, 'anycompatiblearray'::regtype,
+ 'anycompatiblenonarray'::regtype)
+ AND NOT
+ ('anycompatible'::regtype = ANY (p1.proargtypes) OR
+ 'anycompatiblearray'::regtype = ANY (p1.proargtypes) OR
+ 'anycompatiblenonarray'::regtype = ANY (p1.proargtypes) OR
+ 'anycompatiblerange'::regtype = ANY (p1.proargtypes))
+ORDER BY 2;
+ oid | proname
+------+--------------------------
+ 9559 | anycompatible_in
+ 9561 | anycompatiblearray_in
+ 9563 | anycompatiblearray_recv
+ 9565 | anycompatiblenonarray_in
+(4 rows)
+
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype = 'anycompatiblerange'::regtype
+ AND NOT
+ 'anycompatiblerange'::regtype = ANY (p1.proargtypes)
+ORDER BY 2;
+ oid | proname
+------+-----------------------
+ 9567 | anycompatiblerange_in
+(1 row)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index dfc10e32426..d0a6b630b8f 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1823,6 +1823,88 @@ select f1(int4range(42, 49)) as int, f1(float8range(4.5, 7.8)) as num;
(1 row)
drop function f1(x anyrange);
+create function f1(x anycompatible, y anycompatible) returns anycompatiblearray as $$
+begin
+ return array[x, y];
+end$$ language plpgsql;
+select f1(2, 4) as int, f1(2, 4.5) as num;
+ int | num
+-------+---------
+ {2,4} | {2,4.5}
+(1 row)
+
+drop function f1(x anycompatible, y anycompatible);
+create function f1(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+begin
+ return array[lower(x), upper(x), y, z];
+end$$ language plpgsql;
+select f1(int4range(42, 49), 11, 2::smallint) as int, f1(float8range(4.5, 7.8), 7.8, 11::real) as num;
+ int | num
+--------------+------------------
+ {42,49,11,2} | {4.5,7.8,7.8,11}
+(1 row)
+
+select f1(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit
+ERROR: function f1(int4range, integer, numeric) does not exist
+LINE 1: select f1(int4range(42, 49), 11, 4.5) as fail;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function f1(x anycompatiblerange, y anycompatible, z anycompatible);
+-- fail, can't infer type:
+create function f1(x anycompatible) returns anycompatiblerange as $$
+begin
+ return array[x + 1, x + 2];
+end$$ language plpgsql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
+begin
+ return x;
+end$$ language plpgsql;
+select f1(int4range(42, 49), array[11]) as int, f1(float8range(4.5, 7.8), array[7]) as num;
+ int | num
+---------+-----------
+ [42,49) | [4.5,7.8)
+(1 row)
+
+drop function f1(x anycompatiblerange, y anycompatiblearray);
+create function f1(a anyelement, b anyarray,
+ c anycompatible, d anycompatible,
+ OUT x anyarray, OUT y anycompatiblearray)
+as $$
+begin
+ x := a || b;
+ y := array[c, d];
+end$$ language plpgsql;
+select x, pg_typeof(x), y, pg_typeof(y)
+ from f1(11, array[1, 2], 42, 34.5);
+ x | pg_typeof | y | pg_typeof
+----------+-----------+-----------+-----------
+ {11,1,2} | integer[] | {42,34.5} | numeric[]
+(1 row)
+
+select x, pg_typeof(x), y, pg_typeof(y)
+ from f1(11, array[1, 2], point(1,2), point(3,4));
+ x | pg_typeof | y | pg_typeof
+----------+-----------+-------------------+-----------
+ {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[]
+(1 row)
+
+select x, pg_typeof(x), y, pg_typeof(y)
+ from f1(11, '{1,2}', point(1,2), '(3,4)');
+ x | pg_typeof | y | pg_typeof
+----------+-----------+-------------------+-----------
+ {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[]
+(1 row)
+
+select x, pg_typeof(x), y, pg_typeof(y)
+ from f1(11, array[1, 2.2], 42, 34.5); -- fail
+ERROR: function f1(integer, numeric[], integer, numeric) does not exist
+LINE 2: from f1(11, array[1, 2.2], 42, 34.5);
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function f1(a anyelement, b anyarray,
+ c anycompatible, d anycompatible);
--
-- Test handling of OUT parameters, including polymorphic cases.
-- Note that RETURN is optional with OUT params; we try both ways.
@@ -1940,6 +2022,25 @@ select * from duplic('foo'::text);
(1 row)
drop function duplic(anyelement);
+create function duplic(in i anycompatiblerange, out j anycompatible, out k anycompatiblearray) as $$
+begin
+ j := lower(i);
+ k := array[lower(i),upper(i)];
+ return;
+end$$ language plpgsql;
+select * from duplic(int4range(42,49));
+ j | k
+----+---------
+ 42 | {42,49}
+(1 row)
+
+select * from duplic(textrange('aaa', 'bbb'));
+ j | k
+-----+-----------
+ aaa | {aaa,bbb}
+(1 row)
+
+drop function duplic(anycompatiblerange);
--
-- test PERFORM
--
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 331562de19d..d86a7004a6f 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -72,6 +72,82 @@ select polyf(int4range(42, 49)) as int, polyf(float8range(4.5, 7.8)) as num;
(1 row)
drop function polyf(x anyrange);
+create function polyf(x anycompatible, y anycompatible) returns anycompatiblearray as $$
+ select array[x, y]
+$$ language sql;
+select polyf(2, 4) as int, polyf(2, 4.5) as num;
+ int | num
+-------+---------
+ {2,4} | {2,4.5}
+(1 row)
+
+drop function polyf(x anycompatible, y anycompatible);
+create function polyf(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+ select array[lower(x), upper(x), y, z]
+$$ language sql;
+select polyf(int4range(42, 49), 11, 2::smallint) as int, polyf(float8range(4.5, 7.8), 7.8, 11::real) as num;
+ int | num
+--------------+------------------
+ {42,49,11,2} | {4.5,7.8,7.8,11}
+(1 row)
+
+select polyf(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit
+ERROR: function polyf(int4range, integer, numeric) does not exist
+LINE 1: select polyf(int4range(42, 49), 11, 4.5) as fail;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblerange as $$
+ select array[x + 1, x + 2]
+$$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+create function polyf(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
+ select x
+$$ language sql;
+select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8), array[7]) as num;
+ int | num
+---------+-----------
+ [42,49) | [4.5,7.8)
+(1 row)
+
+drop function polyf(x anycompatiblerange, y anycompatiblearray);
+create function polyf(a anyelement, b anyarray,
+ c anycompatible, d anycompatible,
+ OUT x anyarray, OUT y anycompatiblearray)
+as $$
+ select a || b, array[c, d]
+$$ language sql;
+select x, pg_typeof(x), y, pg_typeof(y)
+ from polyf(11, array[1, 2], 42, 34.5);
+ x | pg_typeof | y | pg_typeof
+----------+-----------+-----------+-----------
+ {11,1,2} | integer[] | {42,34.5} | numeric[]
+(1 row)
+
+select x, pg_typeof(x), y, pg_typeof(y)
+ from polyf(11, array[1, 2], point(1,2), point(3,4));
+ x | pg_typeof | y | pg_typeof
+----------+-----------+-------------------+-----------
+ {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[]
+(1 row)
+
+select x, pg_typeof(x), y, pg_typeof(y)
+ from polyf(11, '{1,2}', point(1,2), '(3,4)');
+ x | pg_typeof | y | pg_typeof
+----------+-----------+-------------------+-----------
+ {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[]
+(1 row)
+
+select x, pg_typeof(x), y, pg_typeof(y)
+ from polyf(11, array[1, 2.2], 42, 34.5); -- fail
+ERROR: function polyf(integer, numeric[], integer, numeric) does not exist
+LINE 2: from polyf(11, array[1, 2.2], 42, 34.5);
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function polyf(a anyelement, b anyarray,
+ c anycompatible, d anycompatible);
--
-- Polymorphic aggregate tests
--
@@ -1621,3 +1697,238 @@ View definition:
drop view dfview;
drop function dfunc(anyelement, anyelement, bool);
+--
+-- Tests for ANYCOMPATIBLE polymorphism family
+--
+create function anyctest(anycompatible, anycompatible)
+returns anycompatible as $$
+ select greatest($1, $2)
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, 12) x;
+ x | pg_typeof
+----+-----------
+ 12 | integer
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+ x | pg_typeof
+------+-----------
+ 12.3 | numeric
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, point(1,2)) x; -- fail
+ERROR: function anyctest(integer, point) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, point(1,2)) x;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest('11', '12.3') x; -- defaults to text
+ x | pg_typeof
+------+-----------
+ 12.3 | text
+(1 row)
+
+drop function anyctest(anycompatible, anycompatible);
+create function anyctest(anycompatible, anycompatible)
+returns anycompatiblearray as $$
+ select array[$1, $2]
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, 12) x;
+ x | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+ x | pg_typeof
+-----------+-----------
+ {11,12.3} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, array[1,2]) x; -- fail
+ERROR: function anyctest(integer, integer[]) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, array[1,2]) x;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatible, anycompatible);
+create function anyctest(anycompatible, anycompatiblearray)
+returns anycompatiblearray as $$
+ select array[$1] || $2
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, array[12]) x;
+ x | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, array[12.3]) x;
+ x | pg_typeof
+-----------+-----------
+ {11,12.3} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(12.3, array[13]) x;
+ x | pg_typeof
+-----------+-----------
+ {12.3,13} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(12.3, '{13,14.4}') x;
+ x | pg_typeof
+----------------+-----------
+ {12.3,13,14.4} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, array[point(1,2)]) x; -- fail
+ERROR: function anyctest(integer, point[]) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, array[point(1,2)]) ...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
+ERROR: function anyctest(integer, integer) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatible, anycompatiblearray);
+create function anyctest(anycompatible, anycompatiblerange)
+returns anycompatiblerange as $$
+ select $2
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, int4range(4,7)) x;
+ x | pg_typeof
+-------+-----------
+ [4,7) | int4range
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, numrange(4,7)) x;
+ x | pg_typeof
+-------+-----------
+ [4,7) | numrange
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
+ERROR: function anyctest(integer, integer) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, int4range(4,7)) x; -- fail
+ERROR: function anyctest(numeric, int4range) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11.2, int4range(4,7)) x...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, '[4,7)') x; -- fail
+ERROR: could not determine polymorphic type anycompatiblerange because input has type unknown
+drop function anyctest(anycompatible, anycompatiblerange);
+create function anyctest(anycompatiblerange, anycompatiblerange)
+returns anycompatible as $$
+ select lower($1) + upper($2)
+$$ language sql;
+select x, pg_typeof(x) from anyctest(int4range(11,12), int4range(4,7)) x;
+ x | pg_typeof
+----+-----------
+ 18 | integer
+(1 row)
+
+select x, pg_typeof(x) from anyctest(int4range(11,12), numrange(4,7)) x; -- fail
+ERROR: function anyctest(int4range, numrange) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(int4range(11,12), numra...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatiblerange, anycompatiblerange);
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblerange as $$
+ select $1
+$$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+ select array[$1, $2]
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, 12) x;
+ x | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+ x | pg_typeof
+-----------+-----------
+ {11,12.3} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(array[11], array[1,2]) x; -- fail
+ERROR: function anyctest(integer[], integer[]) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(array[11], array[1,2]) ...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatiblenonarray, anycompatiblenonarray);
+create function anyctest(a anyelement, b anyarray,
+ c anycompatible, d anycompatible)
+returns anycompatiblearray as $$
+ select array[c, d]
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, array[1, 2], 42, 34.5) x;
+ x | pg_typeof
+-----------+-----------
+ {42,34.5} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, array[1, 2], point(1,2), point(3,4)) x;
+ x | pg_typeof
+-------------------+-----------
+ {"(1,2)","(3,4)"} | point[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, '{1,2}', point(1,2), '(3,4)') x;
+ x | pg_typeof
+-------------------+-----------
+ {"(1,2)","(3,4)"} | point[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, array[1, 2.2], 42, 34.5) x; -- fail
+ERROR: function anyctest(integer, numeric[], integer, numeric) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, array[1, 2.2], 42, ...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(a anyelement, b anyarray,
+ c anycompatible, d anycompatible);
+create function anyctest(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+ select $1
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, 12) x;
+ x | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12.2) x;
+ x | pg_typeof
+-----------+-----------
+ {11,12.2} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, '12') x;
+ x | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, '12.2') x; -- fail
+ERROR: invalid input syntax for type integer: "12.2"
+LINE 1: select x, pg_typeof(x) from anyctest(11, '12.2') x;
+ ^
+select x, pg_typeof(x) from anyctest(variadic array[11, 12]) x;
+ x | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(variadic array[11, 12.2]) x;
+ x | pg_typeof
+-----------+-----------
+ {11,12.2} | numeric[]
+(1 row)
+
+drop function anyctest(variadic anycompatiblearray);
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index cdfc43e8549..7eced284520 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1557,6 +1557,59 @@ CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type
DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
+AS 'select $1, $2' LANGUAGE sql;
+SELECT dup(22, array[44]);
+ dup
+-----------
+ (22,{44})
+(1 row)
+
+SELECT dup(4.5, array[44]);
+ dup
+------------
+ (4.5,{44})
+(1 row)
+
+SELECT dup(22, array[44::bigint]);
+ dup
+-----------
+ (22,{44})
+(1 row)
+
+SELECT *, pg_typeof(f3), pg_typeof(f4) FROM dup(22, array[44::bigint]);
+ f3 | f4 | pg_typeof | pg_typeof
+----+------+-----------+-----------
+ 22 | {44} | bigint | bigint[]
+(1 row)
+
+DROP FUNCTION dup(f1 anycompatible, f2 anycompatiblearray);
+CREATE FUNCTION dup (f1 anycompatiblerange, f2 out anycompatible, f3 out anycompatiblearray, f4 out anycompatiblerange)
+AS 'select lower($1), array[lower($1), upper($1)], $1' LANGUAGE sql;
+SELECT dup(int4range(4,7));
+ dup
+---------------------
+ (4,"{4,7}","[4,7)")
+(1 row)
+
+SELECT dup(numrange(4,7));
+ dup
+---------------------
+ (4,"{4,7}","[4,7)")
+(1 row)
+
+SELECT dup(textrange('aaa', 'bbb'));
+ dup
+-------------------------------
+ (aaa,"{aaa,bbb}","[aaa,bbb)")
+(1 row)
+
+DROP FUNCTION dup(f1 anycompatiblerange);
+-- fails, no way to deduce outputs
+CREATE FUNCTION bad (f1 anyarray, out f2 anycompatible, out f3 anycompatiblearray)
+AS 'select $1, array[$1,$1]' LANGUAGE sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatible requires at least one input of type anycompatible, anycompatiblearray, anycompatiblenonarray, or anycompatiblerange.
--
-- table functions
--
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index a28741a888c..c421f5394fe 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -1405,6 +1405,32 @@ ERROR: function rangetypes_sql(numrange, integer[]) does not exist
LINE 1: select rangetypes_sql(numrange(1,10), ARRAY[2,20]);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+create function anycompatiblearray_anycompatiblerange_func(a anycompatiblearray, r anycompatiblerange)
+ returns anycompatible as 'select $1[1] + lower($2);' language sql;
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1,2], int4range(10,20));
+ anycompatiblearray_anycompatiblerange_func
+--------------------------------------------
+ 11
+(1 row)
+
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1,2], numrange(10,20));
+ anycompatiblearray_anycompatiblerange_func
+--------------------------------------------
+ 11
+(1 row)
+
+-- should fail
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1.1,2], int4range(10,20));
+ERROR: function anycompatiblearray_anycompatiblerange_func(numeric[], int4range) does not exist
+LINE 1: select anycompatiblearray_anycompatiblerange_func(ARRAY[1.1,...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anycompatiblearray_anycompatiblerange_func(anycompatiblearray, anycompatiblerange);
+-- should fail
+create function bogus_func(anycompatible)
+ returns anycompatiblerange as 'select int4range(1,10)' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
--
-- Arrays of ranges
--
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04c..274130e7062 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -131,14 +131,16 @@ WHERE p1.typinput = p2.oid AND NOT
-- Check for type of the variadic array parameter's elements.
-- provariadic should be ANYOID if the type of the last element is ANYOID,
--- ANYELEMENTOID if the type of the last element is ANYARRAYOID, and otherwise
--- the element type corresponding to the array type.
+-- ANYELEMENTOID if the type of the last element is ANYARRAYOID,
+-- ANYCOMPATIBLEOID if the type of the last element is ANYCOMPATIBLEARRAYOID,
+-- and otherwise the element type corresponding to the array type.
SELECT oid::regprocedure, provariadic::regtype, proargtypes::regtype[]
FROM pg_proc
WHERE provariadic != 0
AND case proargtypes[array_length(proargtypes, 1)-1]
- WHEN 2276 THEN 2276 -- any -> any
- WHEN 2277 THEN 2283 -- anyarray -> anyelement
+ WHEN '"any"'::regtype THEN '"any"'::regtype
+ WHEN 'anyarray'::regtype THEN 'anyelement'::regtype
+ WHEN 'anycompatiblearray'::regtype THEN 'anycompatible'::regtype
ELSE (SELECT t.oid
FROM pg_type t
WHERE t.typarray = proargtypes[array_length(proargtypes, 1)-1])
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 02578330a6f..5da6f4152be 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -740,6 +740,10 @@ drop view aggordview1;
select least_agg(q1,q2) from int8_tbl;
select least_agg(variadic array[q1,q2]) from int8_tbl;
+select cleast_agg(q1,q2) from int8_tbl;
+select cleast_agg(4.5,f1) from int4_tbl;
+select cleast_agg(variadic array[4.5,f1]) from int4_tbl;
+select pg_typeof(cleast_agg(variadic array[4.5,f1])) from int4_tbl;
-- test aggregates with common transition functions share the same states
begin work;
diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql
index fd7cd400c19..d4b4036fd7d 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -72,7 +72,31 @@ create aggregate aggfns(integer,integer,text) (
initcond = '{}'
);
--- variadic aggregate
+-- check error cases that would require run-time type coercion
+create function least_accum(int8, int8) returns int8 language sql as
+ 'select least($1, $2)';
+
+create aggregate least_agg(int4) (
+ stype = int8, sfunc = least_accum
+); -- fails
+
+drop function least_accum(int8, int8);
+
+create function least_accum(anycompatible, anycompatible)
+returns anycompatible language sql as
+ 'select least($1, $2)';
+
+create aggregate least_agg(int4) (
+ stype = int8, sfunc = least_accum
+); -- fails
+
+create aggregate least_agg(int8) (
+ stype = int8, sfunc = least_accum
+);
+
+drop function least_accum(anycompatible, anycompatible) cascade;
+
+-- variadic aggregates
create function least_accum(anyelement, variadic anyarray)
returns anyelement language sql as
'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
@@ -81,6 +105,14 @@ create aggregate least_agg(variadic items anyarray) (
stype = anyelement, sfunc = least_accum
);
+create function cleast_accum(anycompatible, variadic anycompatiblearray)
+returns anycompatible language sql as
+ 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
+
+create aggregate cleast_agg(variadic items anycompatiblearray) (
+ stype = anycompatible, sfunc = cleast_accum
+);
+
-- test ordered-set aggs using built-in support functions
create aggregate my_percentile_disc(float8 ORDER BY anyelement) (
stype = internal,
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index f06f245db3b..389d5b2464a 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -273,7 +273,7 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
@@ -282,6 +282,37 @@ WHERE p1.prorettype IN
'anyrange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
+-- anyrange is tighter than the rest, can only resolve from anyrange input
+
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype = 'anyrange'::regtype
+ AND NOT
+ 'anyrange'::regtype = ANY (p1.proargtypes)
+ORDER BY 2;
+
+-- similarly for the anycompatible family
+
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype IN
+ ('anycompatible'::regtype, 'anycompatiblearray'::regtype,
+ 'anycompatiblenonarray'::regtype)
+ AND NOT
+ ('anycompatible'::regtype = ANY (p1.proargtypes) OR
+ 'anycompatiblearray'::regtype = ANY (p1.proargtypes) OR
+ 'anycompatiblenonarray'::regtype = ANY (p1.proargtypes) OR
+ 'anycompatiblerange'::regtype = ANY (p1.proargtypes))
+ORDER BY 2;
+
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype = 'anycompatiblerange'::regtype
+ AND NOT
+ 'anycompatiblerange'::regtype = ANY (p1.proargtypes)
+ORDER BY 2;
+
+
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
-- idea to use cstring input for a function meant to be called from SQL;
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index c5e6e6e5ede..07c60c80e48 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -1618,6 +1618,62 @@ select f1(int4range(42, 49)) as int, f1(float8range(4.5, 7.8)) as num;
drop function f1(x anyrange);
+create function f1(x anycompatible, y anycompatible) returns anycompatiblearray as $$
+begin
+ return array[x, y];
+end$$ language plpgsql;
+
+select f1(2, 4) as int, f1(2, 4.5) as num;
+
+drop function f1(x anycompatible, y anycompatible);
+
+create function f1(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+begin
+ return array[lower(x), upper(x), y, z];
+end$$ language plpgsql;
+
+select f1(int4range(42, 49), 11, 2::smallint) as int, f1(float8range(4.5, 7.8), 7.8, 11::real) as num;
+
+select f1(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit
+
+drop function f1(x anycompatiblerange, y anycompatible, z anycompatible);
+
+-- fail, can't infer type:
+create function f1(x anycompatible) returns anycompatiblerange as $$
+begin
+ return array[x + 1, x + 2];
+end$$ language plpgsql;
+
+create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
+begin
+ return x;
+end$$ language plpgsql;
+
+select f1(int4range(42, 49), array[11]) as int, f1(float8range(4.5, 7.8), array[7]) as num;
+
+drop function f1(x anycompatiblerange, y anycompatiblearray);
+
+create function f1(a anyelement, b anyarray,
+ c anycompatible, d anycompatible,
+ OUT x anyarray, OUT y anycompatiblearray)
+as $$
+begin
+ x := a || b;
+ y := array[c, d];
+end$$ language plpgsql;
+
+select x, pg_typeof(x), y, pg_typeof(y)
+ from f1(11, array[1, 2], 42, 34.5);
+select x, pg_typeof(x), y, pg_typeof(y)
+ from f1(11, array[1, 2], point(1,2), point(3,4));
+select x, pg_typeof(x), y, pg_typeof(y)
+ from f1(11, '{1,2}', point(1,2), '(3,4)');
+select x, pg_typeof(x), y, pg_typeof(y)
+ from f1(11, array[1, 2.2], 42, 34.5); -- fail
+
+drop function f1(a anyelement, b anyarray,
+ c anycompatible, d anycompatible);
+
--
-- Test handling of OUT parameters, including polymorphic cases.
-- Note that RETURN is optional with OUT params; we try both ways.
@@ -1699,6 +1755,18 @@ select * from duplic('foo'::text);
drop function duplic(anyelement);
+create function duplic(in i anycompatiblerange, out j anycompatible, out k anycompatiblearray) as $$
+begin
+ j := lower(i);
+ k := array[lower(i),upper(i)];
+ return;
+end$$ language plpgsql;
+
+select * from duplic(int4range(42,49));
+select * from duplic(textrange('aaa', 'bbb'));
+
+drop function duplic(anycompatiblerange);
+
--
-- test PERFORM
--
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index 9c7b43efebb..b57591f69f6 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -53,6 +53,56 @@ select polyf(int4range(42, 49)) as int, polyf(float8range(4.5, 7.8)) as num;
drop function polyf(x anyrange);
+create function polyf(x anycompatible, y anycompatible) returns anycompatiblearray as $$
+ select array[x, y]
+$$ language sql;
+
+select polyf(2, 4) as int, polyf(2, 4.5) as num;
+
+drop function polyf(x anycompatible, y anycompatible);
+
+create function polyf(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+ select array[lower(x), upper(x), y, z]
+$$ language sql;
+
+select polyf(int4range(42, 49), 11, 2::smallint) as int, polyf(float8range(4.5, 7.8), 7.8, 11::real) as num;
+
+select polyf(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit
+
+drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblerange as $$
+ select array[x + 1, x + 2]
+$$ language sql;
+
+create function polyf(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
+ select x
+$$ language sql;
+
+select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8), array[7]) as num;
+
+drop function polyf(x anycompatiblerange, y anycompatiblearray);
+
+create function polyf(a anyelement, b anyarray,
+ c anycompatible, d anycompatible,
+ OUT x anyarray, OUT y anycompatiblearray)
+as $$
+ select a || b, array[c, d]
+$$ language sql;
+
+select x, pg_typeof(x), y, pg_typeof(y)
+ from polyf(11, array[1, 2], 42, 34.5);
+select x, pg_typeof(x), y, pg_typeof(y)
+ from polyf(11, array[1, 2], point(1,2), point(3,4));
+select x, pg_typeof(x), y, pg_typeof(y)
+ from polyf(11, '{1,2}', point(1,2), '(3,4)');
+select x, pg_typeof(x), y, pg_typeof(y)
+ from polyf(11, array[1, 2.2], 42, 34.5); -- fail
+
+drop function polyf(a anyelement, b anyarray,
+ c anycompatible, d anycompatible);
+
--
-- Polymorphic aggregate tests
@@ -868,3 +918,112 @@ select * from dfview;
drop view dfview;
drop function dfunc(anyelement, anyelement, bool);
+
+--
+-- Tests for ANYCOMPATIBLE polymorphism family
+--
+
+create function anyctest(anycompatible, anycompatible)
+returns anycompatible as $$
+ select greatest($1, $2)
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, 12) x;
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+select x, pg_typeof(x) from anyctest(11, point(1,2)) x; -- fail
+select x, pg_typeof(x) from anyctest('11', '12.3') x; -- defaults to text
+
+drop function anyctest(anycompatible, anycompatible);
+
+create function anyctest(anycompatible, anycompatible)
+returns anycompatiblearray as $$
+ select array[$1, $2]
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, 12) x;
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+select x, pg_typeof(x) from anyctest(11, array[1,2]) x; -- fail
+
+drop function anyctest(anycompatible, anycompatible);
+
+create function anyctest(anycompatible, anycompatiblearray)
+returns anycompatiblearray as $$
+ select array[$1] || $2
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, array[12]) x;
+select x, pg_typeof(x) from anyctest(11, array[12.3]) x;
+select x, pg_typeof(x) from anyctest(12.3, array[13]) x;
+select x, pg_typeof(x) from anyctest(12.3, '{13,14.4}') x;
+select x, pg_typeof(x) from anyctest(11, array[point(1,2)]) x; -- fail
+select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
+
+drop function anyctest(anycompatible, anycompatiblearray);
+
+create function anyctest(anycompatible, anycompatiblerange)
+returns anycompatiblerange as $$
+ select $2
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, int4range(4,7)) x;
+select x, pg_typeof(x) from anyctest(11, numrange(4,7)) x;
+select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
+select x, pg_typeof(x) from anyctest(11.2, int4range(4,7)) x; -- fail
+select x, pg_typeof(x) from anyctest(11.2, '[4,7)') x; -- fail
+
+drop function anyctest(anycompatible, anycompatiblerange);
+
+create function anyctest(anycompatiblerange, anycompatiblerange)
+returns anycompatible as $$
+ select lower($1) + upper($2)
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(int4range(11,12), int4range(4,7)) x;
+select x, pg_typeof(x) from anyctest(int4range(11,12), numrange(4,7)) x; -- fail
+
+drop function anyctest(anycompatiblerange, anycompatiblerange);
+
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblerange as $$
+ select $1
+$$ language sql;
+
+create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+ select array[$1, $2]
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, 12) x;
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+select x, pg_typeof(x) from anyctest(array[11], array[1,2]) x; -- fail
+
+drop function anyctest(anycompatiblenonarray, anycompatiblenonarray);
+
+create function anyctest(a anyelement, b anyarray,
+ c anycompatible, d anycompatible)
+returns anycompatiblearray as $$
+ select array[c, d]
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, array[1, 2], 42, 34.5) x;
+select x, pg_typeof(x) from anyctest(11, array[1, 2], point(1,2), point(3,4)) x;
+select x, pg_typeof(x) from anyctest(11, '{1,2}', point(1,2), '(3,4)') x;
+select x, pg_typeof(x) from anyctest(11, array[1, 2.2], 42, 34.5) x; -- fail
+
+drop function anyctest(a anyelement, b anyarray,
+ c anycompatible, d anycompatible);
+
+create function anyctest(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+ select $1
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, 12) x;
+select x, pg_typeof(x) from anyctest(11, 12.2) x;
+select x, pg_typeof(x) from anyctest(11, '12') x;
+select x, pg_typeof(x) from anyctest(11, '12.2') x; -- fail
+select x, pg_typeof(x) from anyctest(variadic array[11, 12]) x;
+select x, pg_typeof(x) from anyctest(variadic array[11, 12.2]) x;
+
+drop function anyctest(variadic anycompatiblearray);
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 476b4f27e25..ae3119a959e 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -407,6 +407,27 @@ DROP FUNCTION dup(anyelement);
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
+CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
+AS 'select $1, $2' LANGUAGE sql;
+SELECT dup(22, array[44]);
+SELECT dup(4.5, array[44]);
+SELECT dup(22, array[44::bigint]);
+SELECT *, pg_typeof(f3), pg_typeof(f4) FROM dup(22, array[44::bigint]);
+
+DROP FUNCTION dup(f1 anycompatible, f2 anycompatiblearray);
+
+CREATE FUNCTION dup (f1 anycompatiblerange, f2 out anycompatible, f3 out anycompatiblearray, f4 out anycompatiblerange)
+AS 'select lower($1), array[lower($1), upper($1)], $1' LANGUAGE sql;
+SELECT dup(int4range(4,7));
+SELECT dup(numrange(4,7));
+SELECT dup(textrange('aaa', 'bbb'));
+
+DROP FUNCTION dup(f1 anycompatiblerange);
+
+-- fails, no way to deduce outputs
+CREATE FUNCTION bad (f1 anyarray, out f2 anycompatible, out f3 anycompatiblearray)
+AS 'select $1, array[$1,$1]' LANGUAGE sql;
+
--
-- table functions
--
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 85eaa9b34c6..4048b1d1854 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -456,6 +456,22 @@ create function rangetypes_sql(q anyrange, b anyarray, out c anyelement)
select rangetypes_sql(int4range(1,10), ARRAY[2,20]);
select rangetypes_sql(numrange(1,10), ARRAY[2,20]); -- match failure
+create function anycompatiblearray_anycompatiblerange_func(a anycompatiblearray, r anycompatiblerange)
+ returns anycompatible as 'select $1[1] + lower($2);' language sql;
+
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1,2], int4range(10,20));
+
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1,2], numrange(10,20));
+
+-- should fail
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1.1,2], int4range(10,20));
+
+drop function anycompatiblearray_anycompatiblerange_func(anycompatiblearray, anycompatiblerange);
+
+-- should fail
+create function bogus_func(anycompatible)
+ returns anycompatiblerange as 'select int4range(1,10)' language sql;
+
--
-- Arrays of ranges
--
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98a..4b492ce0625 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -106,15 +106,17 @@ WHERE p1.typinput = p2.oid AND NOT
-- Check for type of the variadic array parameter's elements.
-- provariadic should be ANYOID if the type of the last element is ANYOID,
--- ANYELEMENTOID if the type of the last element is ANYARRAYOID, and otherwise
--- the element type corresponding to the array type.
+-- ANYELEMENTOID if the type of the last element is ANYARRAYOID,
+-- ANYCOMPATIBLEOID if the type of the last element is ANYCOMPATIBLEARRAYOID,
+-- and otherwise the element type corresponding to the array type.
SELECT oid::regprocedure, provariadic::regtype, proargtypes::regtype[]
FROM pg_proc
WHERE provariadic != 0
AND case proargtypes[array_length(proargtypes, 1)-1]
- WHEN 2276 THEN 2276 -- any -> any
- WHEN 2277 THEN 2283 -- anyarray -> anyelement
+ WHEN '"any"'::regtype THEN '"any"'::regtype
+ WHEN 'anyarray'::regtype THEN 'anyelement'::regtype
+ WHEN 'anycompatiblearray'::regtype THEN 'anycompatible'::regtype
ELSE (SELECT t.oid
FROM pg_type t
WHERE t.typarray = proargtypes[array_length(proargtypes, 1)-1])