Re: proposal: new polymorphic types - commontype and commontypearray

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: proposal: new polymorphic types - commontype and commontypearray
Дата
Msg-id 24137.1584139352@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: proposal: new polymorphic types - commontype and commontypearray  (Pavel Stehule <pavel.stehule@gmail.com>)
Ответы Re: proposal: new polymorphic types - commontype and commontypearray  (Pavel Stehule <pavel.stehule@gmail.com>)
Список pgsql-hackers
Pavel Stehule <pavel.stehule@gmail.com> writes:
> [ anycompatible-types-20191127.patch ]

I'm starting to review this patch seriously.  I've found some bugs and
things I didn't like, and the documentation certainly needs work, but
I think I can get it to a committable state before too much longer.

What I want to talk about right now is some preliminary refactoring
that I'd like to do, as shown in the 0001 patch below.  (0002 is the
rest of the patch as I currently have it.)  There are two main things
in it:

1. Rearrange the macros in pseudotypes.c so that we don't have any
pure-boilerplate functions that aren't built by the macros.  I don't
think this should be controversial, as it's not changing anything
functionally.

2. Refactor the function signature validation logic in pg_proc.c and
pg_aggregate.c to avoid having duplicate logic between those two.
I did that by creating new functions in parse_coerce.c (for lack of
a better place) that say whether a proposed result type or aggregate
transition type is valid given a particular set of declared input types.
The reason that this might be controversial is that it forces a slightly
less precise error detail message to be issued, since the call site that's
throwing the error doesn't know exactly which rule was being violated.
(For example, before there was a specific error message about anyrange
result requiring an anyrange input, and now there isn't.)

I think this is all right, mainly because we'd probably end up with
less-precise messages anyway for the more complex rules that 0002 is
going to add.  If anybody's really hot about it, we could complicate
the API, say by having the call sites pass in the primary error message
or by having the checking subroutines pass back an errdetail string.

We definitely need to do *something* about that, because it's already
the case that pg_aggregate.c is out of step with pg_proc.c about
polymorphism rules --- it's not enforcing the anyrange rule.  I think
there's probably no user-reachable bug in that, because an aggregate
is constrained by its implementation functions for which the rule
would be enforced, but it still seems not good.

Thoughts?

            regards, tom lane

diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 0b7face..354d00a 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -93,8 +93,6 @@ AggregateCreate(const char *aggName,
     Oid            mfinalfn = InvalidOid;    /* can be omitted */
     Oid            sortop = InvalidOid;    /* can be omitted */
     Oid           *aggArgTypes = parameterTypes->values;
-    bool        hasPolyArg;
-    bool        hasInternalArg;
     bool        mtransIsStrict = false;
     Oid            rettype;
     Oid            finaltype;
@@ -131,36 +129,29 @@ AggregateCreate(const char *aggName,
                                FUNC_MAX_ARGS - 1,
                                FUNC_MAX_ARGS - 1)));

