diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index ed0ee584c9..f9d10204e3 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -4761,6 +4761,14 @@ SELECT * FROM pg_attribute anyrange + + commontype + + + + commontypearray + + void @@ -4870,6 +4878,34 @@ SELECT * FROM pg_attribute ). + + commontype + Indicates that a function accepts any data type. Values + are converted to real common type. + (see ). + + + + commontypearray + Indicates that a function accepts any array data type. The + elements of array are converted to common type of these values. + (see ). + + + + commontypenonarray + Indicates that a function accepts any non-array data type + (see ). + + + + commontyperange + Indicates that a function accepts any range data type + (see and + ). The subtype can be used for + deduction of common type. + + cstring Indicates that a function accepts or returns a null-terminated C string. diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index a6b77c1cfe..dece346c03 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -231,7 +231,7 @@ Five pseudo-types of special interest are anyelement, anyarray, anynonarray, anyenum, - and anyrange, + anyrange, commontype and commontypearray. which are collectively called polymorphic types. Any function declared using these types is said to be a polymorphic function. A polymorphic function can @@ -267,6 +267,15 @@ be an enum type. + + Second family of polymorphic types are types commontype and + commontypearray. These types are similar to types + anyelement and anyarray. The arguments declared + as anyelement requires same real type of passed values. For + commontype's arguments is selected common type, and later + all these arguments are casted to this common type. + + Thus, when more than one argument position is declared with a polymorphic type, the net effect is that only certain combinations of actual argument diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index cc3806e85d..d5013ec12d 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -133,7 +133,7 @@ AggregateCreate(const char *aggName, hasInternalArg = false; for (i = 0; i < numArgs; i++) { - if (IsPolymorphicType(aggArgTypes[i])) + if (IsPolymorphicTypeAny(aggArgTypes[i])) hasPolyArg = true; else if (aggArgTypes[i] == INTERNALOID) hasInternalArg = true; @@ -143,7 +143,7 @@ AggregateCreate(const char *aggName, * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ - if (IsPolymorphicType(aggTransType) && !hasPolyArg) + if (IsPolymorphicTypeAny(aggTransType) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), @@ -153,7 +153,7 @@ AggregateCreate(const char *aggName, * Likewise for moving-aggregate transtype, if any */ if (OidIsValid(aggmTransType) && - IsPolymorphicType(aggmTransType) && !hasPolyArg) + IsPolymorphicTypeAny(aggmTransType) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), @@ -489,7 +489,7 @@ AggregateCreate(const char *aggName, * that itself violates the rule against polymorphic result with no * polymorphic input.) */ - if (IsPolymorphicType(finaltype) && !hasPolyArg) + if (IsPolymorphicTypeAny(finaltype) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index db780616e6..1e98db47ee 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -102,6 +102,10 @@ ProcedureCreate(const char *procedureName, bool anyrangeOutParam = false; bool internalInParam = false; bool internalOutParam = false; + bool commonInParam = false; + bool commonOutParam = false; + bool commonrangeInParam = false; + bool commonrangeOutParam = false; Oid variadicType = InvalidOid; Acl *proacl = NULL; Relation rel; @@ -197,6 +201,15 @@ ProcedureCreate(const char *procedureName, case INTERNALOID: internalInParam = true; break; + case COMMONTYPEOID: + case COMMONTYPEARRAYOID: + case COMMONTYPENONARRAYOID: + commonInParam = true; + break; + case COMMONTYPERANGEOID: + commonInParam = true; + commonrangeInParam = true; + break; } } @@ -224,6 +237,15 @@ ProcedureCreate(const char *procedureName, case INTERNALOID: internalOutParam = true; break; + case COMMONTYPEOID: + case COMMONTYPEARRAYOID: + case COMMONTYPENONARRAYOID: + commonOutParam = true; + break; + case COMMONTYPERANGEOID: + commonOutParam = true; + commonrangeOutParam = true; + break; } } } @@ -235,13 +257,20 @@ ProcedureCreate(const char *procedureName, * ANYELEMENT). Also, do not allow return type INTERNAL unless at least * one input argument is INTERNAL. */ - if ((IsPolymorphicType(returnType) || genericOutParam) + if ((IsPolymorphicTypeAny(returnType) || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); + if ((IsPolymorphicTypeCommon(returnType) || commonOutParam) + && !commonInParam) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("cannot determine result data type"), + errdetail("A function returning \"commontype\" or \"commontypearray\" must have at least one \"commontype\" or \"commontypearray\" argument."))); + if ((returnType == ANYRANGEOID || anyrangeOutParam) && !anyrangeInParam) ereport(ERROR, @@ -249,6 +278,13 @@ ProcedureCreate(const char *procedureName, errmsg("cannot determine result data type"), errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument."))); + if ((returnType == COMMONTYPERANGEOID || commonrangeOutParam) && + !commonrangeInParam) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("cannot determine result data type"), + errdetail("A function returning \"commontyperange\" must have at least one \"commontyperange\" argument."))); + if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), @@ -286,6 +322,9 @@ ProcedureCreate(const char *procedureName, case ANYARRAYOID: variadicType = ANYELEMENTOID; break; + case COMMONTYPEARRAYOID: + variadicType = COMMONTYPEOID; + break; default: variadicType = get_element_type(allParams[i]); if (!OidIsValid(variadicType)) diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index d00765fbc7..b64fe12adf 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -334,7 +334,7 @@ DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List transTypeId = typenameTypeId(NULL, transType); transTypeType = get_typtype(transTypeId); if (transTypeType == TYPTYPE_PSEUDO && - !IsPolymorphicType(transTypeId)) + !IsPolymorphicTypeAny(transTypeId)) { if (transTypeId == INTERNALOID && superuser()) /* okay */ ; @@ -375,7 +375,7 @@ DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List mtransTypeId = typenameTypeId(NULL, mtransType); mtransTypeType = get_typtype(mtransTypeId); if (mtransTypeType == TYPTYPE_PSEUDO && - !IsPolymorphicType(mtransTypeId)) + !IsPolymorphicTypeAny(mtransTypeId)) { if (mtransTypeId == INTERNALOID && superuser()) /* okay */ ; diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index ac401689c8..88d099c2a0 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 COMMONTYPEARRAYOID: case ANYOID: /* okay */ break; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 5b2b8d2969..59d31ab615 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -256,7 +256,7 @@ CheckIndexCompatible(Oid oldId, irel = index_open(oldId, AccessShareLock); /* caller probably has a lock */ for (i = 0; i < old_natts; i++) { - if (IsPolymorphicType(get_opclass_input_type(classObjectId[i])) && + if (IsPolymorphicTypeAny(get_opclass_input_type(classObjectId[i])) && TupleDescAttr(irel->rd_att, i)->atttypid != typeObjectId[i]) { ret = false; @@ -284,7 +284,7 @@ CheckIndexCompatible(Oid oldId, right; op_input_types(indexInfo->ii_ExclusionOps[i], &left, &right); - if ((IsPolymorphicType(left) || IsPolymorphicType(right)) && + if ((IsPolymorphicTypeAny(left) || IsPolymorphicTypeAny(right)) && TupleDescAttr(irel->rd_att, i)->atttypid != typeObjectId[i]) { ret = false; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ff76499137..0f3db83d32 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -7586,7 +7586,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, */ old_check_ok = (new_pathtype == old_pathtype && new_castfunc == old_castfunc && - (!IsPolymorphicType(pfeqop_right) || + (!IsPolymorphicTypeAny(pfeqop_right) || new_fktype == old_fktype)); } diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 6e134ae1d2..50a5a69e3b 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -499,7 +499,7 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation) * For a polymorphic-input-type opclass, just keep the same exposed type. * RECORD opclasses work like polymorphic-type ones for this purpose. */ - if (IsPolymorphicType(req_type) || req_type == RECORDOID) + if (IsPolymorphicTypeAny(req_type) || req_type == RECORDOID) req_type = expr_type; /* diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 8ed3816866..a0856181e9 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -1870,7 +1870,7 @@ resolve_aggregate_transtype(Oid aggfuncid, int numArguments) { /* resolve actual type of transition state, if polymorphic */ - if (IsPolymorphicType(aggtranstype)) + if (IsPolymorphicTypeAny(aggtranstype)) { /* have to fetch the agg's declared input types... */ Oid *declaredArgTypes; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 3840252a5b..eb461c0079 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -169,7 +169,9 @@ coerce_type(ParseState *pstate, Node *node, } if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || - targetTypeId == ANYNONARRAYOID) + targetTypeId == ANYNONARRAYOID || + targetTypeId == COMMONTYPEOID || + targetTypeId == COMMONTYPENONARRAYOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. @@ -187,7 +189,9 @@ coerce_type(ParseState *pstate, Node *node, } if (targetTypeId == ANYARRAYOID || targetTypeId == ANYENUMOID || - targetTypeId == ANYRANGEOID) + targetTypeId == ANYRANGEOID || + targetTypeId == COMMONTYPEARRAYOID || + targetTypeId == COMMONTYPERANGEOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. @@ -1389,6 +1393,100 @@ select_common_type(ParseState *pstate, List *exprs, const char *context, return ptype; } +/* + * select_common_type_from_vector() + * Determine the common supertype of vector of Oids. + * + * Similar to select_common_type() but simplified for polymorphics + * type processing. When there are no supertype, then returns InvalidOid, + * when noerror is true, or raise exception when noerror is false. + */ +static Oid +select_common_type_from_vector(int nargs, Oid *typeids, bool noerror) +{ + int i = 0; + Oid ptype; + TYPCATEGORY pcategory; + bool pispreferred; + + Assert(nargs > 0); + ptype = typeids[0]; + + /* fast leave when all types are same */ + if (ptype != UNKNOWNOID) + { + for (i = 1; i < nargs; i++) + { + if (ptype != typeids[i]) + break; + } + + if (i == nargs) + return ptype; + } + + /* + * Nope, so set up for the full algorithm. Note that at this point, lc + * points to the first list item with type different from pexpr's; we need + * not re-examine any items the previous loop advanced over. + */ + 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) + { + if (noerror) + return InvalidOid; + + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("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; + } + } + } + + /* + * Be consistent with select_common_type() + */ + if (ptype == UNKNOWNOID) + ptype = TEXTOID; + + return ptype; +} + /* * coerce_to_common_type() * Coerce an expression to the given type. @@ -1485,6 +1583,12 @@ check_generic_type_consistency(const Oid *actual_arg_types, bool have_anyelement = false; bool have_anynonarray = false; bool have_anyenum = false; + bool have_commontypenonarray = false; + bool have_commontype_range = false; + bool have_generic_common = false; + Oid commontype_range_typeid = InvalidOid; + Oid commontype_actual_types[FUNC_MAX_ARGS]; + int n_commontype_args = 0; /* * Loop through the arguments to see if we have any that are polymorphic. @@ -1528,6 +1632,72 @@ check_generic_type_consistency(const Oid *actual_arg_types, return false; range_typeid = actual_type; } + else if (decl_type == COMMONTYPEOID || + decl_type == COMMONTYPENONARRAYOID) + { + have_generic_common = true; + if (decl_type == COMMONTYPENONARRAYOID) + have_commontypenonarray = true; + if (actual_type == UNKNOWNOID) + continue; + + /* collect used type, reduce repeated values * */ + if (n_commontype_args == 0 || + commontype_actual_types[n_commontype_args - 1] != actual_type) + commontype_actual_types[n_commontype_args++] = actual_type; + } + else if (decl_type == COMMONTYPEARRAYOID) + { + Oid commontype_elem_type; + + have_generic_common = true; + + if (actual_type == UNKNOWNOID) + continue; + + actual_type = getBaseType(actual_type); /* flatten domains */ + commontype_elem_type = get_element_type(actual_type); + + if (!OidIsValid(commontype_elem_type)) + return false; + + /* collect used type, reduce repeated values * */ + if (n_commontype_args == 0 || + commontype_actual_types[n_commontype_args - 1] != commontype_elem_type) + commontype_actual_types[n_commontype_args++] = commontype_elem_type; + } + else if (decl_type == COMMONTYPERANGEOID) + { + Oid commontype_range_typelem; + + have_generic_common = true; + have_commontype_range = true; + + if (actual_type == UNKNOWNOID) + continue; + actual_type = getBaseType(actual_type); /* flatten domains */ + + /* + * range type is used just for derivation of common type, but + * range types should be same. Same behave like anyrange - cast + * between ranges are not supported. + */ + if (OidIsValid(commontype_range_typeid) && + commontype_range_typeid != actual_type) + return false; + + commontype_range_typelem = get_range_subtype(actual_type); + if (!OidIsValid(commontype_range_typelem)) + return false; + + if (!OidIsValid(commontype_range_typeid)) + commontype_range_typeid = actual_type; + + /* collect used type, reduce repeated values * */ + if (n_commontype_args == 0 || + commontype_actual_types[n_commontype_args - 1] != commontype_range_typelem) + commontype_actual_types[n_commontype_args++] = commontype_range_typelem; + } } /* Get the element type based on the array type, if we have one */ @@ -1594,6 +1764,37 @@ check_generic_type_consistency(const Oid *actual_arg_types, return false; } + /* check commontype collected data */ + if (have_generic_common) + { + if (n_commontype_args > 0) + { + Oid commontype_typeid; + + commontype_typeid = select_common_type_from_vector(n_commontype_args, + commontype_actual_types, + true); + + if (!OidIsValid(commontype_typeid)) + return false; + + if (have_commontypenonarray) + { + /* require the commontype type to not be an array or domain over array */ + if (type_is_array_domain(commontype_typeid)) + return false; + } + + if (have_commontype_range && !OidIsValid(commontype_range_typeid)) + return false; + + return true; + } + + /* is not possible derive common type */ + return false; + } + /* Looks valid */ return true; } @@ -1676,11 +1877,15 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, bool allow_poly) { int j; - bool have_generics = false; + bool have_generics_any = false; + bool have_generics_common = false; bool have_unknowns = false; Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; Oid range_typeid = InvalidOid; + Oid commontype_typeid = InvalidOid; + Oid commontype_array_typeid = InvalidOid; + Oid commontype_range_typeid = InvalidOid; Oid array_typelem; Oid range_typelem; bool have_anyelement = (rettype == ANYELEMENTOID || @@ -1688,6 +1893,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, rettype == ANYENUMOID); bool have_anynonarray = (rettype == ANYNONARRAYOID); bool have_anyenum = (rettype == ANYENUMOID); + bool have_commontype_nonarray = (rettype == COMMONTYPENONARRAYOID); + bool have_commontype_array = (rettype == COMMONTYPEARRAYOID); + bool have_commontype_range = (rettype == COMMONTYPERANGEOID); + Oid commontype_actual_types[FUNC_MAX_ARGS]; + int n_commontype_args = 0; /* * Loop through the arguments to see if we have any that are polymorphic. @@ -1702,7 +1912,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, decl_type == ANYNONARRAYOID || decl_type == ANYENUMOID) { - have_generics = have_anyelement = true; + have_generics_any = have_anyelement = true; if (decl_type == ANYNONARRAYOID) have_anynonarray = true; else if (decl_type == ANYENUMOID) @@ -1725,14 +1935,18 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, } else if (decl_type == ANYARRAYOID) { - have_generics = true; + have_generics_any = true; + have_commontype_array = true; + if (actual_type == UNKNOWNOID) { have_unknowns = true; continue; } + if (allow_poly && decl_type == actual_type) continue; /* no new information here */ + actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(array_typeid) && actual_type != array_typeid) ereport(ERROR, @@ -1745,7 +1959,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, } else if (decl_type == ANYRANGEOID) { - have_generics = true; + have_generics_any = true; if (actual_type == UNKNOWNOID) { have_unknowns = true; @@ -1763,128 +1977,289 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, format_type_be(actual_type)))); range_typeid = actual_type; } + else if (decl_type == COMMONTYPEOID || + decl_type == COMMONTYPENONARRAYOID) + { + have_generics_common = true; + + if (decl_type == COMMONTYPENONARRAYOID) + have_commontype_nonarray = true; + /* + * because declared type will be replaced every time, + * we don't need some special work for unknown types. + */ + if (actual_type == UNKNOWNOID) + continue; + + if (allow_poly && decl_type == actual_type) + continue; + + /* collect used type, reduce repeated values * */ + if (n_commontype_args == 0 || + commontype_actual_types[n_commontype_args - 1] != actual_type) + commontype_actual_types[n_commontype_args++] = actual_type; + } + else if (decl_type == COMMONTYPEARRAYOID) + { + Oid commontype_elem_type; + + have_generics_common = true; + have_commontype_array = true; + + if (actual_type == UNKNOWNOID) + continue; + + if (allow_poly && decl_type == actual_type) + continue; + + actual_type = getBaseType(actual_type); /* flatten domains */ + commontype_elem_type = get_element_type(actual_type); + + if (!OidIsValid(commontype_elem_type)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not an array but type %s", + "anyarray", format_type_be(actual_type)))); + + /* collect used type, reduce repeated values * */ + if (n_commontype_args == 0 || + commontype_actual_types[n_commontype_args - 1] != commontype_elem_type) + commontype_actual_types[n_commontype_args++] = commontype_elem_type; + } + else if (decl_type == COMMONTYPERANGEOID) + { + Oid commontype_range_typelem; + + have_generics_common = true; + have_commontype_range = true; + + if (actual_type == UNKNOWNOID) + { + have_unknowns = true; + continue; + } + if (allow_poly && decl_type == actual_type) + continue; /* no new information here */ + actual_type = getBaseType(actual_type); /* flatten domains */ + + if (OidIsValid(commontype_range_typeid) && + actual_type != commontype_range_typeid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("arguments declared \"anyrange\" are not all alike"), + errdetail("%s versus %s", + format_type_be(commontype_range_typeid), + format_type_be(actual_type)))); + + commontype_range_typeid = actual_type; + commontype_range_typelem = get_range_subtype(commontype_range_typeid); + + if (!OidIsValid(commontype_range_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not a range type but type %s", + "anyrange", + format_type_be(commontype_range_typeid)))); + + /* collect used type, reduce repeated values * */ + if (n_commontype_args == 0 || + commontype_actual_types[n_commontype_args - 1] != commontype_range_typelem) + commontype_actual_types[n_commontype_args++] = commontype_range_typelem; + } } /* * Fast Track: if none of the arguments are polymorphic, return the * unmodified rettype. We assume it can't be polymorphic either. */ - if (!have_generics) + if (!have_generics_any && !have_generics_common) return rettype; - /* Get the element type based on the array type, if we have one */ - if (OidIsValid(array_typeid)) + if (have_generics_any) { - if (array_typeid == ANYARRAYOID && !have_anyelement) + /* Get the element type based on the array type, if we have one */ + if (OidIsValid(array_typeid)) { - /* Special case for ANYARRAY input: okay iff no ANYELEMENT */ - array_typelem = ANYELEMENTOID; + if (array_typeid == ANYARRAYOID && !have_anyelement) + { + /* Special case for ANYARRAY input: okay iff no ANYELEMENT */ + array_typelem = ANYELEMENTOID; + } + else + { + array_typelem = get_element_type(array_typeid); + if (!OidIsValid(array_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not an array but type %s", + "anyarray", format_type_be(array_typeid)))); + } + + if (!OidIsValid(elem_typeid)) + { + /* + * if we don't have an element type yet, use the one we just got + */ + elem_typeid = array_typelem; + } + else if (array_typelem != elem_typeid) + { + /* otherwise, they better match */ + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not consistent with argument declared %s", + "anyarray", "anyelement"), + errdetail("%s versus %s", + format_type_be(array_typeid), + format_type_be(elem_typeid)))); + } } - else + + /* Get the element type based on the range type, if we have one */ + if (OidIsValid(range_typeid)) { - array_typelem = get_element_type(array_typeid); - if (!OidIsValid(array_typelem)) + if (range_typeid == ANYRANGEOID && !have_anyelement) + { + /* Special case for ANYRANGE input: okay iff no ANYELEMENT */ + range_typelem = ANYELEMENTOID; + } + else + { + range_typelem = get_range_subtype(range_typeid); + if (!OidIsValid(range_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not a range type but type %s", + "anyrange", + format_type_be(range_typeid)))); + } + + if (!OidIsValid(elem_typeid)) + { + /* + * if we don't have an element type yet, use the one we just got + */ + elem_typeid = range_typelem; + } + else if (range_typelem != elem_typeid) + { + /* otherwise, they better match */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared %s is not an array but type %s", - "anyarray", format_type_be(array_typeid)))); + errmsg("argument declared %s is not consistent with argument declared %s", + "anyrange", "anyelement"), + errdetail("%s versus %s", + format_type_be(range_typeid), + format_type_be(elem_typeid)))); + } } if (!OidIsValid(elem_typeid)) { - /* - * if we don't have an element type yet, use the one we just got - */ - elem_typeid = array_typelem; - } - else if (array_typelem != elem_typeid) - { - /* otherwise, they better match */ - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared %s is not consistent with argument declared %s", - "anyarray", "anyelement"), - errdetail("%s versus %s", - format_type_be(array_typeid), - format_type_be(elem_typeid)))); + if (allow_poly) + { + elem_typeid = ANYELEMENTOID; + array_typeid = ANYARRAYOID; + range_typeid = ANYRANGEOID; + } + else + { + /* Only way to get here is if all the generic args are UNKNOWN */ + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not determine polymorphic type because input has type %s", + "unknown"))); + } } - } - /* Get the element type based on the range type, if we have one */ - if (OidIsValid(range_typeid)) - { - if (range_typeid == ANYRANGEOID && !have_anyelement) + if (have_anynonarray && elem_typeid != ANYELEMENTOID) { - /* Special case for ANYRANGE input: okay iff no ANYELEMENT */ - range_typelem = ANYELEMENTOID; - } - else - { - range_typelem = get_range_subtype(range_typeid); - if (!OidIsValid(range_typelem)) + /* require the element type to not be an array or domain over array */ + if (type_is_array_domain(elem_typeid)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared %s is not a range type but type %s", - "anyrange", - format_type_be(range_typeid)))); + errmsg("type matched to anynonarray is an array type: %s", + format_type_be(elem_typeid)))); } - if (!OidIsValid(elem_typeid)) - { - /* - * if we don't have an element type yet, use the one we just got - */ - elem_typeid = range_typelem; - } - else if (range_typelem != elem_typeid) + if (have_anyenum && elem_typeid != ANYELEMENTOID) { - /* otherwise, they better match */ - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared %s is not consistent with argument declared %s", - "anyrange", "anyelement"), - errdetail("%s versus %s", - format_type_be(range_typeid), - format_type_be(elem_typeid)))); + /* require the element type to be an enum */ + if (!type_is_enum(elem_typeid)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type matched to anyenum is not an enum type: %s", + format_type_be(elem_typeid)))); } } - if (!OidIsValid(elem_typeid)) + if (have_generics_common) { - if (allow_poly) + if (n_commontype_args > 0) { - elem_typeid = ANYELEMENTOID; - array_typeid = ANYARRAYOID; - range_typeid = ANYRANGEOID; + commontype_typeid = select_common_type_from_vector(n_commontype_args, + commontype_actual_types, + false); + + if (have_commontype_array) + { + commontype_array_typeid = get_array_type(commontype_typeid); + + if (!OidIsValid(commontype_array_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(commontype_typeid)))); + } + + /* commontype_range_typid should be defined already */ + if (have_commontype_range && !OidIsValid(commontype_range_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find range type for data type %s", + "commontyperange"))); + + if (have_commontype_nonarray) + { + /* require the element type to not be an array or domain over array */ + if (type_is_array_domain(commontype_typeid)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type matched to anynonarray is an array type: %s", + format_type_be(commontype_typeid)))); + } } else { - /* Only way to get here is if all the generic args are UNKNOWN */ - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("could not determine polymorphic type because input has type %s", - "unknown"))); + if (allow_poly) + { + commontype_typeid = COMMONTYPEOID; + commontype_array_typeid = COMMONTYPEARRAYOID; + commontype_range_typeid = COMMONTYPERANGEOID; + } + else + { + /* Only way to get here is if all the generic args are UNKNOWN */ + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not determine polymorphic common type because input has type %s", + "unknown"))); + } } - } - if (have_anynonarray && elem_typeid != ANYELEMENTOID) - { - /* require the element type to not be an array or domain over array */ - if (type_is_array_domain(elem_typeid)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("type matched to anynonarray is an array type: %s", - format_type_be(elem_typeid)))); - } + /* replace polymorphic common types by selected common types */ + for (j = 0; j < nargs; j++) + { + Oid decl_type = declared_arg_types[j]; - if (have_anyenum && elem_typeid != ANYELEMENTOID) - { - /* require the element type to be an enum */ - if (!type_is_enum(elem_typeid)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("type matched to anyenum is not an enum type: %s", - format_type_be(elem_typeid)))); + if (decl_type == COMMONTYPEOID || + decl_type == COMMONTYPENONARRAYOID) + declared_arg_types[j] = commontype_typeid; + else if (decl_type == COMMONTYPEARRAYOID) + declared_arg_types[j] = commontype_array_typeid; + else if (decl_type == COMMONTYPERANGEOID) + declared_arg_types[j] = commontype_range_typeid; + } } /* @@ -1965,6 +2340,41 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, rettype == ANYENUMOID) return elem_typeid; + if (rettype == COMMONTYPEOID) + { + if (!OidIsValid(commontype_typeid)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find common type"))); + } + return commontype_typeid; + } + + if (rettype == COMMONTYPEARRAYOID) + { + if (!OidIsValid(commontype_array_typeid)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find common array type"))); + } + return commontype_array_typeid; + } + + /* if we return ANYRANGE use the appropriate argument type */ + if (rettype == COMMONTYPERANGEOID) + { + if (!OidIsValid(commontype_range_typeid)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find range type for data type %s", + "commontyperange"))); + } + return commontype_range_typeid; + } + /* we don't return a generic type; send back the original return type */ return rettype; } @@ -2060,6 +2470,75 @@ resolve_generic_type(Oid declared_type, return context_actual_type; } } + else if (declared_type == COMMONTYPEARRAYOID) + { + if (context_declared_type == COMMONTYPEARRAYOID) + { + /* + * Use actual type, but it must be an array; or if it's a domain + * over array, use the base array type. + */ + Oid context_base_type = getBaseType(context_actual_type); + Oid array_typelem = get_element_type(context_base_type); + + if (!OidIsValid(array_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not an array but type %s", + "anyarray", format_type_be(context_base_type)))); + return context_base_type; + } + else if (context_declared_type == COMMONTYPEOID || + context_declared_type == COMMONTYPENONARRAYOID || + context_declared_type == COMMONTYPERANGEOID) + { + /* Use the array type corresponding to actual type */ + Oid array_typeid = get_array_type(context_actual_type); + + if (!OidIsValid(array_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(context_actual_type)))); + return array_typeid; + } + } + else if (declared_type == COMMONTYPEOID || + declared_type == COMMONTYPENONARRAYOID) + { + if (context_declared_type == COMMONTYPEARRAYOID) + { + /* Use the element type corresponding to actual type */ + Oid context_base_type = getBaseType(context_actual_type); + Oid array_typelem = get_element_type(context_base_type); + + if (!OidIsValid(array_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not an array but type %s", + "commontypearray", format_type_be(context_base_type)))); + return array_typelem; + } + else if (context_declared_type == COMMONTYPERANGEOID) + { + /* Use the element type corresponding to actual type */ + Oid context_base_type = getBaseType(context_actual_type); + Oid range_typelem = get_range_subtype(context_base_type); + + if (!OidIsValid(range_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not a range type but type %s", + "anyrange", format_type_be(context_base_type)))); + return range_typelem; + } + else if (context_declared_type == COMMONTYPEOID || + context_declared_type == COMMONTYPENONARRAYOID) + { + /* Use the actual type; it doesn't matter if array or not */ + return context_actual_type; + } + } else { /* declared_type isn't polymorphic, so return it as-is */ @@ -2142,8 +2621,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 COMMONTYPE */ + if (targettype == ANYOID || targettype == ANYELEMENTOID || + targettype == COMMONTYPEARRAYOID) return true; /* If srctype is a domain, reduce to its base type */ @@ -2155,7 +2635,7 @@ IsBinaryCoercible(Oid srctype, Oid targettype) return true; /* Also accept any array type as coercible to ANYARRAY */ - if (targettype == ANYARRAYOID) + if (targettype == ANYARRAYOID || targettype == COMMONTYPEARRAYOID) if (type_is_array(srctype)) return true; diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 24f0d5c08c..99ca904b8e 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -953,7 +953,7 @@ make_scalar_array_op(ParseState *pstate, List *opname, * enforce_generic_type_consistency may or may not have replaced a * polymorphic type with a real one. */ - if (IsPolymorphicType(declared_arg_types[1])) + if (IsPolymorphicTypeAny(declared_arg_types[1])) { /* assume the actual array type is OK */ res_atypeId = atypeId; diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index f21c9b32a6..aee4b5aed0 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -1787,7 +1787,7 @@ get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, */ *need_relabel = (key->parttypid[col] != key->partopcintype[col] && key->partopcintype[col] != RECORDOID && - !IsPolymorphicType(key->partopcintype[col])); + !IsPolymorphicTypeAny(key->partopcintype[col])); return operoid; } diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index de0d0723b7..bf165dbc0c 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -1399,7 +1399,7 @@ json_categorize_type(Oid typoid, default: /* Check for arrays and composites */ if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID - || typoid == RECORDARRAYOID) + || typoid == COMMONTYPEARRAYOID || 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 c02c8569f2..aa9eab48f8 100644 --- a/src/backend/utils/adt/jsonb.c +++ b/src/backend/utils/adt/jsonb.c @@ -648,7 +648,7 @@ jsonb_categorize_type(Oid typoid, default: /* Check for arrays and composites */ if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID - || typoid == RECORDARRAYOID) + || typoid == COMMONTYPEARRAYOID || 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 6194dcd2fe..f84a80590e 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -135,6 +135,33 @@ anyarray_send(PG_FUNCTION_ARGS) return array_send(fcinfo); } +/* + * commontypearray_recv - binary input routine for pseudo-type COMMONARRAY. + * + * XXX this could actually be made to work, since the incoming array + * data will contain the element type OID. Need to think through + * type-safety issues before allowing it, however. + */ +Datum +commontypearray_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "commontypearray"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * commontypearray_send - binary output routine for pseudo-type COMMONTYPEARRAY. + * + * We may as well allow this, since array_send will in fact work. + */ +Datum +commontypearray_send(PG_FUNCTION_ARGS) +{ + return array_send(fcinfo); +} /* * anyenum_in - input routine for pseudo-type ANYENUM. @@ -184,6 +211,30 @@ anyrange_out(PG_FUNCTION_ARGS) return range_out(fcinfo); } +/* + * commontyperange_in - input routine for pseudo-type COMMONTYPERANGE. + */ +Datum +commontyperange_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "commontyperange"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * commontyperange_out - output routine for pseudo-type COMMONTYPERANGE. + * + * We may as well allow this, since range_out will in fact work. + */ +Datum +commontyperange_out(PG_FUNCTION_ARGS) +{ + return range_out(fcinfo); +} + /* * void_in - input routine for pseudo-type VOID. * @@ -418,3 +469,6 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal); PSEUDOTYPE_DUMMY_IO_FUNCS(opaque); PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement); PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray); +PSEUDOTYPE_DUMMY_IO_FUNCS(commontype); +PSEUDOTYPE_DUMMY_IO_FUNCS(commontypearray); +PSEUDOTYPE_DUMMY_IO_FUNCS(commontypenonarray); diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index a5e77a208e..9f8200a947 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -440,10 +440,18 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, bool have_anyrange_result = false; bool have_anynonarray = false; bool have_anyenum = false; + bool have_commontype_result = false; + bool have_commontypearray_result = false; + bool have_commontyperange_result = false; + bool have_commontypenonarray = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; Oid anyrange_type = InvalidOid; + Oid commontype_type = InvalidOid; + Oid commontypearray_type = InvalidOid; + Oid commontyperange_type = InvalidOid; Oid anycollation = InvalidOid; + Oid ctcollation = InvalidOid; int i; /* See if there are any polymorphic outputs; quick out if not */ @@ -468,12 +476,27 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, case ANYRANGEOID: have_anyrange_result = true; break; + case COMMONTYPEOID: + have_commontype_result = true; + break; + case COMMONTYPEARRAYOID: + have_commontypearray_result = true; + break; + case COMMONTYPENONARRAYOID: + have_commontype_result = true; + have_commontypenonarray = true; + break; + case COMMONTYPERANGEOID: + have_commontyperange_result = true; + break; default: break; } } if (!have_anyelement_result && !have_anyarray_result && - !have_anyrange_result) + !have_anyrange_result && + !have_commontype_result && !have_commontypearray_result && + !have_commontyperange_result) return true; /* @@ -501,14 +524,29 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, if (!OidIsValid(anyrange_type)) anyrange_type = get_call_expr_argtype(call_expr, i); break; + case COMMONTYPEOID: + case COMMONTYPENONARRAYOID: + if (!OidIsValid(commontype_type)) + commontype_type = get_call_expr_argtype(call_expr, i); + break; + case COMMONTYPEARRAYOID: + if (!OidIsValid(commontypearray_type)) + commontypearray_type = get_call_expr_argtype(call_expr, i); + break; + case COMMONTYPERANGEOID: + if (!OidIsValid(commontyperange_type)) + commontyperange_type = get_call_expr_argtype(call_expr, i); + break; default: break; } } /* If nothing found, parser messed up */ - if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) && - !OidIsValid(anyrange_type)) + if ((have_anyelement_result || have_anyarray_result || + have_anyrange_result) && + (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) && + !OidIsValid(anyrange_type))) return false; /* If needed, deduce one polymorphic type from others */ @@ -536,6 +574,31 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, anyelement_type, ANYELEMENTOID); + if (have_commontypearray_result && !OidIsValid(commontypearray_type)) + commontypearray_type = resolve_generic_type(COMMONTYPEARRAYOID, + commontype_type, + COMMONTYPEOID); + + if (have_commontype_result && !OidIsValid(commontype_type)) + { + if (OidIsValid(commontypearray_type)) + commontype_type = resolve_generic_type(COMMONTYPEOID, + commontypearray_type, + COMMONTYPEARRAYOID); + + if (OidIsValid(commontyperange_type)) + { + Oid subtype = resolve_generic_type(COMMONTYPEOID, + commontyperange_type, + COMMONTYPERANGEOID); + + /* check for inconsistent array and range results */ + if (OidIsValid(commontype_type) && commontype_type != subtype) + return false; + commontype_type = subtype; + } + } + /* * We can't deduce a range type from other polymorphic inputs, because * there may be multiple range types for the same subtype. @@ -543,10 +606,17 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, if (have_anyrange_result && !OidIsValid(anyrange_type)) return false; + if (have_commontyperange_result && !OidIsValid(commontyperange_type)) + return false; + /* Enforce ANYNONARRAY if needed */ if (have_anynonarray && type_is_array(anyelement_type)) return false; + /* Enforce COMMONTYPENONARRAY if needed */ + if (have_commontypenonarray && type_is_array(commontype_type)) + return false; + /* Enforce ANYENUM if needed */ if (have_anyenum && !type_is_enum(anyelement_type)) return false; @@ -562,7 +632,12 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, else if (OidIsValid(anyarray_type)) anycollation = get_typcollation(anyarray_type); - if (OidIsValid(anycollation)) + if (OidIsValid(commontype_type)) + ctcollation = get_typcollation(commontype_type); + else if (OidIsValid(commontypearray_type)) + ctcollation = get_typcollation(commontypearray_type); + + if (OidIsValid(anycollation) || OidIsValid(ctcollation)) { /* * The types are collatable, so consider whether to use a nondefault @@ -573,6 +648,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, if (OidIsValid(inputcollation)) anycollation = inputcollation; + + if (OidIsValid(inputcollation)) + ctcollation = inputcollation; } /* And finally replace the tuple column types as needed */ @@ -608,6 +686,31 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, 0); /* no collation should be attached to a range type */ break; + case COMMONTYPEOID: + case COMMONTYPENONARRAYOID: + TupleDescInitEntry(tupdesc, i + 1, + NameStr(att->attname), + commontype_type, + -1, + 0); + TupleDescInitEntryCollation(tupdesc, i + 1, ctcollation); + break; + case COMMONTYPEARRAYOID: + TupleDescInitEntry(tupdesc, i + 1, + NameStr(att->attname), + commontypearray_type, + -1, + 0); + TupleDescInitEntryCollation(tupdesc, i + 1, ctcollation); + break; + case COMMONTYPERANGEOID: + TupleDescInitEntry(tupdesc, i + 1, + NameStr(att->attname), + commontyperange_type, + -1, + 0); + /* no collation should be attached to a range type */ + break; default: break; } @@ -632,9 +735,15 @@ 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_commontype_result = false; + bool have_commontypearray_result = false; + bool have_commontyperange_result = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; Oid anyrange_type = InvalidOid; + Oid commontype_type = InvalidOid; + Oid commontypearray_type = InvalidOid; + Oid commontyperange_type = InvalidOid; int inargno; int i; @@ -693,6 +802,52 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, argtypes[i] = anyrange_type; } break; + case COMMONTYPEOID: + case COMMONTYPENONARRAYOID: + if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) + have_commontype_result = true; + else + { + if (!OidIsValid(commontype_type)) + { + commontype_type = get_call_expr_argtype(call_expr, + inargno); + if (!OidIsValid(commontype_type)) + return false; + } + argtypes[i] = commontype_type; + } + break; + case COMMONTYPEARRAYOID: + if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) + have_commontypearray_result = true; + else + { + if (!OidIsValid(commontypearray_type)) + { + commontypearray_type = get_call_expr_argtype(call_expr, + inargno); + if (!OidIsValid(commontypearray_type)) + return false; + } + argtypes[i] = commontypearray_type; + } + break; + case COMMONTYPERANGEOID: + if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) + have_commontyperange_result = true; + else + { + if (!OidIsValid(commontyperange_type)) + { + commontyperange_type = get_call_expr_argtype(call_expr, + inargno); + if (!OidIsValid(commontyperange_type)) + return false; + } + argtypes[i] = commontyperange_type; + } + break; default: break; } @@ -702,47 +857,94 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, /* Done? */ if (!have_anyelement_result && !have_anyarray_result && - !have_anyrange_result) + !have_anyrange_result && + !have_commontype_result && !have_commontypearray_result) return true; - /* If no input polymorphics, parser messed up */ - if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) && - !OidIsValid(anyrange_type)) - return false; - - /* If needed, deduce one polymorphic type from others */ - if (have_anyelement_result && !OidIsValid(anyelement_type)) + if (have_anyelement_result || have_anyarray_result || have_anyrange_result) { - if (OidIsValid(anyarray_type)) - anyelement_type = resolve_generic_type(ANYELEMENTOID, - anyarray_type, - ANYARRAYOID); - if (OidIsValid(anyrange_type)) - { - Oid subtype = resolve_generic_type(ANYELEMENTOID, - anyrange_type, - ANYRANGEOID); + /* If no input polymorphics, parser messed up */ + if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) && + !OidIsValid(anyrange_type)) + return false; - /* check for inconsistent array and range results */ - if (OidIsValid(anyelement_type) && anyelement_type != subtype) - return false; - anyelement_type = subtype; + /* If needed, deduce one polymorphic type from others */ + if (have_anyelement_result && !OidIsValid(anyelement_type)) + { + if (OidIsValid(anyarray_type)) + anyelement_type = resolve_generic_type(ANYELEMENTOID, + anyarray_type, + ANYARRAYOID); + if (OidIsValid(anyrange_type)) + { + Oid subtype = resolve_generic_type(ANYELEMENTOID, + anyrange_type, + ANYRANGEOID); + + /* check for inconsistent array and range results */ + if (OidIsValid(anyelement_type) && anyelement_type != subtype) + return false; + anyelement_type = subtype; + } } + + if (have_anyarray_result && !OidIsValid(anyarray_type)) + anyarray_type = resolve_generic_type(ANYARRAYOID, + anyelement_type, + ANYELEMENTOID); + + /* + * We can't deduce a range type from other polymorphic inputs, because + * there may be multiple range types for the same subtype. + */ + if (have_anyrange_result && !OidIsValid(anyrange_type)) + return false; + + /* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */ } - if (have_anyarray_result && !OidIsValid(anyarray_type)) - anyarray_type = resolve_generic_type(ANYARRAYOID, - anyelement_type, - ANYELEMENTOID); + if (have_commontype_result || have_commontypearray_result || + have_commontyperange_result) + { + /* If no input polymorphics, parser messed up */ + if (!OidIsValid(commontype_type) && !OidIsValid(commontypearray_type) && + !OidIsValid(commontyperange_type)) + return false; - /* - * We can't deduce a range type from other polymorphic inputs, because - * there may be multiple range types for the same subtype. - */ - if (have_anyrange_result && !OidIsValid(anyrange_type)) - return false; + if (have_commontype_result && !OidIsValid(commontype_type)) + { + if (OidIsValid(commontypearray_type)) + { + commontype_type = resolve_generic_type(COMMONTYPEOID, + commontypearray_type, + COMMONTYPEARRAYOID); + + if (OidIsValid(commontyperange_type)) + { + Oid subtype = resolve_generic_type(COMMONTYPEOID, + anyrange_type, + COMMONTYPERANGEOID); + + /* check for inconsistent array and range results */ + if (OidIsValid(commontype_type) && commontype_type != subtype) + return false; + commontype_type = subtype; + } + } + } - /* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */ + if (have_commontypearray_result || !OidIsValid(commontypearray_type)) + commontypearray_type = resolve_generic_type(COMMONTYPEARRAYOID, + commontype_type, + COMMONTYPEOID); + + /* + * We can't deduce a range type from other polymorphic inputs, because + * there may be multiple range types for the same subtype. + */ + if (have_commontyperange_result && !OidIsValid(commontyperange_type)) + return false; + } /* And finally replace the output column types as needed */ for (i = 0; i < numargs; i++) @@ -760,6 +962,16 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, case ANYRANGEOID: argtypes[i] = anyrange_type; break; + case COMMONTYPEOID: + case COMMONTYPENONARRAYOID: + argtypes[i] = commontype_type; + break; + case COMMONTYPEARRAYOID: + argtypes[i] = commontypearray_type; + break; + case COMMONTYPERANGEOID: + argtypes[i] = commontyperange_type; + break; default: break; } diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 3ecc2e12c3..3579257e03 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6952,6 +6952,12 @@ { oid => '2778', descr => 'I/O', proname => 'anynonarray_out', prorettype => 'cstring', proargtypes => 'anynonarray', prosrc => 'anynonarray_out' }, +{ oid => '4007', descr => 'I/O', + proname => 'commontypenonarray_in', prorettype => 'commontypenonarray', + proargtypes => 'cstring', prosrc => 'commontypenonarray_in' }, +{ oid => '4008', descr => 'I/O', + proname => 'commontypenonarray_out', prorettype => 'cstring', + proargtypes => 'commontypenonarray', prosrc => 'commontypenonarray_out' }, { oid => '3116', descr => 'I/O', proname => 'fdw_handler_in', proisstrict => 'f', prorettype => 'fdw_handler', proargtypes => 'cstring', prosrc => 'fdw_handler_in' }, @@ -6971,6 +6977,18 @@ { oid => '3312', descr => 'I/O', proname => 'tsm_handler_out', prorettype => 'cstring', proargtypes => 'tsm_handler', prosrc => 'tsm_handler_out' }, +{ oid => '4001', descr => 'I/O', + proname => 'commontype_in', prorettype => 'commontype', + proargtypes => 'cstring', prosrc => 'commontype_in' }, +{ oid => '4002', descr => 'I/O', + proname => 'commontype_out', prorettype => 'cstring', + proargtypes => 'commontype', prosrc => 'commontype_out' }, +{ oid => '4003', descr => 'I/O', + proname => 'commontypearray_in', prorettype => 'commontypearray', + proargtypes => 'cstring', prosrc => 'commontypearray_in' }, +{ oid => '4004', descr => 'I/O', + proname => 'commontypearray_out', prorettype => 'cstring', + proargtypes => 'commontypearray', prosrc => 'commontypearray_out' }, # tablesample method handlers { oid => '3313', descr => 'BERNOULLI tablesample method handler', @@ -7468,6 +7486,12 @@ { oid => '3447', descr => 'I/O', proname => 'macaddr8_send', prorettype => 'bytea', proargtypes => 'macaddr8', prosrc => 'macaddr8_send' }, +{ oid => '2462', descr => 'I/O', + proname => 'commontypearray_recv', provolatile => 's', prorettype => 'commontypearray', + proargtypes => 'internal', prosrc => 'commontypearray_recv' }, +{ oid => '2463', descr => 'I/O', + proname => 'commontypearray_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'commontypearray', prosrc => 'commontypearray_send' }, # System-view support functions with pretty-print option { oid => '2504', descr => 'source text of a rule with pretty-print option', @@ -9335,6 +9359,12 @@ { oid => '3833', descr => 'I/O', proname => 'anyrange_out', provolatile => 's', prorettype => 'cstring', proargtypes => 'anyrange', prosrc => 'anyrange_out' }, +{ oid => '4005', descr => 'I/O', + proname => 'commontyperange_in', provolatile => 's', prorettype => 'commontyperange', + proargtypes => 'cstring oid int4', prosrc => 'commontyperange_in' }, +{ oid => '4006', descr => 'I/O', + proname => 'commontyperange_out', provolatile => 's', prorettype => 'cstring', + proargtypes => 'commontyperange', prosrc => 'commontyperange_out' }, { oid => '3834', descr => 'I/O', proname => 'range_in', provolatile => 's', prorettype => 'anyrange', proargtypes => 'cstring oid int4', prosrc => 'range_in' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 4b7750d439..3a77a67981 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -585,5 +585,25 @@ typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p', typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out', typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' }, - +{ oid => '3996', descr => 'pseudo-type representing a polymorphic common type', + typname => 'commontype', typlen => '4', typbyval => 't', typtype => 'p', + typcategory => 'P', typinput => 'commontype_in', + typoutput => 'commontype_out', typreceive => '-', typsend => '-', + typalign => 'i' }, +{ oid => '3998', descr => 'pseudo-type representing a polymorphic array type of common type elements', + typname => 'commontypearray', typlen => '-1', typbyval => 'f', typtype => 'p', + typcategory => 'P', typinput => 'commontypearray_in', typoutput => 'commontypearray_out', + typreceive => 'commontypearray_recv', typsend => 'commontypearray_send', typalign => 'd', + typstorage => 'x' }, +{ oid => '4072', + descr => 'pseudo-type representing a polymorphic common type that is not an array', + typname => 'commontypenonarray', typlen => '4', typbyval => 't', typtype => 'p', + typcategory => 'P', typinput => 'commontypenonarray_in', + typoutput => 'commontypenonarray_out', typreceive => '-', typsend => '-', + typalign => 'i' }, +{ oid => '4073', + descr => 'pseudo-type representing a polymorphic common type that is a range', + typname => 'commontyperange', typlen => '-1', typbyval => 'f', typtype => 'p', + typcategory => 'P', typinput => 'commontyperange_in', typoutput => 'commontyperange_out', + typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' } ] diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 24d114b575..8b3580e699 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -280,13 +280,23 @@ typedef FormData_pg_type *Form_pg_type; #define TYPCATEGORY_UNKNOWN 'X' /* Is a type OID a polymorphic pseudotype? (Beware of multiple evaluation) */ -#define IsPolymorphicType(typid) \ +#define IsPolymorphicTypeAny(typid) \ ((typid) == ANYELEMENTOID || \ (typid) == ANYARRAYOID || \ (typid) == ANYNONARRAYOID || \ (typid) == ANYENUMOID || \ (typid) == ANYRANGEOID) +#define IsPolymorphicTypeCommon(typid) \ + ((typid) == COMMONTYPEOID || \ + (typid) == COMMONTYPEARRAYOID || \ + (typid) == COMMONTYPENONARRAYOID || \ + (typid) == COMMONTYPERANGEOID) + +#define IsPolymorphicType(typid) \ + (IsPolymorphicTypeAny(typid) || \ + IsPolymorphicTypeCommon(typid)) + #endif /* EXPOSE_TO_CLIENT_CODE */ diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 3b8e137ab5..15c7a61368 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -507,11 +507,11 @@ do_compile(FunctionCallInfo fcinfo, { if (forValidator) { - if (rettypeid == ANYARRAYOID) + if (rettypeid == ANYARRAYOID || rettypeid == COMMONTYPEARRAYOID) rettypeid = INT4ARRAYOID; else if (rettypeid == ANYRANGEOID) rettypeid = INT4RANGEOID; - else /* ANYELEMENT or ANYNONARRAY */ + else /* ANYELEMENT or ANYNONARRAY or COMMONTYPE */ rettypeid = INT4OID; /* XXX what could we use for ANYENUM? */ } @@ -2412,12 +2412,16 @@ plpgsql_resolve_polymorphic_argtypes(int numargs, case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: /* XXX dubious */ + case COMMONTYPEOID: + case COMMONTYPENONARRAYOID: argtypes[i] = INT4OID; break; case ANYARRAYOID: + case COMMONTYPEARRAYOID: argtypes[i] = INT4ARRAYOID; break; case ANYRANGEOID: + case COMMONTYPERANGEOID: argtypes[i] = INT4RANGEOID; break; default: diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 986417a188..1d661915e7 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1547,3 +1547,177 @@ View definition: drop view dfview; drop function dfunc(anyelement, anyelement, bool); +create or replace function cttestfunc01(commontype, commontype) +returns commontype as $$ +begin + if $1 > $2 then + return $1; + else + return $2; + end if; +end; +$$ language plpgsql; +create or replace function cttestfunc02(commontype, commontype) +returns commontypearray as $$ +begin + return ARRAY[$1, $2]; +end; +$$ language plpgsql; +create or replace function cttestfunc03(commontypearray) +returns commontype as $$ +begin + return $1[1]; +end; +$$ language plpgsql; +create or replace function cttestfunc04(variadic commontypearray) +returns commontype as $$ +begin + return (select min(v) from unnest($1) g(v)); +end; +$$ language plpgsql; +create or replace function cttestfunc05(variadic commontypearray) +returns commontypearray as $$ +begin + return $1; +end; +$$ language plpgsql; +create or replace function cttestfunc06(commontypenonarray, commontypenonarray) +returns commontypearray as $$ +begin + return ARRAY[$1, $2]; +end; +$$ language plpgsql; +create or replace function cttestfunc07(variadic commontypearray) +returns commontypearray as $$ + select $1 +$$ language sql; +create or replace function cttestfunc08(commontypenonarray, commontypenonarray) +returns commontypearray as $$ +select array[$1, $2] +$$ language sql; +select cttestfunc01(10, 20); + cttestfunc01 +-------------- + 20 +(1 row) + +select cttestfunc01(10.1, 20.1); + cttestfunc01 +-------------- + 20.1 +(1 row) + +select cttestfunc01(10, 20.1); + cttestfunc01 +-------------- + 20.1 +(1 row) + +select cttestfunc02(10, 20); + cttestfunc02 +-------------- + {10,20} +(1 row) + +select cttestfunc02(10.1, 20.1); + cttestfunc02 +-------------- + {10.1,20.1} +(1 row) + +select cttestfunc02(10, 20.1); + cttestfunc02 +-------------- + {10,20.1} +(1 row) + +select cttestfunc03(ARRAY[10, 20]); + cttestfunc03 +-------------- + 10 +(1 row) + +select cttestfunc03(ARRAY[10.1, 20.1]); + cttestfunc03 +-------------- + 10.1 +(1 row) + +select cttestfunc03(ARRAY[10, 20.1]); + cttestfunc03 +-------------- + 10 +(1 row) + +select cttestfunc04(10, 20); + cttestfunc04 +-------------- + 10 +(1 row) + +select cttestfunc04(10.1, 20.1); + cttestfunc04 +-------------- + 10.1 +(1 row) + +select cttestfunc04(10, 20.1); + cttestfunc04 +-------------- + 10 +(1 row) + +select cttestfunc05(10, 20); + cttestfunc05 +-------------- + {10,20} +(1 row) + +select cttestfunc05(10.1, 20.1); + cttestfunc05 +-------------- + {10.1,20.1} +(1 row) + +select cttestfunc05(10, 20.1); + cttestfunc05 +-------------- + {10,20.1} +(1 row) + +select cttestfunc06(1,1.1); + cttestfunc06 +-------------- + {1,1.1} +(1 row) + +select cttestfunc07(10, 20); + cttestfunc07 +-------------- + {10,20} +(1 row) + +select cttestfunc07(10.1, 20.1); + cttestfunc07 +-------------- + {10.1,20.1} +(1 row) + +select cttestfunc07(10, 20.1); + cttestfunc07 +-------------- + {10,20.1} +(1 row) + +select cttestfunc08(1,1.1); + cttestfunc08 +-------------- + {1,1.1} +(1 row) + +-- should to fail +select cttestfunc06(array[10], array[2]); +ERROR: function cttestfunc06(integer[], integer[]) does not exist +LINE 1: select cttestfunc06(array[10], array[2]); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql index 03606671d9..ea1a810f40 100644 --- a/src/test/regress/sql/polymorphism.sql +++ b/src/test/regress/sql/polymorphism.sql @@ -814,3 +814,91 @@ select * from dfview; drop view dfview; drop function dfunc(anyelement, anyelement, bool); + +create or replace function cttestfunc01(commontype, commontype) +returns commontype as $$ +begin + if $1 > $2 then + return $1; + else + return $2; + end if; +end; +$$ language plpgsql; + +create or replace function cttestfunc02(commontype, commontype) +returns commontypearray as $$ +begin + return ARRAY[$1, $2]; +end; +$$ language plpgsql; + +create or replace function cttestfunc03(commontypearray) +returns commontype as $$ +begin + return $1[1]; +end; +$$ language plpgsql; + +create or replace function cttestfunc04(variadic commontypearray) +returns commontype as $$ +begin + return (select min(v) from unnest($1) g(v)); +end; +$$ language plpgsql; + +create or replace function cttestfunc05(variadic commontypearray) +returns commontypearray as $$ +begin + return $1; +end; +$$ language plpgsql; + +create or replace function cttestfunc06(commontypenonarray, commontypenonarray) +returns commontypearray as $$ +begin + return ARRAY[$1, $2]; +end; +$$ language plpgsql; + +create or replace function cttestfunc07(variadic commontypearray) +returns commontypearray as $$ + select $1 +$$ language sql; + +create or replace function cttestfunc08(commontypenonarray, commontypenonarray) +returns commontypearray as $$ +select array[$1, $2] +$$ language sql; + + +select cttestfunc01(10, 20); +select cttestfunc01(10.1, 20.1); +select cttestfunc01(10, 20.1); + +select cttestfunc02(10, 20); +select cttestfunc02(10.1, 20.1); +select cttestfunc02(10, 20.1); + +select cttestfunc03(ARRAY[10, 20]); +select cttestfunc03(ARRAY[10.1, 20.1]); +select cttestfunc03(ARRAY[10, 20.1]); + +select cttestfunc04(10, 20); +select cttestfunc04(10.1, 20.1); +select cttestfunc04(10, 20.1); + +select cttestfunc05(10, 20); +select cttestfunc05(10.1, 20.1); +select cttestfunc05(10, 20.1); + +select cttestfunc06(1,1.1); + +select cttestfunc07(10, 20); +select cttestfunc07(10.1, 20.1); +select cttestfunc07(10, 20.1); + +select cttestfunc08(1,1.1); + +-- should to fail +select cttestfunc06(array[10], array[2]);