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);

Reply via email to