-    /* check for polymorphic and INTERNAL arguments */
-    hasPolyArg = false;
-    hasInternalArg = false;
-    for (i = 0; i < numArgs; i++)
-    {
-        if (IsPolymorphicType(aggArgTypes[i]))
-            hasPolyArg = true;
-        else if (aggArgTypes[i] == INTERNALOID)
-            hasInternalArg = true;
-    }
-
     /*
      * 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 (!is_valid_polymorphic_signature(aggTransType,
+                                        aggArgTypes,
+                                        numArgs))
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                  errmsg("cannot determine transition data type"),
-                 errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic
argument.")));
+                 errdetail("An aggregate using a polymorphic transition type must have at least one matching
polymorphicargument."))); 

     /*
      * Likewise for moving-aggregate transtype, if any
      */
     if (OidIsValid(aggmTransType) &&
-        IsPolymorphicType(aggmTransType) && !hasPolyArg)
+        !is_valid_polymorphic_signature(aggmTransType,
+                                        aggArgTypes,
+                                        numArgs))
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                  errmsg("cannot determine transition data type"),
-                 errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic
argument.")));
+                 errdetail("An aggregate using a polymorphic transition type must have at least one matching
polymorphicargument."))); 

     /*
      * An ordered-set aggregate that is VARIADIC must be VARIADIC ANY.  In
@@ -492,12 +483,13 @@ AggregateCreate(const char *aggName,
      * that itself violates the rule against polymorphic result with no
      * polymorphic input.)
      */
-    if (IsPolymorphicType(finaltype) && !hasPolyArg)
+    if (!is_valid_polymorphic_signature(finaltype,
+                                        aggArgTypes,
+                                        numArgs))
         ereport(ERROR,
                 (errcode(ERRCODE_DATATYPE_MISMATCH),
                  errmsg("cannot determine result data type"),
-                 errdetail("An aggregate returning a polymorphic type "
-                           "must have at least one polymorphic argument.")));
+                 errdetail("An aggregate returning a polymorphic type must have at least one matching polymorphic
argument.")));

     /*
      * Also, the return type can't be INTERNAL unless there's at least one
@@ -505,7 +497,9 @@ AggregateCreate(const char *aggName,
      * for regular functions, but at the level of aggregates.  We must test
      * this explicitly because we allow INTERNAL as the transtype.
      */
-    if (finaltype == INTERNALOID && !hasInternalArg)
+    if (!is_valid_internal_signature(finaltype,
+                                     aggArgTypes,
+                                     numArgs))
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                  errmsg("unsafe use of pseudo-type \"internal\""),
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 423fd79..c96a055 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -32,6 +32,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
 #include "parser/parse_type.h"
 #include "tcop/pquery.h"
 #include "tcop/tcopprot.h"
@@ -97,12 +98,6 @@ ProcedureCreate(const char *procedureName,
     int            allParamCount;
     Oid           *allParams;
     char       *paramModes = NULL;
-    bool        genericInParam = false;
-    bool        genericOutParam = false;
-    bool        anyrangeInParam = false;
-    bool        anyrangeOutParam = false;
-    bool        internalInParam = false;
-    bool        internalOutParam = false;
     Oid            variadicType = InvalidOid;
     Acl           *proacl = NULL;
     Relation    rel;
@@ -178,29 +173,32 @@ ProcedureCreate(const char *procedureName,
     }

     /*
-     * Detect whether we have polymorphic or INTERNAL arguments.  The first
-     * loop checks input arguments, the second output arguments.
+     * Do not allow polymorphic return type unless there is a polymorphic
+     * input argument that we can use to deduce the actual return type.
      */
-    for (i = 0; i < parameterCount; i++)
-    {
-        switch (parameterTypes->values[i])
-        {
-            case ANYARRAYOID:
-            case ANYELEMENTOID:
-            case ANYNONARRAYOID:
-            case ANYENUMOID:
-                genericInParam = true;
-                break;
-            case ANYRANGEOID:
-                genericInParam = true;
-                anyrangeInParam = true;
-                break;
-            case INTERNALOID:
-                internalInParam = true;
-                break;
-        }
-    }
+    if (!is_valid_polymorphic_signature(returnType,
+                                        parameterTypes->values,
+                                        parameterCount))
+        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 matching polymorphic
argument.")));
+
+    /*
+     * Also, do not allow return type INTERNAL unless at least one input
+     * argument is INTERNAL.
+     */
+    if (!is_valid_internal_signature(returnType,
+                                     parameterTypes->values,
+                                     parameterCount))
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                 errmsg("unsafe use of pseudo-type \"internal\""),
+                 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));

+    /*
+     * Apply the same tests to any OUT arguments.
+     */
     if (allParameterTypes != PointerGetDatum(NULL))
     {
         for (i = 0; i < allParamCount; i++)
@@ -210,52 +208,24 @@ ProcedureCreate(const char *procedureName,
                 paramModes[i] == PROARGMODE_VARIADIC)
                 continue;        /* ignore input-only params */

-            switch (allParams[i])
-            {
-                case ANYARRAYOID:
-                case ANYELEMENTOID:
-                case ANYNONARRAYOID:
-                case ANYENUMOID:
-                    genericOutParam = true;
-                    break;
-                case ANYRANGEOID:
-                    genericOutParam = true;
-                    anyrangeOutParam = true;
-                    break;
-                case INTERNALOID:
-                    internalOutParam = true;
-                    break;
-            }
+            if (!is_valid_polymorphic_signature(allParams[i],
+                                                parameterTypes->values,
+                                                parameterCount))
+                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 matching
polymorphicargument."))); 
+            if (!is_valid_internal_signature(allParams[i],
+                                             parameterTypes->values,
+                                             parameterCount))
+                ereport(ERROR,
+                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                         errmsg("unsafe use of pseudo-type \"internal\""),
+                         errdetail("A function returning \"internal\" must have at least one \"internal\"
argument.")));
         }
     }

-    /*
-     * Do not allow polymorphic return type unless at least one input argument
-     * is polymorphic.  ANYRANGE return type is even stricter: must have an
-     * ANYRANGE input (since we can't deduce the specific range type from
-     * ANYELEMENT).  Also, do not allow return type INTERNAL unless at least
-     * one input argument is INTERNAL.
-     */
-    if ((IsPolymorphicType(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 ((returnType == ANYRANGEOID || anyrangeOutParam) &&
-        !anyrangeInParam)
-        ereport(ERROR,
-                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
-                 errmsg("cannot determine result data type"),
-                 errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
-
-    if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
-        ereport(ERROR,
-                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
-                 errmsg("unsafe use of pseudo-type \"internal\""),
-                 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
-
+    /* Identify variadic argument type, if any */
     if (paramModes != NULL)
     {
         /*
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 929f758..7318b72 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -2070,6 +2070,71 @@ resolve_generic_type(Oid declared_type,
     return InvalidOid;            /* keep compiler quiet */
 }

+/*
+ * is_valid_polymorphic_signature()
+ *        Is a proposed function signature valid per polymorphism rules?
+ *
+ * Returns false if ret_type is polymorphic but cannot be inferred from
+ * the given declared argument types.
+ */
+bool
+is_valid_polymorphic_signature(Oid ret_type,
+                               const Oid *declared_arg_types,
+                               int nargs)
+{
+    if (ret_type == ANYRANGEOID)
+    {
+        /*
+         * ANYRANGE requires an ANYRANGE input, else we can't tell which of
+         * several range types with the same element type to use.
+         */
+        for (int i = 0; i < nargs; i++)
+        {
+            if (declared_arg_types[i] == ret_type)
+                return true;    /* OK */
+        }
+        return false;
+    }
+    else if (IsPolymorphicType(ret_type))
+    {
+        /* Otherwise, any polymorphic type can be deduced from any other */
+        for (int i = 0; i < nargs; i++)
+        {
+            if (IsPolymorphicType(declared_arg_types[i]))
+                return true;    /* OK */
+        }
+        return false;
+    }
+    else
+        return true;            /* OK, ret_type is not polymorphic */
+}
+
+/*
+ * is_valid_internal_signature()
+ *        Is a proposed function signature valid per INTERNAL safety rules?
+ *
+ * Returns false if ret_type is INTERNAL but none of the declared arg types
+ * are.  It's unsafe to create such a function since it would allow
+ * invocation of INTERNAL-consuming functions directly from SQL.
+ */
+bool
+is_valid_internal_signature(Oid ret_type,
+                            const Oid *declared_arg_types,
+                            int nargs)
+{
+    if (ret_type == INTERNALOID)
+    {
+        for (int i = 0; i < nargs; i++)
+        {
+            if (declared_arg_types[i] == ret_type)
+                return true;    /* OK */
+        }
+        return false;
+    }
+    else
+        return true;            /* OK, ret_type is not INTERNAL */
+}
+

 /* TypeCategory()
  *        Assign a category to the specified type OID.
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 4653fc3..6ab95dc 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -29,6 +29,74 @@


 /*
+ * Generate input and output functions for a pseudotype that will reject all
+ * input and output attempts.  (But for some types, only the input function
+ * need be dummy.)
+ */
+#define PSEUDOTYPE_DUMMY_INPUT_FUNC(typname) \
+Datum \
+typname##_in(PG_FUNCTION_ARGS) \
+{ \
+    ereport(ERROR, \
+            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
+             errmsg("cannot accept a value of type %s", #typname))); \
+\
+    PG_RETURN_VOID();            /* keep compiler quiet */ \
+} \
+\
+extern int no_such_variable
+
+#define PSEUDOTYPE_DUMMY_IO_FUNCS(typname) \
+PSEUDOTYPE_DUMMY_INPUT_FUNC(typname); \
+\
+Datum \
+typname##_out(PG_FUNCTION_ARGS) \
+{ \
+    ereport(ERROR, \
+            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
+             errmsg("cannot display a value of type %s", #typname))); \
+\
+    PG_RETURN_VOID();            /* keep compiler quiet */ \
+} \
+\
+extern int no_such_variable
+
+/*
+ * Likewise for binary send/receive functions.  We don't bother with these
+ * at all for many pseudotypes, but some have them.  (By convention, if
+ * a type has a send function it should have a receive function, even if
+ * that's only dummy.)
+ */
+#define PSEUDOTYPE_DUMMY_RECEIVE_FUNC(typname) \
+Datum \
+typname##_recv(PG_FUNCTION_ARGS) \
+{ \
+    ereport(ERROR, \
+            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
+             errmsg("cannot accept a value of type %s", #typname))); \
+\
+    PG_RETURN_VOID();            /* keep compiler quiet */ \
+} \
+\
+extern int no_such_variable
+
+#define PSEUDOTYPE_DUMMY_BINARY_IO_FUNCS(typname) \
+PSEUDOTYPE_DUMMY_RECEIVE_FUNC(typname); \
+\
+Datum \
+typname##_send(PG_FUNCTION_ARGS) \
+{ \
+    ereport(ERROR, \
+            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
+             errmsg("cannot display a value of type %s", #typname))); \
+\
+    PG_RETURN_VOID();            /* keep compiler quiet */ \
+} \
+\
+extern int no_such_variable
+
+
+/*
  * cstring_in        - input routine for pseudo-type CSTRING.
  *
  * We might as well allow this to support constructs like "foo_in('blah')".
@@ -84,22 +152,18 @@ cstring_send(PG_FUNCTION_ARGS)
 }

 /*
- * anyarray_in        - input routine for pseudo-type ANYARRAY.
+ * anyarray
+ *
+ * XXX anyarray_recv 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
-anyarray_in(PG_FUNCTION_ARGS)
-{
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot accept a value of type %s", "anyarray")));
-
-    PG_RETURN_VOID();            /* keep compiler quiet */
-}
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anyarray);
+PSEUDOTYPE_DUMMY_RECEIVE_FUNC(anyarray);

 /*
- * anyarray_out        - output routine for pseudo-type ANYARRAY.
- *
- * We may as well allow this, since array_out will in fact work.
+ * We need to allow output so that, e.g., pg_statistic columns can be
+ * printed.
  */
 Datum
 anyarray_out(PG_FUNCTION_ARGS)
@@ -107,53 +171,19 @@ anyarray_out(PG_FUNCTION_ARGS)
     return array_out(fcinfo);
 }

-/*
- * anyarray_recv        - binary input routine for pseudo-type ANYARRAY.
- *
- * 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
-anyarray_recv(PG_FUNCTION_ARGS)
-{
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot accept a value of type %s", "anyarray")));
-
-    PG_RETURN_VOID();            /* keep compiler quiet */
-}
-
-/*
- * anyarray_send        - binary output routine for pseudo-type ANYARRAY.
- *
- * We may as well allow this, since array_send will in fact work.
- */
 Datum
 anyarray_send(PG_FUNCTION_ARGS)
 {
     return array_send(fcinfo);
 }

-
 /*
- * anyenum_in        - input routine for pseudo-type ANYENUM.
- */
-Datum
-anyenum_in(PG_FUNCTION_ARGS)
-{
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot accept a value of type %s", "anyenum")));
-
-    PG_RETURN_VOID();            /* keep compiler quiet */
-}
-
-/*
- * anyenum_out        - output routine for pseudo-type ANYENUM.
+ * anyenum
  *
- * We may as well allow this, since enum_out will in fact work.
+ * We may as well allow output, since enum_out will in fact work.
  */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anyenum);
+
 Datum
 anyenum_out(PG_FUNCTION_ARGS)
 {
@@ -161,23 +191,12 @@ anyenum_out(PG_FUNCTION_ARGS)
 }

 /*
- * anyrange_in        - input routine for pseudo-type ANYRANGE.
- */
-Datum
-anyrange_in(PG_FUNCTION_ARGS)
-{
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot accept a value of type %s", "anyrange")));
-
-    PG_RETURN_VOID();            /* keep compiler quiet */
-}
-
-/*
- * anyrange_out        - output routine for pseudo-type ANYRANGE.
+ * anyrange
  *
- * We may as well allow this, since range_out will in fact work.
+ * We may as well allow output, since range_out will in fact work.
  */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anyrange);
+
 Datum
 anyrange_out(PG_FUNCTION_ARGS)
 {
@@ -264,29 +283,20 @@ shell_out(PG_FUNCTION_ARGS)


 /*
- * pg_node_tree_in        - input routine for type PG_NODE_TREE.
+ * pg_node_tree
  *
  * pg_node_tree isn't really a pseudotype --- it's real enough to be a table
  * column --- but it presently has no operations of its own, and disallows
  * input too, so its I/O functions seem to fit here as much as anywhere.
+ *
+ * We disallow input of pg_node_tree values because the SQL functions that
+ * operate on the type are not secure against malformed input.
  */
-Datum
-pg_node_tree_in(PG_FUNCTION_ARGS)
-{
-    /*
-     * We disallow input of pg_node_tree values because the SQL functions that
-     * operate on the type are not secure against malformed input.
-     */
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot accept a value of type %s", "pg_node_tree")));
-
-    PG_RETURN_VOID();            /* keep compiler quiet */
-}
-
+PSEUDOTYPE_DUMMY_INPUT_FUNC(pg_node_tree);
+PSEUDOTYPE_DUMMY_RECEIVE_FUNC(pg_node_tree);

 /*
- * pg_node_tree_out        - output routine for type PG_NODE_TREE.
+ * We do want to allow output, though.
  *
  * The internal representation is the same as TEXT, so just pass it off.
  */
@@ -296,22 +306,6 @@ pg_node_tree_out(PG_FUNCTION_ARGS)
     return textout(fcinfo);
 }

-/*
- * pg_node_tree_recv        - binary input routine for type PG_NODE_TREE.
- */
-Datum
-pg_node_tree_recv(PG_FUNCTION_ARGS)
-{
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot accept a value of type %s", "pg_node_tree")));
-
-    PG_RETURN_VOID();            /* keep compiler quiet */
-}
-
-/*
- * pg_node_tree_send        - binary output routine for type PG_NODE_TREE.
- */
 Datum
 pg_node_tree_send(PG_FUNCTION_ARGS)
 {
@@ -319,102 +313,29 @@ pg_node_tree_send(PG_FUNCTION_ARGS)
 }

 /*
- * pg_ddl_command_in    - input routine for type PG_DDL_COMMAND.
+ * pg_ddl_command
  *
  * Like pg_node_tree, pg_ddl_command isn't really a pseudotype; it's here for
  * the same reasons as that one.
- */
-Datum
-pg_ddl_command_in(PG_FUNCTION_ARGS)
-{
-    /*
-     * Disallow input of pg_ddl_command value.
-     */
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot accept a value of type %s", "pg_ddl_command")));
-
-    PG_RETURN_VOID();            /* keep compiler quiet */
-}
-
-/*
- * pg_ddl_command_out        - output routine for type PG_DDL_COMMAND.
  *
- * We don't have any good way to output this type directly, so punt.
- */
-Datum
-pg_ddl_command_out(PG_FUNCTION_ARGS)
-{
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot output a value of type %s", "pg_ddl_command")));
-
-    PG_RETURN_VOID();
-}
-
-/*
- * pg_ddl_command_recv        - binary input routine for type PG_DDL_COMMAND.
- */
-Datum
-pg_ddl_command_recv(PG_FUNCTION_ARGS)
-{
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot accept a value of type %s", "pg_ddl_command")));
-
-    PG_RETURN_VOID();
-}
-
-/*
- * pg_ddl_command_send        - binary output routine for type PG_DDL_COMMAND.
+ * We don't have any good way to output this type directly, so punt
+ * for output as well as input.
  */
-Datum
-pg_ddl_command_send(PG_FUNCTION_ARGS)
-{
-    ereport(ERROR,
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-             errmsg("cannot output a value of type %s", "pg_ddl_command")));
-
-    PG_RETURN_VOID();
-}
+PSEUDOTYPE_DUMMY_IO_FUNCS(pg_ddl_command);
+PSEUDOTYPE_DUMMY_BINARY_IO_FUNCS(pg_ddl_command);


 /*
- * Generate input and output functions for a pseudotype that will reject all
- * input and output attempts.
+ * Dummy I/O functions for various other pseudotypes.
  */
-#define PSEUDOTYPE_DUMMY_IO_FUNCS(typname) \
-\
-Datum \
-typname##_in(PG_FUNCTION_ARGS) \
-{ \
-    ereport(ERROR, \
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
-             errmsg("cannot accept a value of type %s", #typname))); \
-\
-    PG_RETURN_VOID();            /* keep compiler quiet */ \
-} \
-\
-Datum \
-typname##_out(PG_FUNCTION_ARGS) \
-{ \
-    ereport(ERROR, \
-            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
-             errmsg("cannot display a value of type %s", #typname))); \
-\
-    PG_RETURN_VOID();            /* keep compiler quiet */ \
-} \
-\
-extern int no_such_variable
-
 PSEUDOTYPE_DUMMY_IO_FUNCS(any);
 PSEUDOTYPE_DUMMY_IO_FUNCS(trigger);
 PSEUDOTYPE_DUMMY_IO_FUNCS(event_trigger);
 PSEUDOTYPE_DUMMY_IO_FUNCS(language_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(fdw_handler);
+PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(index_am_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
-PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler);
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index d6a95c1..fce84d6 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -83,6 +83,13 @@ extern Oid    resolve_generic_type(Oid declared_type,
                                  Oid context_actual_type,
                                  Oid context_declared_type);

+extern bool is_valid_polymorphic_signature(Oid ret_type,
+                                           const Oid *declared_arg_types,
+                                           int nargs);
+extern bool is_valid_internal_signature(Oid ret_type,
+                                        const Oid *declared_arg_types,
+                                        int nargs);
+
 extern CoercionPathType find_coercion_pathway(Oid targetTypeId,
                                               Oid sourceTypeId,
                                               CoercionContext ccontext,
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 986417a..d436d29 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -81,7 +81,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
 CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --     N    P
 -- should CREATE
 CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -93,11 +93,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
 CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    Case2 (R = P) && ((B = P) || (B = N))
 --    -------------------------------------
 --    S    tf1      B    tf2
@@ -152,13 +152,13 @@ ERROR:  function tfp(integer[], anyelement) does not exist
 CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    P    N        N    P
 -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
 CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    P    N        P    N
 -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
 CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -174,21 +174,21 @@ ERROR:  function tf2p(anyarray, anyelement) does not exist
 CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    P    P        N    P
 -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
 CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    P    P        P    N
 -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
 CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -218,11 +218,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
 CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --     N    P
 -- should CREATE
 CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -232,7 +232,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
 CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    Case4 (R = N) && ((B = P) || (B = N))
 --    -------------------------------------
 --    S    tf1      B    tf2
@@ -286,21 +286,21 @@ ERROR:  function tfp(integer[], anyelement) does not exist
 CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    P    N        N    P
 -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
 CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    P    N        P    N
 -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
 CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -322,13 +322,13 @@ ERROR:  function tf2p(anyarray, anyelement) does not exist
 CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    P    P        N    P
 -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
 CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one matching polymorphic argument.
 --    P    P        P    N
 -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
 CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index a70060b..ac1abf6 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
 CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
 AS 'select $1, array[$1,$1]' LANGUAGE sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning a polymorphic type must have at least one polymorphic argument.
+DETAIL:  A function returning a polymorphic type must have at least one matching polymorphic argument.
 --
 -- table functions
 --
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 220f2d9..fc82f87 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -1371,12 +1371,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
 create function bogus_func(anyelement)
   returns anyrange as 'select int4range(1,10)' language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL:  A function returning a polymorphic type must have at least one matching polymorphic argument.
 -- should fail
 create function bogus_func(int)
   returns anyrange as 'select int4range(1,10)' language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning a polymorphic type must have at least one polymorphic argument.
+DETAIL:  A function returning a polymorphic type must have at least one matching polymorphic argument.
 create function range_add_bounds(anyrange)
   returns anyelement as 'select lower($1) + upper($1)' language sql;
 select range_add_bounds(int4range(1, 17));
@@ -1510,14 +1510,14 @@ select * from table_succeed(123, int4range(1,11));
 create function outparam_fail(i anyelement, out r anyrange, out t text)
   as $$ select '[1,10]', 'foo' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL:  A function returning a polymorphic type must have at least one matching polymorphic argument.
 --should fail
 create function inoutparam_fail(inout i anyelement, out r anyrange)
   as $$ select $1, '[1,10]' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL:  A function returning a polymorphic type must have at least one matching polymorphic argument.
 --should fail
 create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
   as $$ select $1, '[1,10]' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL:  A function returning a polymorphic type must have at least one matching polymorphic argument.
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 410eaed..6407d3d 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4784,6 +4784,14 @@ SELECT * FROM pg_attribute
    </indexterm>

    <indexterm zone="datatype-pseudo">
+    <primary>anycompatible</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-pseudo">
+    <primary>anycompatiblearray</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-pseudo">
     <primary>void</primary>
    </indexterm>

@@ -4889,6 +4897,34 @@ SELECT * FROM pg_attribute
        </row>

        <row>
+        <entry><type>anycompatible</type></entry>
+        <entry>Indicates that a function accepts any data type. Values
+        are converted to real common type.
+        (see <xref linkend="extend-types-polymorphic"/>).</entry>
+       </row>
+
+       <row>
+        <entry><type>anycompatiblearray</type></entry>
+        <entry>Indicates that a function accepts any array data type. The
+        elements of array are converted to common type of these values.
+        (see <xref linkend="extend-types-polymorphic"/>).</entry>
+       </row>
+
+       <row>
+        <entry><type>anycompatiblenonarray</type></entry>
+        <entry>Indicates that a function accepts any non-array data type
+        (see <xref linkend="extend-types-polymorphic"/>).</entry>
+       </row>
+
+       <row>
+        <entry><type>anycompatiblerange</type></entry>
+        <entry>Indicates that a function accepts any range data type
+        (see <xref linkend="extend-types-polymorphic"/> and
+        <xref linkend="rangetypes"/>). The subtype can be used for
+        deduction of common type.</entry>
+       </row>
+
+       <row>
         <entry><type>cstring</type></entry>
         <entry>Indicates that a function accepts or returns a null-terminated C string.</entry>
        </row>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 9ec1af7..05c0919 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -231,7 +231,7 @@
     <para>
      Five pseudo-types of special interest are <type>anyelement</type>,
      <type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
-     and <type>anyrange</type>,
+     <type>anyrange</type>, <type>anycompatible</type> and <type>anycompatiblearray</type>.
      which are collectively called <firstterm>polymorphic types</firstterm>.
      Any function declared using these types is said to be
      a <firstterm>polymorphic function</firstterm>.  A polymorphic function can
@@ -268,6 +268,15 @@
     </para>

     <para>
+     Second family of polymorphic types are types <type>anycompatible</type> and
+     <type>anycompatiblearray</type>. These types are similar to types
+     <type>anyelement</type> and <type>anyarray</type>. The arguments declared
+     as <type>anyelement</type> requires same real type of passed values. For
+     <type>anycompatible</type>'s arguments is selected common type, and later
+     all these arguments are casted to this common type.
+    </para>
+
+    <para>
      Thus, when more than one argument position is declared with a polymorphic
      type, the net effect is that only certain combinations of actual argument
      types are allowed.  For example, a function declared as
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 76fd938..22540bb 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -404,10 +404,6 @@ ConstructTupleDescriptor(Relation heapRelation,
          */
         keyType = amroutine->amkeytype;

-        /*
-         * Code below is concerned to the opclasses which are not used with
-         * the included columns.
-         */
         if (i < indexInfo->ii_NumIndexKeyAttrs)
         {
             tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(classObjectId[i]));
@@ -422,6 +418,10 @@ ConstructTupleDescriptor(Relation heapRelation,
              * If keytype is specified as ANYELEMENT, and opcintype is
              * ANYARRAY, then the attribute type must be an array (else it'd
              * not have matched this opclass); use its element type.
+             *
+             * We could also allow ANYCOMPATIBLE/ANYCOMPATIBLEARRAY here, but
+             * there seems no need to do so; there's no reason to declare an
+             * opclass as taking ANYCOMPATIBLEARRAY rather than ANYARRAY.
              */
             if (keyType == ANYELEMENTOID && opclassTup->opcintype == ANYARRAYOID)
             {
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index c96a055..704668e 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -257,6 +257,9 @@ ProcedureCreate(const char *procedureName,
                         case ANYARRAYOID:
                             variadicType = ANYELEMENTOID;
                             break;
+                        case ANYCOMPATIBLEARRAYOID:
+                            variadicType = ANYCOMPATIBLEOID;
+                            break;
                         default:
                             variadicType = get_element_type(allParams[i]);
                             if (!OidIsValid(variadicType))
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 5eac55a..694114a 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -320,6 +320,7 @@ interpret_function_parameter_list(ParseState *pstate,
             switch (toid)
             {
                 case ANYARRAYOID:
+                case ANYCOMPATIBLEARRAYOID:
                 case ANYOID:
                     /* okay */
                     break;
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 7318b72..e5ea051 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -169,15 +169,17 @@ coerce_type(ParseState *pstate, Node *node,
     }
     if (targetTypeId == ANYOID ||
         targetTypeId == ANYELEMENTOID ||
-        targetTypeId == ANYNONARRAYOID)
+        targetTypeId == ANYNONARRAYOID ||
+        targetTypeId == ANYCOMPATIBLEOID ||
+        targetTypeId == ANYCOMPATIBLENONARRAYOID)
     {
         /*
          * Assume can_coerce_type verified that implicit coercion is okay.
          *
          * Note: by returning the unmodified node here, we are saying that
          * it's OK to treat an UNKNOWN constant as a valid input for a
-         * function accepting ANY, ANYELEMENT, or ANYNONARRAY.  This should be
-         * all right, since an UNKNOWN value is still a perfectly valid Datum.
+         * function accepting one of these pseudotypes.  This should be all
+         * right, since an UNKNOWN value is still a perfectly valid Datum.
          *
          * NB: we do NOT want a RelabelType here: the exposed type of the
          * function argument must be its actual type, not the polymorphic
@@ -187,7 +189,9 @@ coerce_type(ParseState *pstate, Node *node,
     }
     if (targetTypeId == ANYARRAYOID ||
         targetTypeId == ANYENUMOID ||
-        targetTypeId == ANYRANGEOID)
+        targetTypeId == ANYRANGEOID ||
+        targetTypeId == ANYCOMPATIBLEARRAYOID ||
+        targetTypeId == ANYCOMPATIBLERANGEOID)
     {
         /*
          * Assume can_coerce_type verified that implicit coercion is okay.
@@ -195,10 +199,10 @@ coerce_type(ParseState *pstate, Node *node,
          * These cases are unlike the ones above because the exposed type of
          * the argument must be an actual array, enum, or range type.  In
          * particular the argument must *not* be an UNKNOWN constant.  If it
-         * is, we just fall through; below, we'll call anyarray_in,
-         * anyenum_in, or anyrange_in, which will produce an error.  Also, if
-         * what we have is a domain over array, enum, or range, we have to
-         * relabel it to its base type.
+         * is, we just fall through; below, we'll call the pseudotype's input
+         * function, which will produce an error.  Also, if what we have is a
+         * domain over array, enum, or range, we have to relabel it to its
+         * base type.
          *
          * Note: currently, we can't actually see a domain-over-enum here,
          * since the other functions in this file will not match such a
@@ -1389,6 +1393,99 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
 }

 /*
+ * 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, we can
+     * skip first i elements, because was checked in previous loop.
+     */
+    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.
  *
@@ -1447,6 +1544,13 @@ coerce_to_common_type(ParseState *pstate, Node *node,
  *      we add the extra condition that the ANYELEMENT type must not be an array.
  *      (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
  *      is an extra restriction if not.)
+ * 8) Previous rules are valid too for ANYCOMPATIBLE, ANYCOMPATIBLEARRAY,
+ *      ANYCOMPATIBLENONARRAY and ANYCOMPATIBLERANGE with following exception.
+ *      The used datatypes should not be strictly same. These types should be
+ *      from same type category, and for any used type there should be implicit
+ *      cast to selected one of these types.
+ * 9) There is not any relation between ANY types and ANYCOMPATIBLE types.
+ *      Isn't possible derive ANY type from ANYCOMPATIBLE type and vice versa.
  *
  * Domains over arrays match ANYARRAY, and are immediately flattened to their
  * base type.  (Thus, for example, we will consider it a match if one ANYARRAY
@@ -1484,6 +1588,12 @@ check_generic_type_consistency(const Oid *actual_arg_types,
     bool        have_anyelement = false;
     bool        have_anynonarray = false;
     bool        have_anyenum = false;
+    bool        have_anycompatible_nonarray = false;
+    bool        have_anycompatible_range = false;
+    bool        have_generic_anycompatible = false;
+    Oid            anycompatible_range_typeid = InvalidOid;
+    Oid            anycompatible_actual_types[FUNC_MAX_ARGS];
+    int            n_anycompatible_args = 0;

     /*
      * Loop through the arguments to see if we have any that are polymorphic.
@@ -1527,6 +1637,74 @@ check_generic_type_consistency(const Oid *actual_arg_types,
                 return false;
             range_typeid = actual_type;
         }
+        else if (decl_type == ANYCOMPATIBLEOID ||
+                 decl_type == ANYCOMPATIBLENONARRAYOID)
+        {
+            have_generic_anycompatible = true;
+            if (decl_type == ANYCOMPATIBLENONARRAYOID)
+                have_anycompatible_nonarray = true;
+
+            /* An unknown literal is no help for resolving actual types */
+            if (actual_type == UNKNOWNOID)
+                continue;
+
+            /* collect used type, reduce repeated values */
+            if (n_anycompatible_args == 0 ||
+                anycompatible_actual_types[n_anycompatible_args - 1] != actual_type)
+                anycompatible_actual_types[n_anycompatible_args++] = actual_type;
+        }
+        else if (decl_type == ANYCOMPATIBLEARRAYOID)
+        {
+            Oid            anycompatible_elem_type;
+
+            have_generic_anycompatible = true;
+
+            if (actual_type == UNKNOWNOID)
+                continue;
+
+            actual_type = getBaseType(actual_type); /* flatten domains */
+            anycompatible_elem_type = get_element_type(actual_type);
+
+            if (!OidIsValid(anycompatible_elem_type))
+                return false;
+
+            /* collect used type, reduce repeated values */
+            if (n_anycompatible_args == 0 ||
+                anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_elem_type)
+                anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type;
+        }
+        else if (decl_type == ANYCOMPATIBLERANGEOID)
+        {
+            Oid            anycompatible_range_typelem;
+
+            have_generic_anycompatible = true;
+            have_anycompatible_range = true;
+
+            if (actual_type == UNKNOWNOID)
+                continue;
+            actual_type = getBaseType(actual_type); /* flatten domains */
+
+            /*
+             * range type is used just for derivation of common type, but
+             * range types should be same. Same behave like anyrange - cast
+             * between ranges are not supported.
+             */
+            if (OidIsValid(anycompatible_range_typeid) &&
+                anycompatible_range_typeid != actual_type)
+                return false;
+
+            anycompatible_range_typelem = get_range_subtype(actual_type);
+            if (!OidIsValid(anycompatible_range_typelem))
+                return false;
+
+            if (!OidIsValid(anycompatible_range_typeid))
+                anycompatible_range_typeid = actual_type;
+
+            /* collect used type, reduce repeated values */
+            if (n_anycompatible_args == 0 ||
+                anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_range_typelem)
+                anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+        }
     }

     /* Get the element type based on the array type, if we have one */
@@ -1593,6 +1771,36 @@ check_generic_type_consistency(const Oid *actual_arg_types,
             return false;
     }

+    /* check anycompatible collected data */
+    if (have_generic_anycompatible)
+    {
+        /* we can check type consisteny when we have some not unknown types */
+        if (n_anycompatible_args > 0)
+        {
+            Oid            anycompatible_typeid;
+
+            anycompatible_typeid = select_common_type_from_vector(n_anycompatible_args,
+                                                                  anycompatible_actual_types,
+                                                                  true);
+
+            if (!OidIsValid(anycompatible_typeid))
+                return false;
+
+            if (have_anycompatible_nonarray)
+            {
+                /*
+                 * require the anycompatible type to not be an array or domain
+                 * over array
+                 */
+                if (type_is_array_domain(anycompatible_typeid))
+                    return false;
+            }
+
+            if (have_anycompatible_range && !OidIsValid(anycompatible_range_typeid))
+                return false;
+        }
+    }
+
     /* Looks valid */
     return true;
 }
@@ -1645,6 +1853,16 @@ check_generic_type_consistency(const Oid *actual_arg_types,
  *      we add the extra condition that the ANYELEMENT type must not be an array.
  *      (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
  *      is an extra restriction if not.)
+ * 10) The relation between types from ANY family type are same like
+ *      relations between types from ANYCOMPATIBLE family type, with one
+ *      difference. The parameters with type from ANY family type requires
+ *      exactly one actual type. The arguments with ANYCOMPATIBLE family type
+ *      allows types that shares type category. Later polymorphic type is
+ *      replaced by real type. This real type is one from actual types
+ *      (polymorphic argument actual types) for that is available implicit
+ *      cast from type of any related polymorphic arguments.
+ * 11) The arguments with ANY family type and ANYCOMPATIBLE family type
+ *      are independent.
  *
  * Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
  * respectively, and are immediately flattened to their base type. (In
@@ -1675,11 +1893,15 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                  bool allow_poly)
 {
     int            j;
-    bool        have_generics = false;
+    bool        have_generics_any = false;
+    bool        have_generics_anycompatible = false;
     bool        have_unknowns = false;
     Oid            elem_typeid = InvalidOid;
     Oid            array_typeid = InvalidOid;
     Oid            range_typeid = InvalidOid;
+    Oid            anycompatible_typeid = InvalidOid;
+    Oid            anycompatible_array_typeid = InvalidOid;
+    Oid            anycompatible_range_typeid = InvalidOid;
     Oid            array_typelem;
     Oid            range_typelem;
     bool        have_anyelement = (rettype == ANYELEMENTOID ||
@@ -1687,6 +1909,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                    rettype == ANYENUMOID);
     bool        have_anynonarray = (rettype == ANYNONARRAYOID);
     bool        have_anyenum = (rettype == ANYENUMOID);
+    bool        have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
+    bool        have_anycompatible_array = (rettype == ANYCOMPATIBLEARRAYOID);
+    bool        have_anycompatible_range = (rettype == ANYCOMPATIBLERANGEOID);
+    Oid            anycompatible_actual_types[FUNC_MAX_ARGS];
+    int            n_anycompatible_args = 0;

     /*
      * Loop through the arguments to see if we have any that are polymorphic.
@@ -1701,7 +1928,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)
@@ -1724,14 +1951,18 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
         }
         else if (decl_type == ANYARRAYOID)
         {
-            have_generics = true;
+            have_generics_any = true;
+            have_anycompatible_array = true;
+
             if (actual_type == UNKNOWNOID)
             {
                 have_unknowns = true;
                 continue;
             }
+
             if (allow_poly && decl_type == actual_type)
                 continue;        /* no new information here */
+
             actual_type = getBaseType(actual_type); /* flatten domains */
             if (OidIsValid(array_typeid) && actual_type != array_typeid)
                 ereport(ERROR,
@@ -1744,7 +1975,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;
@@ -1762,128 +1993,300 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                    format_type_be(actual_type))));
             range_typeid = actual_type;
         }
-    }
+        else if (decl_type == ANYCOMPATIBLEOID ||
+                 decl_type == ANYCOMPATIBLENONARRAYOID)
+        {
+            have_generics_anycompatible = true;

-    /*
-     * Fast Track: if none of the arguments are polymorphic, return the
-     * unmodified rettype.  We assume it can't be polymorphic either.
-     */
-    if (!have_generics)
-        return rettype;
+            if (decl_type == ANYCOMPATIBLENONARRAYOID)
+                have_anycompatible_nonarray = true;

-    /* Get the element type based on the array type, if we have one */
-    if (OidIsValid(array_typeid))
-    {
-        if (array_typeid == ANYARRAYOID && !have_anyelement)
-        {
-            /* Special case for ANYARRAY input: okay iff no ANYELEMENT */
-            array_typelem = ANYELEMENTOID;
+            /*
+             * because declared type will be replaced every time, we don't
+             * need some special work for unknown types.
+             */
+            if (actual_type == UNKNOWNOID)
+                continue;
+
+            if (allow_poly && decl_type == actual_type)
+                continue;
+
+            /* collect used type, reduce repeated values */
+            if (n_anycompatible_args == 0 ||
+                anycompatible_actual_types[n_anycompatible_args - 1] != actual_type)
+                anycompatible_actual_types[n_anycompatible_args++] = actual_type;
         }
-        else
+        else if (decl_type == ANYCOMPATIBLEARRAYOID)
         {
-            array_typelem = get_element_type(array_typeid);
-            if (!OidIsValid(array_typelem))
+            Oid            anycompatible_elem_type;
+
+            have_generics_anycompatible = true;
+            have_anycompatible_array = true;
+
+            if (actual_type == UNKNOWNOID)
+                continue;
+
+            if (allow_poly && decl_type == actual_type)
+                continue;
+
+            actual_type = getBaseType(actual_type); /* flatten domains */
+            anycompatible_elem_type = get_element_type(actual_type);
+
+            if (!OidIsValid(anycompatible_elem_type))
                 ereport(ERROR,
                         (errcode(ERRCODE_DATATYPE_MISMATCH),
                          errmsg("argument declared %s is not an array but type %s",
-                                "anyarray", format_type_be(array_typeid))));
-        }
+                                "anyarray", format_type_be(actual_type))));

-        if (!OidIsValid(elem_typeid))
-        {
-            /*
-             * if we don't have an element type yet, use the one we just got
-             */
-            elem_typeid = array_typelem;
+            /* collect used type, reduce repeated values */
+            if (n_anycompatible_args == 0 ||
+                anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_elem_type)
+                anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type;
         }
-        else if (array_typelem != elem_typeid)
+        else if (decl_type == ANYCOMPATIBLERANGEOID)
         {
-            /* otherwise, they better match */
-            ereport(ERROR,
-                    (errcode(ERRCODE_DATATYPE_MISMATCH),
-                     errmsg("argument declared %s is not consistent with argument declared %s",
-                            "anyarray", "anyelement"),
-                     errdetail("%s versus %s",
-                               format_type_be(array_typeid),
-                               format_type_be(elem_typeid))));
+            Oid            anycompatible_range_typelem;
+
+            have_generics_anycompatible = true;
+            have_anycompatible_range = true;
+
+            if (actual_type == UNKNOWNOID)
+            {
+                have_unknowns = true;
+                continue;
+            }
+            if (allow_poly && decl_type == actual_type)
+                continue;        /* no new information here */
+            actual_type = getBaseType(actual_type); /* flatten domains */
+
+            if (OidIsValid(anycompatible_range_typeid) &&
+                actual_type != anycompatible_range_typeid)
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("arguments declared \"anycompatiblerange\" are not all alike"),
+                         errdetail("%s versus %s",
+                                   format_type_be(anycompatible_range_typeid),
+                                   format_type_be(actual_type))));
+
+            anycompatible_range_typeid = actual_type;
+            anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid);
+
+            if (!OidIsValid(anycompatible_range_typelem))
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("argument declared %s is not a range type but type %s",
+                                "anycompatiblerange",
+                                format_type_be(anycompatible_range_typeid))));
+
+            /* collect used type, reduce repeated values */
+            if (n_anycompatible_args == 0 ||
+                anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_range_typelem)
+                anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
         }
     }

-    /* Get the element type based on the range type, if we have one */
-    if (OidIsValid(range_typeid))
+    /*
+     * Fast Track: if none of the arguments are polymorphic, return the
+     * unmodified rettype.  We assume it can't be polymorphic either.
+     */
+    if (!have_generics_any && !have_generics_anycompatible)
+        return rettype;
+
+    if (have_generics_any)
     {
-        if (range_typeid == ANYRANGEOID && !have_anyelement)
+        /* Get the element type based on the array type, if we have one */
+        if (OidIsValid(array_typeid))
         {
-            /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
-            range_typelem = ANYELEMENTOID;
+            if (array_typeid == ANYARRAYOID && !have_anyelement)
+            {
+                /* Special case for ANYARRAY input: okay iff no ANYELEMENT */
+                array_typelem = ANYELEMENTOID;
+            }
+            else
+            {
+                array_typelem = get_element_type(array_typeid);
+                if (!OidIsValid(array_typelem))
+                    ereport(ERROR,
+                            (errcode(ERRCODE_DATATYPE_MISMATCH),
+                             errmsg("argument declared %s is not an array but type %s",
+                                    "anyarray", format_type_be(array_typeid))));
+            }
+
+            if (!OidIsValid(elem_typeid))
+            {
+                /*
+                 * if we don't have an element type yet, use the one we just
+                 * got
+                 */
+                elem_typeid = array_typelem;
+            }
+            else if (array_typelem != elem_typeid)
+            {
+                /* otherwise, they better match */
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("argument declared %s is not consistent with argument declared %s",
+                                "anyarray", "anyelement"),
+                         errdetail("%s versus %s",
+                                   format_type_be(array_typeid),
+                                   format_type_be(elem_typeid))));
+            }
         }
-        else
+
+        /* Get the element type based on the range type, if we have one */
+        if (OidIsValid(range_typeid))
         {
-            range_typelem = get_range_subtype(range_typeid);
-            if (!OidIsValid(range_typelem))
+            if (range_typeid == ANYRANGEOID && !have_anyelement)
+            {
+                /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+                range_typelem = ANYELEMENTOID;
+            }
+            else
+            {
+                range_typelem = get_range_subtype(range_typeid);
+                if (!OidIsValid(range_typelem))
+                    ereport(ERROR,
+                            (errcode(ERRCODE_DATATYPE_MISMATCH),
+                             errmsg("argument declared %s is not a range type but type %s",
+                                    "anyrange",
+                                    format_type_be(range_typeid))));
+            }
+
+            if (!OidIsValid(elem_typeid))
+            {
+                /*
+                 * if we don't have an element type yet, use the one we just
+                 * got
+                 */
+                elem_typeid = range_typelem;
+            }
+            else if (range_typelem != elem_typeid)
+            {
+                /* otherwise, they better match */
                 ereport(ERROR,
                         (errcode(ERRCODE_DATATYPE_MISMATCH),
-                         errmsg("argument declared %s is not a range type but type %s",
-                                "anyrange",
-                                format_type_be(range_typeid))));
+                         errmsg("argument declared %s is not consistent with argument declared %s",
+                                "anyrange", "anyelement"),
+                         errdetail("%s versus %s",
+                                   format_type_be(range_typeid),
+                                   format_type_be(elem_typeid))));
+            }
         }

         if (!OidIsValid(elem_typeid))
         {
+            if (allow_poly)
+            {
+                elem_typeid = ANYELEMENTOID;
+                array_typeid = ANYARRAYOID;
+                range_typeid = ANYRANGEOID;
+            }
+            else
+            {
+                /* Only way to get here is if all the generic args are UNKNOWN */
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("could not determine polymorphic type because input has type %s",
+                                "unknown")));
+            }
+        }
+
+        if (have_anynonarray && elem_typeid != ANYELEMENTOID)
+        {
             /*
-             * if we don't have an element type yet, use the one we just got
+             * require the element type to not be an array or domain over
+             * array
              */
-            elem_typeid = range_typelem;
+            if (type_is_array_domain(elem_typeid))
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("type matched to anynonarray is an array type: %s",
+                                format_type_be(elem_typeid))));
         }
