aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_coerce.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_coerce.c')
-rw-r--r--src/backend/parser/parse_coerce.c180
1 files changed, 157 insertions, 23 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 127818abdea..a461ac9aef1 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -158,7 +158,8 @@ coerce_type(ParseState *pstate, Node *node,
return node;
}
if (targetTypeId == ANYARRAYOID ||
- targetTypeId == ANYENUMOID)
+ targetTypeId == ANYENUMOID ||
+ targetTypeId == ANYRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
@@ -1275,9 +1276,11 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 1) All arguments declared ANYARRAY must have matching datatypes,
* and must in fact be varlena arrays.
* 2) All arguments declared ANYELEMENT must have matching datatypes.
- * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
- * the actual ANYELEMENT datatype is in fact the element type for
- * the actual ANYARRAY datatype.
+ * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the
+ * actual ANYELEMENT datatype is in fact the element type for the actual
+ * ANYARRAY datatype. Similarly, if there are arguments of both ANYELEMENT
+ * and ANYRANGE, make sure the actual ANYELEMENT datatype is in fact the
+ * subtype for the actual ANYRANGE type.
* 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
@@ -1311,6 +1314,8 @@ check_generic_type_consistency(Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid array_typelem;
+ Oid range_typeid = InvalidOid;
+ Oid range_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1348,6 +1353,15 @@ check_generic_type_consistency(Oid *actual_arg_types,
return false;
array_typeid = actual_type;
}
+ else if (decl_type == ANYRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type);
+ if (OidIsValid(range_typeid) && actual_type != range_typeid)
+ return false;
+ range_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1393,6 +1407,27 @@ check_generic_type_consistency(Oid *actual_arg_types,
return false;
}
+ /* Get the element type based on the range type, if we have one */
+ if (OidIsValid(range_typeid))
+ {
+ range_typelem = get_range_subtype(range_typeid);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
/* Looks valid */
return true;
}
@@ -1416,23 +1451,28 @@ check_generic_type_consistency(Oid *actual_arg_types,
* if it is declared as a polymorphic type:
*
* 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
- * argument's actual type as the function's return type.
- * 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument
- * is ANYELEMENT, use the actual type of the argument to determine
- * the function's return type, i.e. the element type's corresponding
- * array type.
+ * argument's actual type as the function's return type. Similarly, if
+ * return type is ANYRANGE, and any argument is ANYRANGE, use the argument's
+ * actual type as the function's return type.
+ * 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument is
+ * ANYELEMENT, use the actual type of the argument to determine the
+ * function's return type, i.e. the element type's corresponding array
+ * type. Note: similar behavior does not exist for ANYRANGE, because it's
+ * impossble to determine the range type from the subtype alone.\
* 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
- * generate an ERROR. This condition is prevented by CREATE FUNCTION
- * and is therefore not expected here.
+ * generate an ERROR. Similarly, if the return type is ANYRANGE, and no
+ * argument is ANYRANGE or ANYELEMENT, generate an error. These conditions
+ * are prevented by CREATE FUNCTION and is therefore not expected here.
* 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
* argument's actual type as the function's return type.
- * 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any
- * argument is ANYARRAY, use the actual type of the argument to determine
- * the function's return type, i.e. the array type's corresponding
- * element type.
- * 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
- * generate an ERROR. This condition is prevented by CREATE FUNCTION
- * and is therefore not expected here.
+ * 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument
+ * is ANYARRAY or ANYRANGE, use the actual type of the argument to determine
+ * the function's return type; i.e. the array type's corresponding element
+ * type or the range type's corresponding subtype (or both, in which case
+ * they must match).
+ * 6) If return type is ANYELEMENT, no argument is ANYARRAY, ANYRANGE, or
+ * ANYELEMENT, generate an ERROR. This condition is prevented by CREATE
+ * FUNCTION and is therefore not expected here.
* 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
@@ -1441,9 +1481,10 @@ check_generic_type_consistency(Oid *actual_arg_types,
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
*
- * Domains over arrays match ANYARRAY arguments, and are immediately flattened
- * to their base type. (In particular, if the return type is also ANYARRAY,
- * we'll set it to the base type not the domain type.)
+ * 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.)
*
* 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
@@ -1473,7 +1514,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
bool have_unknowns = false;
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
+ Oid range_typeid = InvalidOid;
Oid array_typelem;
+ Oid range_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
@@ -1534,6 +1577,26 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
format_type_be(actual_type))));
array_typeid = actual_type;
}
+ else if (decl_type == ANYRANGEOID)
+ {
+ have_generics = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(range_typeid) && actual_type != range_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anyrange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(range_typeid),
+ format_type_be(actual_type))));
+ range_typeid = actual_type;
+ }
}
/*
@@ -1579,6 +1642,34 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
format_type_be(elem_typeid))));
}
}
+ /* Get the element type based on the range type, if we have one */
+ else if (OidIsValid(range_typeid))
+ {
+ range_typelem = get_range_subtype(range_typeid);
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyrange\" is not a range but type %s",
+ format_type_be(range_typeid))));
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyrange\" is not consistent with argument declared \"anyelement\""),
+ errdetail("%s versus %s",
+ format_type_be(range_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
else if (!OidIsValid(elem_typeid))
{
if (allow_poly)
@@ -1645,6 +1736,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
}
declared_arg_types[j] = array_typeid;
}
+ else if (decl_type == ANYRANGEOID)
+ {
+ 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))));
+ }
+ declared_arg_types[j] = range_typeid;
+ }
}
}
@@ -1663,6 +1765,19 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
return array_typeid;
}
+ /* if we return ANYRANGE use the appropriate argument type */
+ if (rettype == ANYRANGEOID)
+ {
+ 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))));
+ }
+ return range_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -1711,7 +1826,8 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID)
+ context_declared_type == ANYENUMOID ||
+ context_declared_type == ANYRANGEOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -1726,7 +1842,8 @@ resolve_generic_type(Oid declared_type,
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID)
+ declared_type == ANYENUMOID ||
+ declared_type == ANYRANGEOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -1741,6 +1858,18 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_base_type))));
return array_typelem;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid range_typelem = get_range_subtype(context_actual_type);
+
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyrange\" is not a range but type %s",
+ format_type_be(context_actual_type))));
+ return range_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -1854,6 +1983,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_enum(srctype))
return true;
+ /* Also accept any range type as coercible to ANYRANGE */
+ if (targettype == ANYRANGEOID)
+ if (type_is_range(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))