diff options
Diffstat (limited to 'src')
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]) |