-        else if (range_typelem != elem_typeid)
+
+        if (have_anyenum && elem_typeid != ANYELEMENTOID)
         {
-            /* otherwise, they better match */
-            ereport(ERROR,
-                    (errcode(ERRCODE_DATATYPE_MISMATCH),
-                     errmsg("argument declared %s is not consistent with argument declared %s",
-                            "anyrange", "anyelement"),
-                     errdetail("%s versus %s",
-                               format_type_be(range_typeid),
-                               format_type_be(elem_typeid))));
+            /* require the element type to be an enum */
+            if (!type_is_enum(elem_typeid))
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("type matched to anyenum is not an enum type: %s",
+                                format_type_be(elem_typeid))));
         }
     }

-    if (!OidIsValid(elem_typeid))
+    if (have_generics_anycompatible)
     {
-        if (allow_poly)
+        if (n_anycompatible_args > 0)
         {
-            elem_typeid = ANYELEMENTOID;
-            array_typeid = ANYARRAYOID;
-            range_typeid = ANYRANGEOID;
+            anycompatible_typeid = select_common_type_from_vector(n_anycompatible_args,
+                                                                  anycompatible_actual_types,
+                                                                  false);
+
+            if (have_anycompatible_array)
+            {
+                anycompatible_array_typeid = get_array_type(anycompatible_typeid);
+
+                if (!OidIsValid(anycompatible_array_typeid))
+                    ereport(ERROR,
+                            (errcode(ERRCODE_UNDEFINED_OBJECT),
+                             errmsg("could not find array type for data type %s",
+                                    format_type_be(anycompatible_typeid))));
+            }
+
+            /* anycompatible_range_typid should be defined already */
+            /* XXX this error message is not very on-point */
+            if (have_anycompatible_range &&
+                !OidIsValid(anycompatible_range_typeid))
+                ereport(ERROR,
+                        (errcode(ERRCODE_UNDEFINED_OBJECT),
+                         errmsg("could not find range type for data type %s",
+                                "anycompatiblerange")));
+
+            if (have_anycompatible_nonarray)
+            {
+                /*
+                 * require the element type to not be an array or domain over
+                 * array
+                 */
+                if (type_is_array_domain(anycompatible_typeid))
+                    ereport(ERROR,
+                            (errcode(ERRCODE_DATATYPE_MISMATCH),
+                             errmsg("type matched to anynonarray is an array type: %s",
+                                    format_type_be(anycompatible_typeid))));
+            }
         }
         else
         {
-            /* Only way to get here is if all the generic args are UNKNOWN */
-            ereport(ERROR,
-                    (errcode(ERRCODE_DATATYPE_MISMATCH),
-                     errmsg("could not determine polymorphic type because input has type %s",
-                            "unknown")));
+            if (allow_poly)
+            {
+                anycompatible_typeid = ANYCOMPATIBLEOID;
+                anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
+                anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
+            }
+            else
+            {
+                /* Only way to get here is if all the generic args are UNKNOWN */
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("could not determine polymorphic common type because input has type %s",
+                                "unknown")));
+            }
         }
