Hi pá 14. 6. 2019 v 6:09 odesílatel Pavel Stehule <pavel.steh...@gmail.com> napsal:
> > > čt 13. 6. 2019 v 2:37 odesílatel Tom Lane <t...@sss.pgh.pa.us> napsal: > >> Greg Stark <st...@mit.edu> writes: >> > The proposals I see above are "commontype", "supertype", >> "anycommontype", >> > and various abbreviations of those. I would humbly add "compatibletype". >> > Fwiw I kind of like commontype. >> > Alternately an argument could be made that length and typing convenience >> > isn't really a factor here since database users never have to type these >> > types. The only place they get written is when defining polymorphic >> > functions which is a pretty uncommon operation. >> > In which case a very explicit "anycompatibletype" may be better. >> >> I could go with "anycompatibletype". That would lead us to needing >> related names like "anycompatiblearraytype", which is getting annoyingly >> long, but you might be right that people wouldn't have to type it that >> often. >> >> Also, given the precedent of "anyarray" and "anyrange", it might be >> okay to make these just "anycompatible" and "anycompatiblearray". >> > > I like anycompatible and anycompatiblearray. > > I'll update the patch > and here it is Regards Pavel > > Regards > > Pavel > > >> [ wanders away wondering if psql can tab-complete type names in >> function definitions ... ] >> >> regards, tom lane >> >
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 35ecd48ed5..d440f1c6ed 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -4791,6 +4791,14 @@ SELECT * FROM pg_attribute <primary>anyrange</primary> </indexterm> + <indexterm zone="datatype-pseudo"> + <primary>anycompatible</primary> + </indexterm> + + <indexterm zone="datatype-pseudo"> + <primary>anycompatiblearray</primary> + </indexterm> + <indexterm zone="datatype-pseudo"> <primary>void</primary> </indexterm> @@ -4900,6 +4908,34 @@ SELECT * FROM pg_attribute <xref linkend="rangetypes"/>).</entry> </row> + <row> + <entry><type>anycompatible</type></entry> + <entry>Indicates that a function accepts any data type. Values + are converted to real common type. + (see <xref linkend="extend-types-polymorphic"/>).</entry> + </row> + + <row> + <entry><type>anycompatiblearray</type></entry> + <entry>Indicates that a function accepts any array data type. The + elements of array are converted to common type of these values. + (see <xref linkend="extend-types-polymorphic"/>).</entry> + </row> + + <row> + <entry><type>anycompatiblenonarray</type></entry> + <entry>Indicates that a function accepts any non-array data type + (see <xref linkend="extend-types-polymorphic"/>).</entry> + </row> + + <row> + <entry><type>anycompatiblerange</type></entry> + <entry>Indicates that a function accepts any range data type + (see <xref linkend="extend-types-polymorphic"/> and + <xref linkend="rangetypes"/>). The subtype can be used for + deduction of common type.</entry> + </row> + <row> <entry><type>cstring</type></entry> <entry>Indicates that a function accepts or returns a null-terminated C string.</entry> diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index b5e59d542a..2de97eab33 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -231,7 +231,7 @@ <para> Five pseudo-types of special interest are <type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>, - and <type>anyrange</type>, + <type>anyrange</type>, <type>anycompatible</type> and <type>anycompatiblearray</type>. which are collectively called <firstterm>polymorphic types</firstterm>. Any function declared using these types is said to be a <firstterm>polymorphic function</firstterm>. A polymorphic function can @@ -267,6 +267,15 @@ be an enum type. </para> + <para> + Second family of polymorphic types are types <type>anycompatible</type> and + <type>anycompatiblearray</type>. These types are similar to types + <type>anyelement</type> and <type>anyarray</type>. The arguments declared + as <type>anyelement</type> requires same real type of passed values. For + <type>anycompatible</type>'s arguments is selected common type, and later + all these arguments are casted to this common type. + </para> + <para> 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 7cab039ded..b3e7eb391b 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -136,7 +136,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; @@ -146,7 +146,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"), @@ -156,7 +156,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"), @@ -492,7 +492,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 002584b941..26328ceb11 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -103,6 +103,10 @@ ProcedureCreate(const char *procedureName, bool anyrangeOutParam = false; bool internalInParam = false; bool internalOutParam = false; + bool anycompatibleInParam = false; + bool anycompatibleOutParam = false; + bool anycompatiblerangeInParam = false; + bool anycompatiblerangeOutParam = false; Oid variadicType = InvalidOid; Acl *proacl = NULL; Relation rel; @@ -198,6 +202,15 @@ ProcedureCreate(const char *procedureName, case INTERNALOID: internalInParam = true; break; + case ANYCOMPATIBLEOID: + case ANYCOMPATIBLEARRAYOID: + case ANYCOMPATIBLENONARRAYOID: + anycompatibleInParam = true; + break; + case ANYCOMPATIBLERANGEOID: + anycompatibleInParam = true; + anycompatiblerangeInParam = true; + break; } } @@ -225,6 +238,15 @@ ProcedureCreate(const char *procedureName, case INTERNALOID: internalOutParam = true; break; + case ANYCOMPATIBLEOID: + case ANYCOMPATIBLEARRAYOID: + case ANYCOMPATIBLENONARRAYOID: + anycompatibleOutParam = true; + break; + case ANYCOMPATIBLERANGEOID: + anycompatibleOutParam = true; + anycompatiblerangeOutParam = true; + break; } } } @@ -236,13 +258,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) || anycompatibleOutParam) + && !anycompatibleInParam) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("cannot determine result data type"), + errdetail("A function returning \"anycompatible\" or \"anycompatiblearray\" must have at least one \"anycompatible\" or \"anycompatiblearray\" argument."))); + if ((returnType == ANYRANGEOID || anyrangeOutParam) && !anyrangeInParam) ereport(ERROR, @@ -250,6 +279,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 == ANYCOMPATIBLERANGEOID || anycompatiblerangeOutParam) && + !anycompatiblerangeInParam) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("cannot determine result data type"), + errdetail("A function returning \"anycompatiblerange\" must have at least one \"anycompatiblerange\" argument."))); + if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), @@ -287,6 +323,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/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index d569067dc4..77846f2a6f 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -339,7 +339,7 @@ DefineAggregate(ParseState *pstate, transTypeId = typenameTypeId(NULL, transType); transTypeType = get_typtype(transTypeId); if (transTypeType == TYPTYPE_PSEUDO && - !IsPolymorphicType(transTypeId)) + !IsPolymorphicTypeAny(transTypeId)) { if (transTypeId == INTERNALOID && superuser()) /* okay */ ; @@ -380,7 +380,7 @@ DefineAggregate(ParseState *pstate, 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 4f62e48d98..a07ca3b58e 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -319,6 +319,7 @@ interpret_function_parameter_list(ParseState *pstate, switch (toid) { case ANYARRAYOID: + case ANYCOMPATIBLEARRAYOID: case ANYOID: /* okay */ break; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d05d2fd3d5..c6e4e8e478 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -270,7 +270,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; @@ -298,7 +298,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 cb2c5e181b..aced4675bf 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -7907,7 +7907,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 b50e9ccdf1..816084c2ff 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 d50410d23a..1ef95e6629 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 903478d8ca..1500c9081d 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 == ANYCOMPATIBLEOID || + targetTypeId == ANYCOMPATIBLENONARRAYOID) { /* * 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 == ANYCOMPATIBLEARRAYOID || + targetTypeId == ANYCOMPATIBLERANGEOID) { /* * 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_anycompatible_nonarray = false; + bool have_anycompatible_range = false; + bool have_generic_anycompatible = false; + Oid anycompatible_range_typeid = InvalidOid; + Oid anycompatible_actual_types[FUNC_MAX_ARGS]; + int n_anycompatible_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 == ANYCOMPATIBLEOID || + decl_type == ANYCOMPATIBLENONARRAYOID) + { + have_generic_anycompatible = true; + if (decl_type == ANYCOMPATIBLENONARRAYOID) + have_anycompatible_nonarray = true; + if (actual_type == UNKNOWNOID) + continue; + + /* collect used type, reduce repeated values * */ + if (n_anycompatible_args == 0 || + anycompatible_actual_types[n_anycompatible_args - 1] != actual_type) + anycompatible_actual_types[n_anycompatible_args++] = actual_type; + } + else if (decl_type == ANYCOMPATIBLEARRAYOID) + { + Oid anycompatible_elem_type; + + have_generic_anycompatible = true; + + if (actual_type == UNKNOWNOID) + continue; + + actual_type = getBaseType(actual_type); /* flatten domains */ + anycompatible_elem_type = get_element_type(actual_type); + + if (!OidIsValid(anycompatible_elem_type)) + return false; + + /* collect used type, reduce repeated values * */ + if (n_anycompatible_args == 0 || + anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_elem_type) + anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type; + } + else if (decl_type == ANYCOMPATIBLERANGEOID) + { + Oid anycompatible_range_typelem; + + have_generic_anycompatible = true; + have_anycompatible_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(anycompatible_range_typeid) && + anycompatible_range_typeid != actual_type) + return false; + + anycompatible_range_typelem = get_range_subtype(actual_type); + if (!OidIsValid(anycompatible_range_typelem)) + return false; + + if (!OidIsValid(anycompatible_range_typeid)) + anycompatible_range_typeid = actual_type; + + /* collect used type, reduce repeated values * */ + if (n_anycompatible_args == 0 || + anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_range_typelem) + anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem; + } } /* Get the element type based on the array type, if we have one */ @@ -1594,6 +1764,40 @@ check_generic_type_consistency(const Oid *actual_arg_types, return false; } + /* check anycompatible collected data */ + if (have_generic_anycompatible) + { + if (n_anycompatible_args > 0) + { + Oid anycompatible_typeid; + + anycompatible_typeid = select_common_type_from_vector(n_anycompatible_args, + anycompatible_actual_types, + true); + + if (!OidIsValid(anycompatible_typeid)) + return false; + + 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; + } + + if (have_anycompatible_range && !OidIsValid(anycompatible_range_typeid)) + return false; + + return true; + } + + /* is not possible derive common type */ + return false; + } + /* Looks valid */ return true; } @@ -1676,11 +1880,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_anycompatible = false; bool have_unknowns = false; Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; Oid range_typeid = InvalidOid; + Oid anycompatible_typeid = InvalidOid; + Oid anycompatible_array_typeid = InvalidOid; + Oid anycompatible_range_typeid = InvalidOid; Oid array_typelem; Oid range_typelem; bool have_anyelement = (rettype == ANYELEMENTOID || @@ -1688,6 +1896,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, rettype == ANYENUMOID); 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); + Oid anycompatible_actual_types[FUNC_MAX_ARGS]; + int n_anycompatible_args = 0; /* * Loop through the arguments to see if we have any that are polymorphic. @@ -1702,7 +1915,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 +1938,18 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, } else if (decl_type == ANYARRAYOID) { - have_generics = true; + have_generics_any = true; + have_anycompatible_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 +1962,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 +1980,298 @@ 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_generics_anycompatible = true; - /* - * Fast Track: if none of the arguments are polymorphic, return the - * unmodified rettype. We assume it can't be polymorphic either. - */ - if (!have_generics) - return rettype; + if (decl_type == ANYCOMPATIBLENONARRAYOID) + have_anycompatible_nonarray = true; - /* Get the element type based on the array type, if we have one */ - if (OidIsValid(array_typeid)) - { - if (array_typeid == ANYARRAYOID && !have_anyelement) - { - /* Special case for ANYARRAY input: okay iff no ANYELEMENT */ - array_typelem = ANYELEMENTOID; + /* + * 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_anycompatible_args == 0 || + anycompatible_actual_types[n_anycompatible_args - 1] != actual_type) + anycompatible_actual_types[n_anycompatible_args++] = actual_type; } - else + else if (decl_type == ANYCOMPATIBLEARRAYOID) { - array_typelem = get_element_type(array_typeid); - if (!OidIsValid(array_typelem)) + Oid anycompatible_elem_type; + + have_generics_anycompatible = true; + have_anycompatible_array = true; + + if (actual_type == UNKNOWNOID) + continue; + + if (allow_poly && decl_type == actual_type) + continue; + + 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", - "anyarray", format_type_be(array_typeid)))); - } + "anyarray", format_type_be(actual_type)))); - if (!OidIsValid(elem_typeid)) - { - /* - * if we don't have an element type yet, use the one we just got - */ - elem_typeid = array_typelem; + /* collect used type, reduce repeated values * */ + if (n_anycompatible_args == 0 || + anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_elem_type) + anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type; } - else if (array_typelem != elem_typeid) + else if (decl_type == ANYCOMPATIBLERANGEOID) { - /* 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)))); + Oid anycompatible_range_typelem; + + have_generics_anycompatible = true; + have_anycompatible_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(anycompatible_range_typeid) && + actual_type != anycompatible_range_typeid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("arguments declared \"anyrange\" are not all alike"), + errdetail("%s versus %s", + format_type_be(anycompatible_range_typeid), + format_type_be(actual_type)))); + + anycompatible_range_typeid = actual_type; + anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid); + + if (!OidIsValid(anycompatible_range_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared %s is not a range type but type %s", + "anyrange", + format_type_be(anycompatible_range_typeid)))); + + /* collect used type, reduce repeated values * */ + if (n_anycompatible_args == 0 || + anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_range_typelem) + anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem; } } - /* Get the element type based on the range type, if we have one */ - if (OidIsValid(range_typeid)) + /* + * Fast Track: if none of the arguments are polymorphic, return the + * unmodified rettype. We assume it can't be polymorphic either. + */ + if (!have_generics_any && !have_generics_anycompatible) + return rettype; + + if (have_generics_any) { - if (range_typeid == ANYRANGEOID && !have_anyelement) + /* Get the element type based on the array type, if we have one */ + if (OidIsValid(array_typeid)) { - /* Special case for ANYRANGE input: okay iff no ANYELEMENT */ - range_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)) { - range_typelem = get_range_subtype(range_typeid); - if (!OidIsValid(range_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 a range type but type %s", - "anyrange", - format_type_be(range_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 (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"))); + } + } + + if (have_anynonarray && elem_typeid != ANYELEMENTOID) { /* - * if we don't have an element type yet, use the one we just got + * require the element type to not be an array or domain over + * array */ - elem_typeid = range_typelem; + if (type_is_array_domain(elem_typeid)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type matched to anynonarray is an array type: %s", + format_type_be(elem_typeid)))); } - else if (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_anycompatible) { - if (allow_poly) + if (n_anycompatible_args > 0) { - elem_typeid = ANYELEMENTOID; - array_typeid = ANYARRAYOID; - range_typeid = ANYRANGEOID; + anycompatible_typeid = select_common_type_from_vector(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)))); + } + + /* anycompatibke_range_typid should be defined already */ + if (have_anycompatible_range && !OidIsValid(anycompatible_range_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find range type for data type %s", + "anycompatiblerange"))); + + 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 anynonarray is an array type: %s", + format_type_be(anycompatible_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) + { + anycompatible_typeid = ANYCOMPATIBLEOID; + anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID; + anycompatible_range_typeid = ANYCOMPATIBLERANGEOID; + } + 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 == 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; + } } /* @@ -1965,6 +2352,41 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, rettype == ANYENUMOID) return elem_typeid; + if (rettype == ANYCOMPATIBLEOID) + { + if (!OidIsValid(anycompatible_typeid)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find common type"))); + } + return anycompatible_typeid; + } + + if (rettype == ANYCOMPATIBLEARRAYOID) + { + if (!OidIsValid(anycompatible_array_typeid)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find common array type"))); + } + return anycompatible_array_typeid; + } + + /* if we return ANYRANGE use the appropriate argument type */ + if (rettype == ANYCOMPATIBLERANGEOID) + { + if (!OidIsValid(anycompatible_range_typeid)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find range type for data type %s", + "anycompatiblerange"))); + } + return anycompatible_range_typeid; + } + /* we don't return a generic type; send back the original return type */ return rettype; } @@ -2060,6 +2482,75 @@ resolve_generic_type(Oid declared_type, return context_actual_type; } } + else if (declared_type == ANYCOMPATIBLEARRAYOID) + { + if (context_declared_type == ANYCOMPATIBLEARRAYOID) + { + /* + * 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 == ANYCOMPATIBLEOID || + context_declared_type == ANYCOMPATIBLENONARRAYOID || + context_declared_type == ANYCOMPATIBLERANGEOID) + { + /* 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 == ANYCOMPATIBLEOID || + declared_type == ANYCOMPATIBLENONARRAYOID) + { + if (context_declared_type == ANYCOMPATIBLEARRAYOID) + { + /* 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", + "anycompatiblearray", format_type_be(context_base_type)))); + return array_typelem; + } + else if (context_declared_type == ANYCOMPATIBLERANGEOID) + { + /* 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 == ANYCOMPATIBLEOID || + context_declared_type == ANYCOMPATIBLENONARRAYOID) + { + /* 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 +2633,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 == ANYCOMPATIBLEARRAYOID) return true; /* If srctype is a domain, reduce to its base type */ @@ -2155,7 +2647,7 @@ IsBinaryCoercible(Oid srctype, Oid targettype) return true; /* Also accept any array type as coercible to ANYARRAY */ - if (targettype == ANYARRAYOID) + if (targettype == ANYARRAYOID || targettype == ANYCOMPATIBLEARRAYOID) if (type_is_array(srctype)) return true; diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 0e1015962d..b61a9da264 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 99d26de7e6..62a89f30b1 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -1850,7 +1850,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 26d293709a..17c7c45a8a 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 == 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 69f41ab455..d95aadf0da 100644 --- a/src/backend/utils/adt/jsonb.c +++ b/src/backend/utils/adt/jsonb.c @@ -659,7 +659,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 5c886cfe96..727b4ed371 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); } +/* + * anycompatiblearray_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 +anycompatiblearray_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "anycompatiblearray"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * anycompatiblearray_send - binary output routine for pseudo-type COMMONTYPEARRAY. + * + * We may as well allow this, since array_send will in fact work. + */ +Datum +anycompatiblearray_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); } +/* + * anycompatiblerange_in - input routine for pseudo-type COMMONTYPERANGE. + */ +Datum +anycompatiblerange_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "anycompatiblerange"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * anycompatiblerange_out - output routine for pseudo-type COMMONTYPERANGE. + * + * We may as well allow this, since range_out will in fact work. + */ +Datum +anycompatiblerange_out(PG_FUNCTION_ARGS) +{ + return range_out(fcinfo); +} + /* * void_in - input routine for pseudo-type VOID. * @@ -419,3 +470,6 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(opaque); PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement); PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray); PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler); +PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatible); +PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblearray); +PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblenonarray); diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index b7fac5d295..3bba9c07a4 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_anycompatible_result = false; + bool have_anycompatible_array_result = false; + bool have_anycompatible_range_result = false; + bool have_anycompatible_nonarray = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; Oid anyrange_type = InvalidOid; + Oid anycompatible_type = InvalidOid; + Oid anycompatible_array_type = InvalidOid; + Oid anycompatible_range_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 ANYCOMPATIBLEOID: + have_anycompatible_result = true; + break; + case ANYCOMPATIBLEARRAYOID: + have_anycompatible_array_result = true; + break; + case ANYCOMPATIBLENONARRAYOID: + have_anycompatible_result = true; + have_anycompatible_nonarray = true; + break; + case ANYCOMPATIBLERANGEOID: + have_anycompatible_range_result = true; + break; default: break; } } if (!have_anyelement_result && !have_anyarray_result && - !have_anyrange_result) + !have_anyrange_result && + !have_anycompatible_result && !have_anycompatible_array_result && + !have_anycompatible_range_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 ANYCOMPATIBLEOID: + case ANYCOMPATIBLENONARRAYOID: + if (!OidIsValid(anycompatible_type)) + anycompatible_type = get_call_expr_argtype(call_expr, i); + break; + case ANYCOMPATIBLEARRAYOID: + if (!OidIsValid(anycompatible_array_type)) + anycompatible_array_type = get_call_expr_argtype(call_expr, i); + break; + case ANYCOMPATIBLERANGEOID: + if (!OidIsValid(anycompatible_range_type)) + anycompatible_range_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_anycompatible_array_result && !OidIsValid(anycompatible_array_type)) + anycompatible_array_type = resolve_generic_type(ANYCOMPATIBLEARRAYOID, + anycompatible_type, + ANYCOMPATIBLEOID); + + if (have_anycompatible_result && !OidIsValid(anycompatible_type)) + { + if (OidIsValid(anycompatible_array_type)) + anycompatible_type = resolve_generic_type(ANYCOMPATIBLEOID, + anycompatible_array_type, + ANYCOMPATIBLEARRAYOID); + + if (OidIsValid(anycompatible_range_type)) + { + Oid subtype = resolve_generic_type(ANYCOMPATIBLEOID, + anycompatible_range_type, + ANYCOMPATIBLERANGEOID); + + /* check for inconsistent array and range results */ + if (OidIsValid(anycompatible_type) && anycompatible_type != subtype) + return false; + anycompatible_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_anycompatible_range_result && !OidIsValid(anycompatible_range_type)) + return false; + /* Enforce ANYNONARRAY if needed */ if (have_anynonarray && type_is_array(anyelement_type)) return false; + /* Enforce COMMONTYPENONARRAY if needed */ + if (have_anycompatible_nonarray && type_is_array(anycompatible_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(anycompatible_type)) + ctcollation = get_typcollation(anycompatible_type); + else if (OidIsValid(anycompatible_array_type)) + ctcollation = get_typcollation(anycompatible_array_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 ANYCOMPATIBLEOID: + case ANYCOMPATIBLENONARRAYOID: + TupleDescInitEntry(tupdesc, i + 1, + NameStr(att->attname), + anycompatible_type, + -1, + 0); + TupleDescInitEntryCollation(tupdesc, i + 1, ctcollation); + break; + case ANYCOMPATIBLEARRAYOID: + TupleDescInitEntry(tupdesc, i + 1, + NameStr(att->attname), + anycompatible_array_type, + -1, + 0); + TupleDescInitEntryCollation(tupdesc, i + 1, ctcollation); + break; + case ANYCOMPATIBLERANGEOID: + TupleDescInitEntry(tupdesc, i + 1, + NameStr(att->attname), + anycompatible_range_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_anycompatible_result = false; + bool have_anycompatible_array_result = false; + bool have_anycompatible_range_result = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; Oid anyrange_type = InvalidOid; + Oid anycompatible_type = InvalidOid; + Oid anycompatible_array_type = InvalidOid; + Oid anycompatible_range_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 ANYCOMPATIBLEOID: + case ANYCOMPATIBLENONARRAYOID: + if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) + have_anycompatible_result = true; + else + { + if (!OidIsValid(anycompatible_type)) + { + anycompatible_type = get_call_expr_argtype(call_expr, + inargno); + if (!OidIsValid(anycompatible_type)) + return false; + } + argtypes[i] = anycompatible_type; + } + break; + case ANYCOMPATIBLEARRAYOID: + if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) + have_anycompatible_array_result = true; + else + { + if (!OidIsValid(anycompatible_array_type)) + { + anycompatible_array_type = get_call_expr_argtype(call_expr, + inargno); + if (!OidIsValid(anycompatible_array_type)) + return false; + } + argtypes[i] = anycompatible_array_type; + } + break; + case ANYCOMPATIBLERANGEOID: + if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) + have_anycompatible_range_result = true; + else + { + if (!OidIsValid(anycompatible_range_type)) + { + anycompatible_range_type = get_call_expr_argtype(call_expr, + inargno); + if (!OidIsValid(anycompatible_range_type)) + return false; + } + argtypes[i] = anycompatible_range_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_anycompatible_result && !have_anycompatible_array_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_anycompatible_result || have_anycompatible_array_result || + have_anycompatible_range_result) + { + /* If no input polymorphics, parser messed up */ + if (!OidIsValid(anycompatible_type) && !OidIsValid(anycompatible_array_type) && + !OidIsValid(anycompatible_range_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_anycompatible_result && !OidIsValid(anycompatible_type)) + { + if (OidIsValid(anycompatible_array_type)) + { + anycompatible_type = resolve_generic_type(ANYCOMPATIBLEOID, + anycompatible_array_type, + ANYCOMPATIBLEARRAYOID); + + if (OidIsValid(anycompatible_range_type)) + { + Oid subtype = resolve_generic_type(ANYCOMPATIBLEOID, + anyrange_type, + ANYCOMPATIBLERANGEOID); + + /* check for inconsistent array and range results */ + if (OidIsValid(anycompatible_type) && anycompatible_type != subtype) + return false; + anycompatible_type = subtype; + } + } + } - /* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */ + if (have_anycompatible_array_result || !OidIsValid(anycompatible_array_type)) + anycompatible_array_type = resolve_generic_type(ANYCOMPATIBLEARRAYOID, + anycompatible_type, + ANYCOMPATIBLEOID); + + /* + * We can't deduce a range type from other polymorphic inputs, because + * there may be multiple range types for the same subtype. + */ + if (have_anycompatible_range_result && !OidIsValid(anycompatible_range_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 ANYCOMPATIBLEOID: + case ANYCOMPATIBLENONARRAYOID: + argtypes[i] = anycompatible_type; + break; + case ANYCOMPATIBLEARRAYOID: + argtypes[i] = anycompatible_array_type; + break; + case ANYCOMPATIBLERANGEOID: + argtypes[i] = anycompatible_range_type; + break; default: break; } diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 87335248a0..e8052940b4 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7016,6 +7016,12 @@ { oid => '2778', descr => 'I/O', proname => 'anynonarray_out', prorettype => 'cstring', proargtypes => 'anynonarray', prosrc => 'anynonarray_out' }, +{ oid => '4230', descr => 'I/O', + proname => 'anycompatiblenonarray_in', prorettype => 'anycompatiblenonarray', + proargtypes => 'cstring', prosrc => 'anycompatiblenonarray_in' }, +{ oid => '4231', descr => 'I/O', + proname => 'anycompatiblenonarray_out', prorettype => 'cstring', + proargtypes => 'anycompatiblenonarray', prosrc => 'anycompatiblenonarray_out' }, { oid => '3116', descr => 'I/O', proname => 'fdw_handler_in', proisstrict => 'f', prorettype => 'fdw_handler', proargtypes => 'cstring', prosrc => 'fdw_handler_in' }, @@ -7042,6 +7048,18 @@ { oid => '268', descr => 'I/O', proname => 'table_am_handler_out', prorettype => 'cstring', proargtypes => 'table_am_handler', prosrc => 'table_am_handler_out' }, +{ oid => '4232', descr => 'I/O', + proname => 'anycompatible_in', prorettype => 'anycompatible', + proargtypes => 'cstring', prosrc => 'anycompatible_in' }, +{ oid => '4233', descr => 'I/O', + proname => 'anycompatible_out', prorettype => 'cstring', + proargtypes => 'anycompatible', prosrc => 'anycompatible_out' }, +{ oid => '4234', descr => 'I/O', + proname => 'anycompatiblearray_in', prorettype => 'anycompatiblearray', + proargtypes => 'cstring', prosrc => 'anycompatiblearray_in' }, +{ oid => '4235', descr => 'I/O', + proname => 'anycompatiblearray_out', prorettype => 'cstring', + proargtypes => 'anycompatiblearray', prosrc => 'anycompatiblearray_out' }, # tablesample method handlers { oid => '3313', descr => 'BERNOULLI tablesample method handler', @@ -7539,6 +7557,12 @@ { oid => '3447', descr => 'I/O', proname => 'macaddr8_send', prorettype => 'bytea', proargtypes => 'macaddr8', prosrc => 'macaddr8_send' }, +{ oid => '4236', descr => 'I/O', + proname => 'anycompatiblearray_recv', provolatile => 's', prorettype => 'anycompatiblearray', + proargtypes => 'internal', prosrc => 'anycompatiblearray_recv' }, +{ oid => '4237', descr => 'I/O', + proname => 'anycompatiblearray_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'anycompatiblearray', prosrc => 'anycompatiblearray_send' }, # System-view support functions with pretty-print option { oid => '2504', descr => 'source text of a rule with pretty-print option', @@ -9456,6 +9480,12 @@ { oid => '3833', descr => 'I/O', proname => 'anyrange_out', provolatile => 's', prorettype => 'cstring', proargtypes => 'anyrange', prosrc => 'anyrange_out' }, +{ oid => '4238', descr => 'I/O', + proname => 'anycompatiblerange_in', provolatile => 's', prorettype => 'anycompatiblerange', + proargtypes => 'cstring oid int4', prosrc => 'anycompatiblerange_in' }, +{ oid => '4239', descr => 'I/O', + proname => 'anycompatiblerange_out', provolatile => 's', prorettype => 'cstring', + proargtypes => 'anycompatiblerange', prosrc => 'anycompatiblerange_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 be49e00114..0275596080 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -594,5 +594,25 @@ typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p', typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out', typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' }, - +{ oid => '4240', 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 => '4241', descr => 'pseudo-type representing a polymorphic array type of 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 => '4242', + 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 => '4243', + descr => 'pseudo-type representing a polymorphic common type that is a range', + 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 2a584b4b13..f153b3a7c0 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) == ANYCOMPATIBLEOID || \ + (typid) == ANYCOMPATIBLEARRAYOID || \ + (typid) == ANYCOMPATIBLENONARRAYOID || \ + (typid) == ANYCOMPATIBLERANGEOID) + +#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 30c7e967df..48ed3bba0e 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 == ANYCOMPATIBLEARRAYOID) 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 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/polymorphism.out b/src/test/regress/expected/polymorphism.out index 986417a188..3f49db19d8 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(anycompatible, anycompatible) +returns anycompatible as $$ +begin + if $1 > $2 then + return $1; + else + return $2; + end if; +end; +$$ language plpgsql; +create or replace function cttestfunc02(anycompatible, anycompatible) +returns anycompatiblearray as $$ +begin + return ARRAY[$1, $2]; +end; +$$ language plpgsql; +create or replace function cttestfunc03(anycompatiblearray) +returns anycompatible as $$ +begin + return $1[1]; +end; +$$ language plpgsql; +create or replace function cttestfunc04(variadic anycompatiblearray) +returns anycompatible as $$ +begin + return (select min(v) from unnest($1) g(v)); +end; +$$ language plpgsql; +create or replace function cttestfunc05(variadic anycompatiblearray) +returns anycompatiblearray as $$ +begin + return $1; +end; +$$ language plpgsql; +create or replace function cttestfunc06(anycompatiblenonarray, anycompatiblenonarray) +returns anycompatiblearray as $$ +begin + return ARRAY[$1, $2]; +end; +$$ language plpgsql; +create or replace function cttestfunc07(variadic anycompatiblearray) +returns anycompatiblearray as $$ + select $1 +$$ language sql; +create or replace function cttestfunc08(anycompatiblenonarray, anycompatiblenonarray) +returns anycompatiblearray 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..566b25bf0e 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(anycompatible, anycompatible) +returns anycompatible as $$ +begin + if $1 > $2 then + return $1; + else + return $2; + end if; +end; +$$ language plpgsql; + +create or replace function cttestfunc02(anycompatible, anycompatible) +returns anycompatiblearray as $$ +begin + return ARRAY[$1, $2]; +end; +$$ language plpgsql; + +create or replace function cttestfunc03(anycompatiblearray) +returns anycompatible as $$ +begin + return $1[1]; +end; +$$ language plpgsql; + +create or replace function cttestfunc04(variadic anycompatiblearray) +returns anycompatible as $$ +begin + return (select min(v) from unnest($1) g(v)); +end; +$$ language plpgsql; + +create or replace function cttestfunc05(variadic anycompatiblearray) +returns anycompatiblearray as $$ +begin + return $1; +end; +$$ language plpgsql; + +create or replace function cttestfunc06(anycompatiblenonarray, anycompatiblenonarray) +returns anycompatiblearray as $$ +begin + return ARRAY[$1, $2]; +end; +$$ language plpgsql; + +create or replace function cttestfunc07(variadic anycompatiblearray) +returns anycompatiblearray as $$ + select $1 +$$ language sql; + +create or replace function cttestfunc08(anycompatiblenonarray, anycompatiblenonarray) +returns anycompatiblearray 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]);