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