-    }

-    if (have_anynonarray && elem_typeid != ANYELEMENTOID)
-    {
-        /* require the element type to not be an array or domain over array */
-        if (type_is_array_domain(elem_typeid))
-            ereport(ERROR,
-                    (errcode(ERRCODE_DATATYPE_MISMATCH),
-                     errmsg("type matched to anynonarray is an array type: %s",
-                            format_type_be(elem_typeid))));
-    }
+        /* replace polymorphic common types by selected common types */
+        for (j = 0; j < nargs; j++)
+        {
+            Oid            decl_type = declared_arg_types[j];

-    if (have_anyenum && elem_typeid != ANYELEMENTOID)
-    {
-        /* require the element type to be an enum */
-        if (!type_is_enum(elem_typeid))
-            ereport(ERROR,
-                    (errcode(ERRCODE_DATATYPE_MISMATCH),
-                     errmsg("type matched to anyenum is not an enum type: %s",
-                            format_type_be(elem_typeid))));
+            if (decl_type == ANYCOMPATIBLEOID ||
+                decl_type == ANYCOMPATIBLENONARRAYOID)    /* XXX seems wrong? */
+                declared_arg_types[j] = anycompatible_typeid;
+            else if (decl_type == ANYCOMPATIBLEARRAYOID)
+                declared_arg_types[j] = anycompatible_array_typeid;
+            else if (decl_type == ANYCOMPATIBLERANGEOID)
+                declared_arg_types[j] = anycompatible_range_typeid;
+        }
     }

     /*
@@ -1964,6 +2367,35 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
         rettype == ANYENUMOID)
         return elem_typeid;

+    if (rettype == ANYCOMPATIBLEOID)
+    {
+        if (!OidIsValid(anycompatible_typeid))
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_OBJECT),
+                     errmsg("could not find common type")));
+        return anycompatible_typeid;
+    }
+
+    if (rettype == ANYCOMPATIBLEARRAYOID)
+    {
+        if (!OidIsValid(anycompatible_array_typeid))
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_OBJECT),
+                     errmsg("could not find common array type")));
+        return anycompatible_array_typeid;
+    }
+
+    /* if we return ANYRANGE use the appropriate argument type */
+    if (rettype == ANYCOMPATIBLERANGEOID)
+    {
+        if (!OidIsValid(anycompatible_range_typeid))
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_OBJECT),
+                     errmsg("could not find range type for data type %s",
+                            "anycompatiblerange")));
+        return anycompatible_range_typeid;
+    }
+
     /* we don't return a generic type; send back the original return type */
     return rettype;
 }
