Hi, the possibility to use polymorphic types is a specific interesting PostgreSQL feature. The polymorphic types allows to use almost all types, but when some type is selected, then this type is required strictly without possibility to use some implicit casting.
So if I have a fx(anyelement, anyelement), then I can call function fx with parameters (int, int), (numeric, numeric), but I cannot to use parameters (int, numeric). The strict design has sense, but for few important cases is too restrictive. We are not able to implement (with plpgsql) functions like coalesce, greatest, least where all numeric types can be used. Alternative solution can be based on usage "any" type. But we can work with this type only from "C" extensions, and there is some performance penalization due dynamic casting inside function. Four years ago I proposed implicit casting to common type of arguments with anyelement type. https://www.postgresql.org/message-id/CAFj8pRCZVo_xoW0cfxt%3DmmgjXKBgr3Gm1VMGL_zx9wDRHmm6Cw%40mail.gmail.com My proposal was rejected, because it introduce compatibility issues. Now I have a solution that doesn't break anything. With two new polymorphic types: commontype and commontypearray we can write functions like coalesce, greatest, .. More, these types are independent on current polymorphic types - and can be used with current polymorphic types together to cover some new use cases. CREATE OR REPLACE FUNCTION fx(anyelement, commontype, anyelement, commontype) RETURNS commontype or CREATE OR REPLACE FUNCTION fx(anyelement, commontype, anyelement, commontype) RETURNS anyelement and commontype and anyelement types can be really independent. Comments, notes? Regards Pavel
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index ed0ee584c9..10876837e2 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -4761,6 +4761,14 @@ SELECT * FROM pg_attribute <primary>anyrange</primary> </indexterm> + <indexterm zone="datatype-pseudo"> + <primary>commontype</primary> + </indexterm> + + <indexterm zone="datatype-pseudo"> + <primary>commontypearray</primary> + </indexterm> + <indexterm zone="datatype-pseudo"> <primary>void</primary> </indexterm> @@ -4870,6 +4878,20 @@ SELECT * FROM pg_attribute <xref linkend="rangetypes"/>).</entry> </row> + <row> + <entry><type>commontype</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>commontypearray</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>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 a6b77c1cfe..dece346c03 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>commontype</type> and <type>commontypearray</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>commontype</type> and + <type>commontypearray</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>commontype</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 4b12e9f274..d31658af53 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 74ee309c79..6bcd6f48eb 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -101,6 +101,8 @@ ProcedureCreate(const char *procedureName, bool anyrangeOutParam = false; bool internalInParam = false; bool internalOutParam = false; + bool commonInParam = false; + bool commonOutParam = false; Oid variadicType = InvalidOid; Acl *proacl = NULL; Relation rel; @@ -196,6 +198,10 @@ ProcedureCreate(const char *procedureName, case INTERNALOID: internalInParam = true; break; + case COMMONTYPEOID: + case COMMONTYPEARRAYOID: + commonInParam = true; + break; } } @@ -223,6 +229,10 @@ ProcedureCreate(const char *procedureName, case INTERNALOID: internalOutParam = true; break; + case COMMONTYPEOID: + case COMMONTYPEARRAYOID: + commonOutParam = true; + break; } } } @@ -234,13 +244,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, @@ -285,6 +302,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)) @@ -847,7 +867,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) if (get_typtype(proc->prorettype) == TYPTYPE_PSEUDO && proc->prorettype != RECORDOID && proc->prorettype != VOIDOID && - !IsPolymorphicType(proc->prorettype)) + !IsPolymorphicTypeAny(proc->prorettype)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SQL functions cannot return type %s", @@ -860,7 +880,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) { if (get_typtype(proc->proargtypes.values[i]) == TYPTYPE_PSEUDO) { - if (IsPolymorphicType(proc->proargtypes.values[i])) + if (IsPolymorphicTypeAny(proc->proargtypes.values[i])) haspolyarg = true; else ereport(ERROR, diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 877f658ce7..a1f6472f5f 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 ebece4d1d7..90ab0c7249 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -321,6 +321,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 73656d8cc8..1902778fe0 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 843ed48aa7..1a8e3f8246 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -7514,7 +7514,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/executor/functions.c b/src/backend/executor/functions.c index fc7c6051c5..9d5c5502ef 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -216,7 +216,7 @@ prepare_sql_fn_parse_info(HeapTuple procedureTuple, { Oid argtype = argOidVect[argnum]; - if (IsPolymorphicType(argtype)) + if (IsPolymorphicTypeAny(argtype)) { argtype = get_call_expr_argtype(call_expr, argnum); if (argtype == InvalidOid) @@ -647,7 +647,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK) */ rettype = procedureStruct->prorettype; - if (IsPolymorphicType(rettype)) + if (IsPolymorphicTypeAny(rettype)) { rettype = get_fn_expr_rettype(finfo); if (rettype == InvalidOid) /* this probably should not happen */ @@ -1595,7 +1595,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, Oid restype; ListCell *lc; - AssertArg(!IsPolymorphicType(rettype)); + AssertArg(!IsPolymorphicTypeAny(rettype)); if (modifyTargetList) *modifyTargetList = false; /* initialize for no change */ diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index b22b36ec0e..bde620009b 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 61727e1d71..b3c85e3e97 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -1849,7 +1849,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 f4fc7b61e7..e0275e806d 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -1389,6 +1389,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. @@ -1676,11 +1770,14 @@ 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 common_type_typeid = InvalidOid; + Oid common_type_array_typeid = InvalidOid; Oid array_typelem; Oid range_typelem; bool have_anyelement = (rettype == ANYELEMENTOID || @@ -1688,6 +1785,10 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, rettype == ANYENUMOID); bool have_anynonarray = (rettype == ANYNONARRAYOID); bool have_anyenum = (rettype == ANYENUMOID); + bool have_common_type = (rettype == COMMONTYPEOID); + bool have_common_type_array = (rettype == COMMONTYPEARRAYOID); + Oid actual_types[FUNC_MAX_ARGS]; + int n_actual_types = 0; /* * Loop through the arguments to see if we have any that are polymorphic. @@ -1702,7 +1803,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) @@ -1723,16 +1824,59 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, format_type_be(actual_type)))); elem_typeid = actual_type; } + else if (decl_type == COMMONTYPEOID) + { + have_generics_common = have_common_type = 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; + + /* store real type, reduce repeated values */ + if ((n_actual_types > 0 && actual_types[n_actual_types - 1] != actual_type) || + n_actual_types == 0) + actual_types[n_actual_types++] = actual_type; + } + else if (decl_type == COMMONTYPEARRAYOID) + { + Oid elem_type; + + have_generics_common = have_common_type = have_common_type_array = true; + + if (actual_type == UNKNOWNOID) + continue; + + if (allow_poly && decl_type == actual_type) + continue; + + actual_type = getBaseType(actual_type); /* flatten domains */ + elem_type = get_element_type(actual_type); + + if (OidIsValid(elem_type)) + { + if ((n_actual_types > 0 && actual_types[n_actual_types - 1] != elem_type) || + n_actual_types == 0) + actual_types[n_actual_types++] = elem_type; + } + } else if (decl_type == ANYARRAYOID) { - have_generics = true; + have_generics_any = 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 +1889,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; @@ -1769,122 +1913,153 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, * 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 (have_anyenum && elem_typeid != ANYELEMENTOID) { - /* - * 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 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_common_type || have_common_type_array) && n_actual_types > 0) { - if (allow_poly) - { - elem_typeid = ANYELEMENTOID; - array_typeid = ANYARRAYOID; - range_typeid = ANYRANGEOID; - } - else + common_type_typeid = select_common_type_from_vector(n_actual_types, + actual_types, + true); + + if (have_common_type_array) { - /* 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"))); + common_type_array_typeid = get_array_type(common_type_typeid); + + if (!OidIsValid(common_type_array_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(common_type_typeid)))); } - } - 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)))); - } + 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) + declared_arg_types[j] = common_type_typeid; + else if (decl_type == COMMONTYPEARRAYOID) + declared_arg_types[j] = common_type_array_typeid; + } } /* @@ -1965,6 +2140,28 @@ enforce_generic_type_consistency(const Oid *actual_arg_types, rettype == ANYENUMOID) return elem_typeid; + if (rettype == COMMONTYPEOID) + { + if (!OidIsValid(common_type_typeid)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find common type"))); + } + return common_type_typeid; + } + + if (rettype == COMMONTYPEARRAYOID) + { + if (!OidIsValid(common_type_array_typeid)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find common array type"))); + } + return common_type_array_typeid; + } + /* we don't return a generic type; send back the original return type */ return rettype; } @@ -2060,6 +2257,37 @@ resolve_generic_type(Oid declared_type, return context_actual_type; } } + else if (declared_type == COMMONTYPEOID) + { + 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 (declared_type == COMMONTYPEARRAYOID) + { + if (context_declared_type == COMMONTYPEOID) + { + /* 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 { /* declared_type isn't polymorphic, so return it as-is */ @@ -2142,8 +2370,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 +2384,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 2f780b9941..19dfeee2d3 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 eeaab2f4c9..b3812ee623 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -1786,7 +1786,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 f47a498228..7821b4d595 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 0ae9d7b9c5..3e97fb423e 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 dbe67cdb4c..487ec1d9c6 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. @@ -418,3 +445,5 @@ 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); diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index c4df255f10..1a303f1366 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -439,10 +439,15 @@ 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; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; Oid anyrange_type = InvalidOid; + Oid commontype_type = InvalidOid; + Oid commontypearray_type = InvalidOid; Oid anycollation = InvalidOid; + Oid ctcollation = InvalidOid; int i; /* See if there are any polymorphic outputs; quick out if not */ @@ -467,12 +472,19 @@ 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; default: break; } } if (!have_anyelement_result && !have_anyarray_result && - !have_anyrange_result) + !have_anyrange_result && + !have_commontype_result && !have_commontypearray_result) return true; /* @@ -500,14 +512,24 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, if (!OidIsValid(anyrange_type)) anyrange_type = get_call_expr_argtype(call_expr, i); break; + case COMMONTYPEOID: + 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; 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 */ @@ -535,6 +557,16 @@ 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)) + commontype_type = resolve_generic_type(COMMONTYPEOID, + commontypearray_type, + COMMONTYPEARRAYOID); + /* * We can't deduce a range type from other polymorphic inputs, because * there may be multiple range types for the same subtype. @@ -561,7 +593,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 @@ -572,6 +609,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 */ @@ -607,6 +647,22 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, 0); /* no collation should be attached to a range type */ break; + case COMMONTYPEOID: + 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; default: break; } @@ -631,9 +687,13 @@ 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; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; Oid anyrange_type = InvalidOid; + Oid commontype_type = InvalidOid; + Oid commontypearray_type = InvalidOid; int inargno; int i; @@ -692,6 +752,36 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, argtypes[i] = anyrange_type; } break; + case COMMONTYPEOID: + 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; default: break; } @@ -701,48 +791,79 @@ 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 (have_anyelement_result || have_anyarray_result || have_anyrange_result) + { + /* 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 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_commontype_result || have_commontypearray_result) { - if (OidIsValid(anyarray_type)) - anyelement_type = resolve_generic_type(ANYELEMENTOID, - anyarray_type, - ANYARRAYOID); - if (OidIsValid(anyrange_type)) + if (have_commontype_result && !OidIsValid(commontype_type)) { - Oid subtype = resolve_generic_type(ANYELEMENTOID, - anyrange_type, - ANYRANGEOID); + if (OidIsValid(commontypearray_type)) + { + commontype_type = resolve_generic_type(COMMONTYPEOID, + commontypearray_type, + COMMONTYPEARRAYOID); + } + else + return false; + } - /* check for inconsistent array and range results */ - if (OidIsValid(anyelement_type) && anyelement_type != subtype) + if (have_commontypearray_result || !OidIsValid(commontypearray_type)) + { + if (OidIsValid(commontype_type)) + { + commontypearray_type = resolve_generic_type(COMMONTYPEARRAYOID, + commontype_type, + COMMONTYPEOID); + } + else 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 */ - /* And finally replace the output column types as needed */ for (i = 0; i < numargs; i++) { @@ -759,6 +880,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, case ANYRANGEOID: argtypes[i] = anyrange_type; break; + case COMMONTYPEOID: + argtypes[i] = commontype_type; + break; + case COMMONTYPEARRAYOID: + argtypes[i] = commontypearray_type; + break; default: break; } diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 034a41eb55..b72061ca04 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6918,6 +6918,18 @@ { oid => '3312', descr => 'I/O', proname => 'tsm_handler_out', prorettype => 'cstring', proargtypes => 'tsm_handler', prosrc => 'tsm_handler_out' }, +{ oid => '2227', descr => 'I/O', + proname => 'commontype_in', prorettype => 'commontype', + proargtypes => 'cstring', prosrc => 'commontype_in' }, +{ oid => '2228', descr => 'I/O', + proname => 'commontype_out', prorettype => 'cstring', + proargtypes => 'commontype', prosrc => 'commontype_out' }, +{ oid => '2233', descr => 'I/O', + proname => 'commontypearray_in', prorettype => 'commontypearray', + proargtypes => 'cstring', prosrc => 'commontypearray_in' }, +{ oid => '2234', descr => 'I/O', + proname => 'commontypearray_out', prorettype => 'cstring', + proargtypes => 'commontypearray', prosrc => 'commontypearray_out' }, # tablesample method handlers { oid => '3313', descr => 'BERNOULLI tablesample method handler', @@ -7415,6 +7427,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', diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index d295eae1b9..91a3dca327 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -585,5 +585,14 @@ 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' }, ] diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 05185dd809..c7c86e0561 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -279,13 +279,21 @@ 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) + +#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 8bacc74cce..fa7254df43 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -505,11 +505,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? */ } @@ -2403,9 +2403,11 @@ plpgsql_resolve_polymorphic_argtypes(int numargs, case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: /* XXX dubious */ + case COMMONTYPEOID: argtypes[i] = INT4OID; break; case ANYARRAYOID: + case COMMONTYPEARRAYOID: argtypes[i] = INT4ARRAYOID; break; case ANYRANGEOID: diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 986417a188..a864c073e4 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1547,3 +1547,127 @@ 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; +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) + diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql index 03606671d9..12f241410f 100644 --- a/src/test/regress/sql/polymorphism.sql +++ b/src/test/regress/sql/polymorphism.sql @@ -814,3 +814,62 @@ 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; + +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);