@@ -2059,6 +2491,79 @@ resolve_generic_type(Oid declared_type,
             return context_actual_type;
         }
     }
+    else if (declared_type == ANYCOMPATIBLEARRAYOID)
+    {
+        if (context_declared_type == ANYCOMPATIBLEARRAYOID)
+        {
+            /*
+             * Use actual type, but it must be an array; or if it's a domain
+             * over array, use the base array type.
+             */
+            Oid            context_base_type = getBaseType(context_actual_type);
+            Oid            array_typelem = get_element_type(context_base_type);
+
+            if (!OidIsValid(array_typelem))
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("argument declared %s is not an array but type %s",
+                                "anycompatiblearray",
+                                format_type_be(context_base_type))));
+            return context_base_type;
+        }
+        else if (context_declared_type == ANYCOMPATIBLEOID ||
+                 context_declared_type == ANYCOMPATIBLENONARRAYOID ||
+                 context_declared_type == ANYCOMPATIBLERANGEOID)
+        {
+            /* Use the array type corresponding to actual type */
+            Oid            array_typeid = get_array_type(context_actual_type);
+
+            if (!OidIsValid(array_typeid))
+                ereport(ERROR,
+                        (errcode(ERRCODE_UNDEFINED_OBJECT),
+                         errmsg("could not find array type for data type %s",
+                                format_type_be(context_actual_type))));
+            return array_typeid;
+        }
+    }
+    else if (declared_type == ANYCOMPATIBLEOID ||
+             declared_type == ANYCOMPATIBLENONARRAYOID ||
+             declared_type == ANYCOMPATIBLERANGEOID)
+    {
+        if (context_declared_type == ANYCOMPATIBLEARRAYOID)
+        {
+            /* Use the element type corresponding to actual type */
+            Oid            context_base_type = getBaseType(context_actual_type);
+            Oid            array_typelem = get_element_type(context_base_type);
+
+            if (!OidIsValid(array_typelem))
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("argument declared %s is not an array but type %s",
+                                "anycompatiblearray",
+                                format_type_be(context_base_type))));
+            return array_typelem;
+        }
+        else if (context_declared_type == ANYCOMPATIBLERANGEOID)
+        {
+            /* Use the element type corresponding to actual type */
+            Oid            context_base_type = getBaseType(context_actual_type);
+            Oid            range_typelem = get_range_subtype(context_base_type);
+
+            if (!OidIsValid(range_typelem))
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("argument declared %s is not a range type but type %s",
+                                "anycompatiblerange",
+                                format_type_be(context_base_type))));
+            return range_typelem;
+        }
+        else if (context_declared_type == ANYCOMPATIBLEOID ||
+                 context_declared_type == ANYCOMPATIBLENONARRAYOID)
+        {
+            /* Use the actual type; it doesn't matter if array or not */
+            return context_actual_type;
+        }
+    }
     else
     {
         /* declared_type isn't polymorphic, so return it as-is */
@@ -2082,11 +2587,12 @@ is_valid_polymorphic_signature(Oid ret_type,
                                const Oid *declared_arg_types,
                                int nargs)
 {
-    if (ret_type == ANYRANGEOID)
+    if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
     {
         /*
          * ANYRANGE requires an ANYRANGE input, else we can't tell which of
-         * several range types with the same element type to use.
+         * several range types with the same element type to use.  Likewise
+         * for ANYCOMPATIBLERANGE.
          */
         for (int i = 0; i < nargs; i++)
         {
@@ -2095,12 +2601,22 @@ is_valid_polymorphic_signature(Oid ret_type,
         }
         return false;
     }
-    else if (IsPolymorphicType(ret_type))
+    else if (IsPolymorphicTypeFamily1(ret_type))
+    {
+        /* Otherwise, any family-1 type can be deduced from any other */
+        for (int i = 0; i < nargs; i++)
+        {
+            if (IsPolymorphicTypeFamily1(declared_arg_types[i]))
+                return true;    /* OK */
+        }
+        return false;
+    }
+    else if (IsPolymorphicTypeFamily2(ret_type))
     {
-        /* Otherwise, any polymorphic type can be deduced from any other */
+        /* Otherwise, any family-2 type can be deduced from any other */
         for (int i = 0; i < nargs; i++)
         {
-            if (IsPolymorphicType(declared_arg_types[i]))
+            if (IsPolymorphicTypeFamily2(declared_arg_types[i]))
                 return true;    /* OK */
         }
         return false;
@@ -2206,8 +2722,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 ANYCOMPATIBLE */
+    if (targettype == ANYOID || targettype == ANYELEMENTOID ||
+        targettype == ANYCOMPATIBLEOID)
         return true;

     /* If srctype is a domain, reduce to its base type */
@@ -2218,13 +2735,13 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
     if (srctype == targettype)
         return true;

-    /* Also accept any array type as coercible to ANYARRAY */
-    if (targettype == ANYARRAYOID)
+    /* Also accept any array type as coercible to ANY[COMPATIBLE]ARRAY */
+    if (targettype == ANYARRAYOID || targettype == ANYCOMPATIBLEARRAYOID)
         if (type_is_array(srctype))
             return true;

-    /* Also accept any non-array type as coercible to ANYNONARRAY */
-    if (targettype == ANYNONARRAYOID)
+    /* Also accept any non-array type as coercible to ANY[COMPATIBLE]NONARRAY */
+    if (targettype == ANYNONARRAYOID || targettype == ANYCOMPATIBLENONARRAYOID)
         if (!type_is_array(srctype))
             return true;

@@ -2233,8 +2750,8 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
         if (type_is_enum(srctype))
             return true;

-    /* Also accept any range type as coercible to ANYRANGE */
-    if (targettype == ANYRANGEOID)
+    /* Also accept any range type as coercible to ANY[COMPATIBLE]RANGE */
+    if (targettype == ANYRANGEOID || targettype == ANYCOMPATIBLERANGEOID)
         if (type_is_range(srctype))
             return true;

diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index f78420e..8bb00ab 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -195,7 +195,7 @@ json_categorize_type(Oid typoid,
         default:
             /* Check for arrays and composites */
             if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
-                || typoid == RECORDARRAYOID)
+                || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
                 *tcategory = JSONTYPE_ARRAY;
             else if (type_is_rowtype(typoid))    /* includes RECORDOID */
                 *tcategory = JSONTYPE_COMPOSITE;
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index b961d29..1e9ca04 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -677,7 +677,7 @@ jsonb_categorize_type(Oid typoid,
         default:
             /* Check for arrays and composites */
             if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
-                || typoid == RECORDARRAYOID)
+                || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
                 *tcategory = JSONBTYPE_ARRAY;
             else if (type_is_rowtype(typoid))    /* includes RECORDOID */
                 *tcategory = JSONBTYPE_COMPOSITE;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 6ab95dc..1f63379 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -178,6 +178,32 @@ anyarray_send(PG_FUNCTION_ARGS)
 }

 /*
+ * anycompatiblearray
+ *
+ * XXX anycompatiblearray_recv 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.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblearray);
+PSEUDOTYPE_DUMMY_RECEIVE_FUNC(anycompatiblearray);
+
+/*
+ * We may as well allow output, since we do for anyarray.
+ * This code is probably unreachable in practice though.
+ */
+Datum
+anycompatiblearray_out(PG_FUNCTION_ARGS)
+{
+    return array_out(fcinfo);
+}
+
+Datum
+anycompatiblearray_send(PG_FUNCTION_ARGS)
+{
+    return array_send(fcinfo);
+}
+
+/*
  * anyenum
  *
  * We may as well allow output, since enum_out will in fact work.
@@ -204,6 +230,19 @@ anyrange_out(PG_FUNCTION_ARGS)
 }

 /*
+ * anycompatiblerange
+ *
+ * We may as well allow output, since range_out will in fact work.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblerange);
+
+Datum
+anycompatiblerange_out(PG_FUNCTION_ARGS)
+{
+    return range_out(fcinfo);
+}
+
+/*
  * void_in        - input routine for pseudo-type VOID.
  *
  * We allow this so that PL functions can return VOID without any special
@@ -339,3 +378,5 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatible);
+PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblenonarray);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 0201e4f..5e10449 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -472,10 +472,18 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
     bool        have_anyrange_result = false;
     bool        have_anynonarray = false;
     bool        have_anyenum = false;
+    bool        have_anycompatible_result = false;
+    bool        have_anycompatible_array_result = false;
+    bool        have_anycompatible_range_result = false;
+    bool        have_anycompatible_nonarray = false;
     Oid            anyelement_type = InvalidOid;
     Oid            anyarray_type = InvalidOid;
     Oid            anyrange_type = InvalidOid;
+    Oid            anycompatible_type = InvalidOid;
+    Oid            anycompatible_array_type = InvalidOid;
+    Oid            anycompatible_range_type = InvalidOid;
     Oid            anycollation = InvalidOid;
+    Oid            anycompatcollation = InvalidOid;
     int            i;

     /* See if there are any polymorphic outputs; quick out if not */
@@ -500,17 +508,37 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
             case ANYRANGEOID:
                 have_anyrange_result = true;
                 break;
+            case ANYCOMPATIBLEOID:
+                have_anycompatible_result = true;
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                have_anycompatible_array_result = true;
+                break;
+            case ANYCOMPATIBLENONARRAYOID:
+                have_anycompatible_result = true;
+                have_anycompatible_nonarray = true;
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                have_anycompatible_range_result = true;
+                break;
             default:
                 break;
         }
     }
-    if (!have_anyelement_result && !have_anyarray_result &&
-        !have_anyrange_result)
+    if (!have_anyelement_result &&
+        !have_anyarray_result &&
+        !have_anyrange_result &&
+        !have_anycompatible_result &&
+        !have_anycompatible_array_result &&
+        !have_anycompatible_range_result)
         return true;

     /*
      * Otherwise, extract actual datatype(s) from input arguments.  (We assume
-     * the parser already validated consistency of the arguments.)
+     * the parser already validated consistency of the arguments.  Also, for
+     * the ANYCOMPATIBLE pseudotype family, we expect that all matching
+     * arguments were coerced to the selected common supertype, so that it
+     * doesn't matter which one's exposed type we look at.)
      */
     if (!call_expr)
         return false;            /* no hope */
@@ -533,14 +561,37 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                 if (!OidIsValid(anyrange_type))
                     anyrange_type = get_call_expr_argtype(call_expr, i);
                 break;
+            case ANYCOMPATIBLEOID:
+            case ANYCOMPATIBLENONARRAYOID:
+                if (!OidIsValid(anycompatible_type))
+                    anycompatible_type = get_call_expr_argtype(call_expr, i);
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                if (!OidIsValid(anycompatible_array_type))
+                    anycompatible_array_type = get_call_expr_argtype(call_expr, i);
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                if (!OidIsValid(anycompatible_range_type))
+                    anycompatible_range_type = get_call_expr_argtype(call_expr, i);
+                break;
             default:
                 break;
         }
     }

     /* If nothing found, parser messed up */
-    if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
-        !OidIsValid(anyrange_type))
+    if ((have_anyelement_result || have_anyarray_result ||
+         have_anyrange_result) &&
+        (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
+         !OidIsValid(anyrange_type)))
+        return false;
+
+    if ((have_anycompatible_result ||
+         have_anycompatible_array_result ||
+         have_anycompatible_range_result) &&
+        (!OidIsValid(anycompatible_type) &&
+         !OidIsValid(anycompatible_array_type) &&
+         !OidIsValid(anycompatible_range_type)))
         return false;

     /* If needed, deduce one polymorphic type from others */
@@ -563,11 +614,36 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
         }
     }

+    if (have_anycompatible_result && !OidIsValid(anycompatible_type))
+    {
+        if (OidIsValid(anycompatible_array_type))
+            anycompatible_type = resolve_generic_type(ANYCOMPATIBLEOID,
+                                                      anycompatible_array_type,
+                                                      ANYCOMPATIBLEARRAYOID);
+
+        if (OidIsValid(anycompatible_range_type))
+        {
+            Oid            subtype = resolve_generic_type(ANYCOMPATIBLEOID,
+                                                       anycompatible_range_type,
+                                                       ANYCOMPATIBLERANGEOID);
+
+            /* check for inconsistent array and range results */
+            if (OidIsValid(anycompatible_type) && anycompatible_type != subtype)
+                return false;
+            anycompatible_type = subtype;
+        }
+    }
+
     if (have_anyarray_result && !OidIsValid(anyarray_type))
         anyarray_type = resolve_generic_type(ANYARRAYOID,
                                              anyelement_type,
                                              ANYELEMENTOID);

+    if (have_anycompatible_array_result && !OidIsValid(anycompatible_array_type))
+        anycompatible_array_type = resolve_generic_type(ANYCOMPATIBLEARRAYOID,
+                                                        anycompatible_type,
+                                                        ANYCOMPATIBLEOID);
+
     /*
      * We can't deduce a range type from other polymorphic inputs, because
      * there may be multiple range types for the same subtype.
@@ -575,10 +651,17 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
     if (have_anyrange_result && !OidIsValid(anyrange_type))
         return false;

+    if (have_anycompatible_range_result && !OidIsValid(anycompatible_range_type))
+        return false;
+
     /* Enforce ANYNONARRAY if needed */
     if (have_anynonarray && type_is_array(anyelement_type))
         return false;

+    /* Enforce ANYCOMPATIBLENONARRAY if needed */
+    if (have_anycompatible_nonarray && type_is_array(anycompatible_type))
+        return false;
+
     /* Enforce ANYENUM if needed */
     if (have_anyenum && !type_is_enum(anyelement_type))
         return false;
@@ -594,7 +677,12 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
     else if (OidIsValid(anyarray_type))
         anycollation = get_typcollation(anyarray_type);

-    if (OidIsValid(anycollation))
+    if (OidIsValid(anycompatible_type))
+        anycompatcollation = get_typcollation(anycompatible_type);
+    else if (OidIsValid(anycompatible_array_type))
+        anycompatcollation = get_typcollation(anycompatible_array_type);
+
+    if (OidIsValid(anycollation) || OidIsValid(anycompatcollation))
     {
         /*
          * The types are collatable, so consider whether to use a nondefault
@@ -605,6 +693,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,

         if (OidIsValid(inputcollation))
             anycollation = inputcollation;
+
+        if (OidIsValid(inputcollation))
+            anycompatcollation = inputcollation;
     }

     /* And finally replace the tuple column types as needed */
@@ -640,6 +731,31 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                    0);
                 /* no collation should be attached to a range type */
                 break;
+            case ANYCOMPATIBLEOID:
+            case ANYCOMPATIBLENONARRAYOID:
+                TupleDescInitEntry(tupdesc, i + 1,
+                                   NameStr(att->attname),
+                                   anycompatible_type,
+                                   -1,
+                                   0);
+                TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                TupleDescInitEntry(tupdesc, i + 1,
+                                   NameStr(att->attname),
+                                   anycompatible_array_type,
+                                   -1,
+                                   0);
+                TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                TupleDescInitEntry(tupdesc, i + 1,
+                                   NameStr(att->attname),
+                                   anycompatible_range_type,
+                                   -1,
+                                   0);
+                /* no collation should be attached to a range type */
+                break;
             default:
                 break;
         }
@@ -664,9 +780,15 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
     bool        have_anyelement_result = false;
     bool        have_anyarray_result = false;
     bool        have_anyrange_result = false;
+    bool        have_anycompatible_result = false;
+    bool        have_anycompatible_array_result = false;
+    bool        have_anycompatible_range_result = false;
     Oid            anyelement_type = InvalidOid;
     Oid            anyarray_type = InvalidOid;
     Oid            anyrange_type = InvalidOid;
+    Oid            anycompatible_type = InvalidOid;
+    Oid            anycompatible_array_type = InvalidOid;
+    Oid            anycompatible_range_type = InvalidOid;
     int            inargno;
     int            i;

@@ -725,6 +847,52 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
                     argtypes[i] = anyrange_type;
                 }
                 break;
+            case ANYCOMPATIBLEOID:
+            case ANYCOMPATIBLENONARRAYOID:
+                if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+                    have_anycompatible_result = true;
+                else
+                {
+                    if (!OidIsValid(anycompatible_type))
+                    {
+                        anycompatible_type = get_call_expr_argtype(call_expr,
+                                                                   inargno);
+                        if (!OidIsValid(anycompatible_type))
+                            return false;
+                    }
+                    argtypes[i] = anycompatible_type;
+                }
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+                    have_anycompatible_array_result = true;
+                else
+                {
+                    if (!OidIsValid(anycompatible_array_type))
+                    {
+                        anycompatible_array_type = get_call_expr_argtype(call_expr,
+                                                                         inargno);
+                        if (!OidIsValid(anycompatible_array_type))
+                            return false;
+                    }
+                    argtypes[i] = anycompatible_array_type;
+                }
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+                    have_anycompatible_range_result = true;
+                else
+                {
+                    if (!OidIsValid(anycompatible_range_type))
+                    {
+                        anycompatible_range_type = get_call_expr_argtype(call_expr,
+                                                                         inargno);
+                        if (!OidIsValid(anycompatible_range_type))
+                            return false;
+                    }
+                    argtypes[i] = anycompatible_range_type;
+                }
+                break;
             default:
                 break;
         }
@@ -733,48 +901,99 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
     }

     /* Done? */
-    if (!have_anyelement_result && !have_anyarray_result &&
-        !have_anyrange_result)
+    if (!have_anyelement_result &&
+        !have_anyarray_result &&
+        !have_anyrange_result &&
+        !have_anycompatible_result &&
+        !have_anycompatible_array_result &&
+        !have_anycompatible_range_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_anycompatible_result || have_anycompatible_array_result ||
+        have_anycompatible_range_result)
     {
-        if (OidIsValid(anyarray_type))
-            anyelement_type = resolve_generic_type(ANYELEMENTOID,
-                                                   anyarray_type,
-                                                   ANYARRAYOID);
-        if (OidIsValid(anyrange_type))
+        /* If no input polymorphics, parser messed up */
+        if (!OidIsValid(anycompatible_type) &&
+            !OidIsValid(anycompatible_array_type) &&
+            !OidIsValid(anycompatible_range_type))
+            return false;
+
+        if (have_anycompatible_result && !OidIsValid(anycompatible_type))
         {
-            Oid            subtype = resolve_generic_type(ANYELEMENTOID,
-                                                       anyrange_type,
-                                                       ANYRANGEOID);
+            if (OidIsValid(anycompatible_array_type))
+                anycompatible_type = resolve_generic_type(ANYCOMPATIBLEOID,
+                                                          anycompatible_array_type,
+                                                          ANYCOMPATIBLEARRAYOID);

-            /* check for inconsistent array and range results */
-            if (OidIsValid(anyelement_type) && anyelement_type != subtype)
-                return false;
-            anyelement_type = subtype;
+            if (OidIsValid(anycompatible_range_type))
+            {
+                Oid            subtype = resolve_generic_type(ANYCOMPATIBLEOID,
+                                                           anyrange_type,
+                                                           ANYCOMPATIBLERANGEOID);
+
+                /* check for inconsistent array and range results */
+                if (OidIsValid(anycompatible_type) && anycompatible_type != subtype)
+                    return false;
+                anycompatible_type = subtype;
+            }
         }
-    }

-    if (have_anyarray_result && !OidIsValid(anyarray_type))
-        anyarray_type = resolve_generic_type(ANYARRAYOID,
-                                             anyelement_type,
-                                             ANYELEMENTOID);
+        if (have_anycompatible_array_result || !OidIsValid(anycompatible_array_type))
+            anycompatible_array_type = resolve_generic_type(ANYCOMPATIBLEARRAYOID,
+                                                            anycompatible_type,
+                                                            ANYCOMPATIBLEOID);

-    /*
-     * We can't deduce a range type from other polymorphic inputs, because
-     * there may be multiple range types for the same subtype.
-     */
-    if (have_anyrange_result && !OidIsValid(anyrange_type))
-        return false;
+        /*
+         * We can't deduce a range type from other polymorphic inputs, because
+         * there may be multiple range types for the same subtype.
+         */
+        if (have_anycompatible_range_result && !OidIsValid(anycompatible_range_type))
+            return false;

-    /* XXX do we need to enforce ANYNONARRAY or ANYENUM here?  I think not */
+        /* XXX do we need to enforce ANYCOMPATIBLENONARRAY here?  I think not */
+    }

     /* And finally replace the output column types as needed */
     for (i = 0; i < numargs; i++)
@@ -792,6 +1011,16 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
             case ANYRANGEOID:
                 argtypes[i] = anyrange_type;
                 break;
+            case ANYCOMPATIBLEOID:
+            case ANYCOMPATIBLENONARRAYOID:
+                argtypes[i] = anycompatible_type;
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                argtypes[i] = anycompatible_array_type;
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                argtypes[i] = anycompatible_range_type;
+                break;
             default:
                 break;
         }
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7fb574f..8387238 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7106,6 +7106,42 @@
 { oid => '268', descr => 'I/O',
   proname => 'table_am_handler_out', prorettype => 'cstring',
   proargtypes => 'table_am_handler', prosrc => 'table_am_handler_out' },
+{ oid => '9559', descr => 'I/O',
+  proname => 'anycompatible_in', prorettype => 'anycompatible',
+  proargtypes => 'cstring', prosrc => 'anycompatible_in' },
+{ oid => '9560', descr => 'I/O',
+  proname => 'anycompatible_out', prorettype => 'cstring',
+  proargtypes => 'anycompatible', prosrc => 'anycompatible_out' },
+{ oid => '9561', descr => 'I/O',
+  proname => 'anycompatiblearray_in', prorettype => 'anycompatiblearray',
+  proargtypes => 'cstring', prosrc => 'anycompatiblearray_in' },
+{ oid => '9562', descr => 'I/O',
+  proname => 'anycompatiblearray_out', provolatile => 's',
+  prorettype => 'cstring', proargtypes => 'anycompatiblearray',
+  prosrc => 'anycompatiblearray_out' },
+{ oid => '9563', descr => 'I/O',
+  proname => 'anycompatiblearray_recv', provolatile => 's',
+  prorettype => 'anycompatiblearray', proargtypes => 'internal',
+  prosrc => 'anycompatiblearray_recv' },
+{ oid => '9564', descr => 'I/O',
+  proname => 'anycompatiblearray_send', provolatile => 's',
+  prorettype => 'bytea', proargtypes => 'anycompatiblearray',
+  prosrc => 'anycompatiblearray_send' },
+{ oid => '9565', descr => 'I/O',
+  proname => 'anycompatiblenonarray_in', prorettype => 'anycompatiblenonarray',
+  proargtypes => 'cstring', prosrc => 'anycompatiblenonarray_in' },
+{ oid => '9566', descr => 'I/O',
+  proname => 'anycompatiblenonarray_out', prorettype => 'cstring',
+  proargtypes => 'anycompatiblenonarray',
+  prosrc => 'anycompatiblenonarray_out' },
+{ oid => '9567', descr => 'I/O',
+  proname => 'anycompatiblerange_in', provolatile => 's',
+  prorettype => 'anycompatiblerange', proargtypes => 'cstring oid int4',
+  prosrc => 'anycompatiblerange_in' },
+{ oid => '9568', descr => 'I/O',
+  proname => 'anycompatiblerange_out', provolatile => 's',
+  prorettype => 'cstring', proargtypes => 'anycompatiblerange',
+  prosrc => 'anycompatiblerange_out' },

 # tablesample method handlers
 { oid => '3313', descr => 'BERNOULLI tablesample method handler',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index b00597d..20d5167 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -590,5 +590,30 @@
   typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
   typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '9550',
+  descr => 'pseudo-type representing a polymorphic common type',
+  typname => 'anycompatible', typlen => '4', typbyval => 't', typtype => 'p',
+  typcategory => 'P', typinput => 'anycompatible_in',
+  typoutput => 'anycompatible_out', typreceive => '-', typsend => '-',
+  typalign => 'i' },
+{ oid => '9551',
+  descr => 'pseudo-type representing an array of polymorphic common type elements',
+  typname => 'anycompatiblearray', typlen => '-1', typbyval => 'f',
+  typtype => 'p', typcategory => 'P', typinput => 'anycompatiblearray_in',
+  typoutput => 'anycompatiblearray_out',
+  typreceive => 'anycompatiblearray_recv', typsend => 'anycompatiblearray_send',
+  typalign => 'd', typstorage => 'x' },
+{ oid => '9552',
+  descr => 'pseudo-type representing a polymorphic common type that is not an array',
+  typname => 'anycompatiblenonarray', typlen => '4', typbyval => 't',
+  typtype => 'p', typcategory => 'P', typinput => 'anycompatiblenonarray_in',
+  typoutput => 'anycompatiblenonarray_out', typreceive => '-', typsend => '-',
+  typalign => 'i' },
+{ oid => '9553',
+  descr => 'pseudo-type representing a polymorphic common type that is a range',
+  typname => 'anycompatiblerange', typlen => '-1', typbyval => 'f',
+  typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
+  typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
+  typalign => 'd', typstorage => 'x' },

 ]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 9789094..7b37562 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -295,12 +295,23 @@ typedef FormData_pg_type *Form_pg_type;

 /* Is a type OID a polymorphic pseudotype?    (Beware of multiple evaluation) */
 #define IsPolymorphicType(typid)  \
+    (IsPolymorphicTypeFamily1(typid) || \
+     IsPolymorphicTypeFamily2(typid))
+
+/* Code not part of polymorphic type resolution should not use these macros: */
+#define IsPolymorphicTypeFamily1(typid)  \
     ((typid) == ANYELEMENTOID || \
      (typid) == ANYARRAYOID || \
      (typid) == ANYNONARRAYOID || \
      (typid) == ANYENUMOID || \
      (typid) == ANYRANGEOID)

+#define IsPolymorphicTypeFamily2(typid)  \
+    ((typid) == ANYCOMPATIBLEOID || \
+     (typid) == ANYCOMPATIBLEARRAYOID || \
+     (typid) == ANYCOMPATIBLENONARRAYOID || \
+     (typid) == ANYCOMPATIBLERANGEOID)
+
 #endif                            /* EXPOSE_TO_CLIENT_CODE */


diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index c8e43e6..828ff5a 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -507,11 +507,13 @@ do_compile(FunctionCallInfo fcinfo,
             {
                 if (forValidator)
                 {
-                    if (rettypeid == ANYARRAYOID)
+                    if (rettypeid == ANYARRAYOID ||
+                        rettypeid == ANYCOMPATIBLEARRAYOID)
                         rettypeid = INT4ARRAYOID;
-                    else if (rettypeid == ANYRANGEOID)
+                    else if (rettypeid == ANYRANGEOID ||
+                             rettypeid == ANYCOMPATIBLERANGEOID)
                         rettypeid = INT4RANGEOID;
-                    else        /* ANYELEMENT or ANYNONARRAY */
+                    else        /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
                         rettypeid = INT4OID;
                     /* XXX what could we use for ANYENUM? */
                 }
@@ -2493,12 +2495,16 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
                 case ANYELEMENTOID:
                 case ANYNONARRAYOID:
                 case ANYENUMOID:    /* XXX dubious */
+                case ANYCOMPATIBLEOID:
+                case ANYCOMPATIBLENONARRAYOID:
                     argtypes[i] = INT4OID;
                     break;
                 case ANYARRAYOID:
+                case ANYCOMPATIBLEARRAYOID:
                     argtypes[i] = INT4ARRAYOID;
                     break;
                 case ANYRANGEOID:
+                case ANYCOMPATIBLERANGEOID:
                     argtypes[i] = INT4RANGEOID;
                     break;
                 default:
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index d436d29..838352b 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -1547,3 +1547,177 @@ View definition:

 drop view dfview;
 drop function dfunc(anyelement, anyelement, bool);
+create or replace function cttestfunc01(anycompatible, anycompatible)
+returns anycompatible as $$
+begin
+  if $1 > $2 then
+    return $1;
+  else
+    return $2;
+  end if;
+end;
+$$ language plpgsql;
+create or replace function cttestfunc02(anycompatible, anycompatible)
+returns anycompatiblearray as $$
+begin
+  return ARRAY[$1, $2];
+end;
+$$ language plpgsql;
+create or replace function cttestfunc03(anycompatiblearray)
+returns anycompatible as $$
+begin
+  return $1[1];
+end;
+$$ language plpgsql;
+create or replace function cttestfunc04(variadic anycompatiblearray)
+returns anycompatible as $$
+begin
+  return (select min(v) from unnest($1) g(v));
+end;
+$$ language plpgsql;
+create or replace function cttestfunc05(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+begin
+  return $1;
+end;
+$$ language plpgsql;
+create or replace function cttestfunc06(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+begin
+  return ARRAY[$1, $2];
+end;
+$$ language plpgsql;
+create or replace function cttestfunc07(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+  select $1
+$$ language sql;
+create or replace function cttestfunc08(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+select array[$1, $2]
+$$ language sql;
+select cttestfunc01(10, 20);
+ cttestfunc01
+--------------
+           20
+(1 row)
+
+select cttestfunc01(10.1, 20.1);
+ cttestfunc01
+--------------
+         20.1
+(1 row)
+
+select cttestfunc01(10, 20.1);
+ cttestfunc01
+--------------
+         20.1
+(1 row)
+
+select cttestfunc02(10, 20);
+ cttestfunc02
+--------------
+ {10,20}
+(1 row)
+
+select cttestfunc02(10.1, 20.1);
+ cttestfunc02
+--------------
+ {10.1,20.1}
+(1 row)
+
+select cttestfunc02(10, 20.1);
+ cttestfunc02
+--------------
+ {10,20.1}
+(1 row)
+
+select cttestfunc03(ARRAY[10, 20]);
+ cttestfunc03
+--------------
+           10
+(1 row)
+
+select cttestfunc03(ARRAY[10.1, 20.1]);
+ cttestfunc03
+--------------
+         10.1
+(1 row)
+
+select cttestfunc03(ARRAY[10, 20.1]);
+ cttestfunc03
+--------------
+           10
+(1 row)
+
+select cttestfunc04(10, 20);
+ cttestfunc04
+--------------
+           10
+(1 row)
+
+select cttestfunc04(10.1, 20.1);
+ cttestfunc04
+--------------
+         10.1
+(1 row)
+
+select cttestfunc04(10, 20.1);
+ cttestfunc04
+--------------
+           10
+(1 row)
+
+select cttestfunc05(10, 20);
+ cttestfunc05
+--------------
+ {10,20}
+(1 row)
+
+select cttestfunc05(10.1, 20.1);
+ cttestfunc05
+--------------
+ {10.1,20.1}
+(1 row)
+
+select cttestfunc05(10, 20.1);
+ cttestfunc05
+--------------
+ {10,20.1}
+(1 row)
+
+select cttestfunc06(1,1.1);
+ cttestfunc06
+--------------
+ {1,1.1}
+(1 row)
+
+select cttestfunc07(10, 20);
+ cttestfunc07
+--------------
+ {10,20}
+(1 row)
+
+select cttestfunc07(10.1, 20.1);
+ cttestfunc07
+--------------
+ {10.1,20.1}
+(1 row)
+
+select cttestfunc07(10, 20.1);
+ cttestfunc07
+--------------
+ {10,20.1}
+(1 row)
+
+select cttestfunc08(1,1.1);
+ cttestfunc08
+--------------
+ {1,1.1}
+(1 row)
+
+-- should to fail
+select cttestfunc06(array[10], array[2]);
+ERROR:  function cttestfunc06(integer[], integer[]) does not exist
+LINE 1: select cttestfunc06(array[10], array[2]);
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index 0360667..566b25b 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -814,3 +814,91 @@ select * from dfview;

 drop view dfview;
 drop function dfunc(anyelement, anyelement, bool);
+
+create or replace function cttestfunc01(anycompatible, anycompatible)
+returns anycompatible as $$
+begin
+  if $1 > $2 then
+    return $1;
+  else
+    return $2;
+  end if;
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc02(anycompatible, anycompatible)
+returns anycompatiblearray as $$
+begin
+  return ARRAY[$1, $2];
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc03(anycompatiblearray)
+returns anycompatible as $$
+begin
+  return $1[1];
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc04(variadic anycompatiblearray)
+returns anycompatible as $$
+begin
+  return (select min(v) from unnest($1) g(v));
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc05(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+begin
+  return $1;
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc06(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+begin
+  return ARRAY[$1, $2];
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc07(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+  select $1
+$$ language sql;
+
+create or replace function cttestfunc08(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+select array[$1, $2]
+$$ language sql;
+
+
+select cttestfunc01(10, 20);
+select cttestfunc01(10.1, 20.1);
+select cttestfunc01(10, 20.1);
+
+select cttestfunc02(10, 20);
+select cttestfunc02(10.1, 20.1);
+select cttestfunc02(10, 20.1);
+
+select cttestfunc03(ARRAY[10, 20]);
+select cttestfunc03(ARRAY[10.1, 20.1]);
+select cttestfunc03(ARRAY[10, 20.1]);
+
+select cttestfunc04(10, 20);
+select cttestfunc04(10.1, 20.1);
+select cttestfunc04(10, 20.1);
+
+select cttestfunc05(10, 20);
+select cttestfunc05(10.1, 20.1);
+select cttestfunc05(10, 20.1);
+
+select cttestfunc06(1,1.1);
+
+select cttestfunc07(10, 20);
+select cttestfunc07(10.1, 20.1);
+select cttestfunc07(10, 20.1);
+
+select cttestfunc08(1,1.1);
+
+-- should to fail
+select cttestfunc06(array[10], array[2]);

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Laurenz Albe
Дата:
Сообщение: Re: Berserk Autovacuum (let's save next Mandrill)
Следующее
От: Justin Pryzby
Дата:
Сообщение: Re: Berserk Autovacuum (let's save next Mandrill)