Re: proposal: new polymorphic types - commontype and commontypearray

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: proposal: new polymorphic types - commontype and commontypearray
Дата
Msg-id 4509.1584495935@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: proposal: new polymorphic types - commontype and commontypearray  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: proposal: new polymorphic types - commontype and commontypearray  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
I wrote:
> The cfbot will be unhappy at this point, but I need to rebase the
> main patch again ...

And rebased.  Still not quite happy about some of the details in
enforce_generic_type_consistency, and I've not looked at the test
cases or documentation at all.  But this should make the cfbot
happy.

            regards, tom lane

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 97706da..931c23c 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
@@ -270,6 +270,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 0cac936..6cdda35 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -262,6 +262,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 c3fb51d..0a6c051 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -167,15 +167,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
@@ -185,7 +187,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.
@@ -193,10 +197,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
@@ -1387,6 +1391,98 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
 }

 /*
+ * select_common_type_from_vector()
+ *        Determine the common supertype of a vector of type OIDs.
+ *
+ * This is the same logic as select_common_type(), but working from
+ * an array of type OIDs not a list of expressions.  As in that function,
+ * earlier entries in the array have some preference over later ones.
+ * On failure, return InvalidOid if noerror is true, else throw an error.
+ */
+static Oid
+select_common_type_from_vector(int nargs, const Oid *typeids, bool noerror)
+{
+    Oid            ptype;
+    TYPCATEGORY pcategory;
+    bool        pispreferred;
+    int            i = 1;
+
+    Assert(nargs > 0);
+    ptype = typeids[0];
+
+    /* If all input types are valid and exactly the same, pick that type. */
+    if (ptype != UNKNOWNOID)
+    {
+        for (; i < nargs; i++)
+        {
+            if (typeids[i] != ptype)
+                break;
+        }
+        if (i == nargs)
+            return ptype;
+    }
+
+    /*
+     * Nope, so set up for the full algorithm.  Note that at this point, we
+     * can skip array entries before "i"; they are all equal to ptype.
+     */
+    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)
+            {
+                /*
+                 * both types in different categories? then not much hope...
+                 */
+                if (noerror)
+                    return InvalidOid;
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("argument 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;
+            }
+        }
+    }
+
+    /* Like select_common_type(), choose TEXT if all inputs were UNKNOWN */
+    if (ptype == UNKNOWNOID)
+        ptype = TEXTOID;
+
+    return ptype;
+}
+
+/*
  * coerce_to_common_type()
  *        Coerce an expression to the given type.
  *
@@ -1442,14 +1538,28 @@ 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.)
+ * 7) All arguments declared ANYCOMPATIBLE must be implicitly castable
+ *      to a common supertype (chosen as per select_common_type's rules).
+ *      ANYCOMPATIBLENONARRAY works like ANYCOMPATIBLE but also requires the
+ *      common supertype to not be an array.  If there are ANYCOMPATIBLEARRAY
+ *      or ANYCOMPATIBLERANGE arguments, their element types are included in
+ *      the choice of common supertype.
+ * 8) The resolved type of ANYCOMPATIBLEARRAY arguments will be the array
+ *      type over the common supertype (which might not be the same array type
+ *      as any of the original arrays).
+ * 9) All ANYCOMPATIBLERANGE arguments must be the exact same range type
+ *      (after domain flattening), since we have no preference rule that would
+ *      let us choose one over another.  Furthermore, that range's subtype
+ *      must exactly match the common supertype chosen by rule 7.
  *
  * 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
  * argument is a domain over int4[] while another one is just int4[].)    Also
- * notice that such a domain does *not* match ANYNONARRAY.
+ * notice that such a domain does *not* match ANYNONARRAY.  The same goes
+ * for ANYCOMPATIBLEARRAY and ANYCOMPATIBLENONARRAY.
  *
- * Similarly, domains over ranges match ANYRANGE, and are immediately
- * flattened to their base type.
+ * Similarly, domains over ranges match ANYRANGE or ANYCOMPATIBLERANGE,
+ * and are immediately flattened to their base type.
  *
  * Note that domains aren't currently considered to match ANYENUM,
  * even if their base type would match.
@@ -1467,13 +1577,19 @@ check_generic_type_consistency(const Oid *actual_arg_types,
     Oid            elem_typeid = InvalidOid;
     Oid            array_typeid = InvalidOid;
     Oid            range_typeid = InvalidOid;
+    Oid            anycompatible_range_typeid = InvalidOid;
+    Oid            anycompatible_range_typelem = InvalidOid;
     bool        have_anynonarray = false;
     bool        have_anyenum = false;
+    bool        have_anycompatible_nonarray = false;
+    int            n_anycompatible_args = 0;
+    Oid            anycompatible_actual_types[FUNC_MAX_ARGS];

     /*
      * Loop through the arguments to see if we have any that are polymorphic.
      * If so, require the actual types to be consistent.
      */
+    Assert(nargs <= FUNC_MAX_ARGS);
     for (int j = 0; j < nargs; j++)
     {
         Oid            decl_type = declared_arg_types[j];
@@ -1511,6 +1627,50 @@ check_generic_type_consistency(const Oid *actual_arg_types,
                 return false;
             range_typeid = actual_type;
         }
+        else if (decl_type == ANYCOMPATIBLEOID ||
+                 decl_type == ANYCOMPATIBLENONARRAYOID)
+        {
+            if (decl_type == ANYCOMPATIBLENONARRAYOID)
+                have_anycompatible_nonarray = true;
+            if (actual_type == UNKNOWNOID)
+                continue;
+            /* collect the actual types of non-unknown COMPATIBLE args */
+            anycompatible_actual_types[n_anycompatible_args++] = actual_type;
+        }
+        else if (decl_type == ANYCOMPATIBLEARRAYOID)
+        {
+            Oid            elem_type;
+
+            if (actual_type == UNKNOWNOID)
+                continue;
+            actual_type = getBaseType(actual_type); /* flatten domains */
+            elem_type = get_element_type(actual_type);
+            if (!OidIsValid(elem_type))
+                return false;    /* not an array */
+            /* collect the element type for common-supertype choice */
+            anycompatible_actual_types[n_anycompatible_args++] = elem_type;
+        }
+        else if (decl_type == ANYCOMPATIBLERANGEOID)
+        {
+            if (actual_type == UNKNOWNOID)
+                continue;
+            actual_type = getBaseType(actual_type); /* flatten domains */
+            if (OidIsValid(anycompatible_range_typeid))
+            {
+                /* All ANYCOMPATIBLERANGE arguments must be the same type */
+                if (anycompatible_range_typeid != actual_type)
+                    return false;
+            }
+            else
+            {
+                anycompatible_range_typeid = actual_type;
+                anycompatible_range_typelem = get_range_subtype(actual_type);
+                if (!OidIsValid(anycompatible_range_typelem))
+                    return false;    /* not a range type */
+                /* collect the element type for common-supertype choice */
+                anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+            }
+        }
     }

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

+    /* Check matching of ANYCOMPATIBLE-family arguments, if any */
+    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;        /* there's no common supertype */
+
+        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;
+        }
+
+        /*
+         * the anycompatible type must exactly match the range element type,
+         * if we were able to identify one
+         */
+        if (OidIsValid(anycompatible_range_typelem) &&
+            anycompatible_range_typelem != anycompatible_typeid)
+            return false;
+    }
+
     /* Looks valid */
     return true;
 }
@@ -1610,6 +1802,11 @@ check_generic_type_consistency(const Oid *actual_arg_types,
  * successful, we alter that position of declared_arg_types[] so that
  * make_fn_arguments will coerce the literal to the right thing.
  *
+ * If we have polymorphic arguments of the ANYCOMPATIBLE family,
+ * we similarly alter declared_arg_types[] entries to show the resolved
+ * common supertype, so that make_fn_arguments will coerce the actual
+ * arguments to the proper type.
+ *
  * Rules are applied to the function's return type (possibly altering it)
  * if it is declared as a polymorphic type:
  *
@@ -1631,11 +1828,20 @@ 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.)
+ * 8) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
+ *      ANYCOMPATIBLERANGE are handled by resolving the common supertype
+ *      of those arguments (or their element types, for array and range
+ *      inputs), and then coercing all those arguments to the common supertype,
+ *      or the array type over the common supertype for ANYCOMPATIBLEARRAY.
+ *      For ANYCOMPATIBLERANGE, there must be at least one non-UNKNOWN input,
+ *      all such inputs must be the same range type, and that type's subtype
+ *      must match the common supertype.
  *
  * Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
  * respectively, and are immediately flattened to their base type.  (In
  * particular, if the return type is also ANYARRAY or ANYRANGE, we'll set
- * it to the base type not the domain type.)
+ * it to the base type not the domain type.)  The same is true for
+ * ANYCOMPATIBLEARRAY and ANYCOMPATIBLERANGE.
  *
  * When allow_poly is false, we are not expecting any of the actual_arg_types
  * to be polymorphic, and we should not return a polymorphic result type
@@ -1652,7 +1858,12 @@ check_generic_type_consistency(const Oid *actual_arg_types,
  * the element type to infer the result type.  Note this means that functions
  * taking ANYARRAY had better behave sanely if applied to the pg_statistic
  * columns; they can't just assume that successive inputs are of the same
- * actual element type.
+ * actual element type.  There is no similar logic for ANYCOMPATIBLEARRAY;
+ * there isn't a need for it since there are no catalog columns of that type,
+ * so we won't see it as input.  We could consider matching an actual ANYARRAY
+ * input to an ANYCOMPATIBLEARRAY argument, but at present that seems useless
+ * as well, since there's no value in using ANYCOMPATIBLEARRAY unless there's
+ * at least one other ANYCOMPATIBLE-family argument or result.
  */
 Oid
 enforce_generic_type_consistency(const Oid *actual_arg_types,
@@ -1661,18 +1872,28 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                  Oid rettype,
                                  bool allow_poly)
 {
+    bool        have_poly_anycompatible = false;
     bool        have_poly_unknowns = false;
     Oid            elem_typeid = InvalidOid;
     Oid            array_typeid = InvalidOid;
     Oid            range_typeid = InvalidOid;
-    int            n_poly_args = 0;
+    Oid            anycompatible_typeid = InvalidOid;
+    Oid            anycompatible_array_typeid = InvalidOid;
+    Oid            anycompatible_range_typeid = InvalidOid;
     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);
+    int            n_poly_args = 0;    /* this counts all family-1 arguments */
+    int            n_anycompatible_args = 0;    /* this counts only non-unknowns */
+    Oid            anycompatible_actual_types[FUNC_MAX_ARGS];

     /*
      * Loop through the arguments to see if we have any that are polymorphic.
      * If so, require the actual types to be consistent.
      */
+    Assert(nargs <= FUNC_MAX_ARGS);
     for (int j = 0; j < nargs; j++)
     {
         Oid            decl_type = declared_arg_types[j];
@@ -1743,13 +1964,83 @@ 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_poly_anycompatible = true;
+            if (decl_type == ANYCOMPATIBLENONARRAYOID)
+                have_anycompatible_nonarray = true;
+            if (actual_type == UNKNOWNOID)
+                continue;
+            if (allow_poly && decl_type == actual_type)
+                continue;        /* no new information here */
+            /* collect the actual types of non-unknown COMPATIBLE args */
+            anycompatible_actual_types[n_anycompatible_args++] = actual_type;
+        }
+        else if (decl_type == ANYCOMPATIBLEARRAYOID)
+        {
+            Oid            anycompatible_elem_type;
+
+            have_poly_anycompatible = true;
+            have_anycompatible_array = true;
+            if (actual_type == UNKNOWNOID)
+                continue;
+            if (allow_poly && decl_type == actual_type)
+                continue;        /* no new information here */
+            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",
+                                "anycompatiblearray",
+                                format_type_be(actual_type))));
+            /* collect the element type for common-supertype choice */
+            anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type;
+        }
+        else if (decl_type == ANYCOMPATIBLERANGEOID)
+        {
+            Oid            anycompatible_range_typelem;
+
+            have_poly_anycompatible = true;
+            have_anycompatible_range = true;
+            if (actual_type == UNKNOWNOID)
+                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))
+            {
+                /* All ANYCOMPATIBLERANGE arguments must be the same type */
+                if (anycompatible_range_typeid != actual_type)
+                    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))));
+            }
+            else
+            {
+                anycompatible_range_typeid = actual_type;
+                anycompatible_range_typelem = get_range_subtype(actual_type);
+                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(actual_type))));
+                /* collect the element type for common-supertype choice */
+                anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+            }
+        }
     }

     /*
      * Fast Track: if none of the arguments are polymorphic, return the
      * unmodified rettype.  We assume it can't be polymorphic either.
      */
-    if (n_poly_args == 0)
+    if (n_poly_args == 0 && !have_poly_anycompatible)
     {
         Assert(!IsPolymorphicType(rettype));
         return rettype;
@@ -1766,13 +2057,14 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
             {
                 /*
                  * Special case for matching ANYARRAY input to an ANYARRAY
-                 * argument: allow it iff no other arguments are polymorphic
-                 * (otherwise we couldn't be sure whether the array element
-                 * type matches up) and the result type doesn't require us to
-                 * infer a specific element type.
+                 * argument: allow it iff no other arguments are family-1
+                 * polymorphics (otherwise we couldn't be sure whether the
+                 * array element type matches up) and the result type doesn't
+                 * require us to infer a specific element type.
                  */
                 if (n_poly_args != 1 ||
-                    (rettype != ANYARRAYOID && IsPolymorphicType(rettype)))
+                    (rettype != ANYARRAYOID &&
+                     IsPolymorphicTypeFamily1(rettype)))
                     ereport(ERROR,
                             (errcode(ERRCODE_DATATYPE_MISMATCH),
                              errmsg("cannot determine element type of \"anyarray\" argument")));
@@ -1888,9 +2180,93 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
         }
     }

+    /* Check matching of ANYCOMPATIBLE-family arguments, if any */
+    if (have_poly_anycompatible)
+    {
+        if (n_anycompatible_args > 0)
+        {
+            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 anycompatiblenonarray is an array type: %s",
+                                    format_type_be(anycompatible_typeid))));
+            }
+        }
+        else
+        {
+            if (allow_poly)
+            {
+                anycompatible_typeid = ANYCOMPATIBLEOID;
+                anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
+                anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
+            }
+            else
+            {
+                /*
+                 * Only way to get here is if all the ANYCOMPATIBLE args have
+                 * UNKNOWN inputs
+                 *
+                 * XXX shouldn't we use TEXT?
+                 */
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("could not determine polymorphic common type because input has type %s",
+                                "unknown")));
+            }
+        }
+
+        /* replace polymorphic common types by selected common types */
+        for (int j = 0; j < nargs; j++)
+        {
+            Oid            decl_type = declared_arg_types[j];
+
+            if (decl_type == ANYCOMPATIBLEOID ||
+                decl_type == ANYCOMPATIBLENONARRAYOID)    /* XXX wrong for allow_poly? */
+                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;
+        }
+    }
+
     /*
      * If we had any UNKNOWN inputs for polymorphic arguments, re-scan to
      * assign correct types to them.
+     *
+     * Note: we don't have to consider unknown inputs that were matched to
+     * ANYCOMPATIBLE-family arguments, because we forcibly updated their
+     * declared_arg_types[] positions just above.
      */
     if (have_poly_unknowns)
     {
@@ -1967,6 +2343,38 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
         return range_typeid;
     }

+    /* if we return ANYCOMPATIBLE use the appropriate type */
+    if (rettype == ANYCOMPATIBLEOID ||
+        rettype == ANYCOMPATIBLENONARRAYOID)
+    {
+        if (!OidIsValid(anycompatible_typeid))
+            ereport(ERROR,
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg("could not find common type")));
+        return anycompatible_typeid;
+    }
+
+    /* if we return ANYCOMPATIBLEARRAY use the appropriate type */
+    if (rettype == ANYCOMPATIBLEARRAYOID)
+    {
+        if (!OidIsValid(anycompatible_array_typeid))
+            ereport(ERROR,
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg("could not find common array type")));
+        return anycompatible_array_typeid;
+    }
+
+    /* if we return ANYCOMPATIBLERANGE 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;
 }
@@ -1984,11 +2392,12 @@ check_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++)
         {
@@ -1998,17 +2407,30 @@ check_valid_polymorphic_signature(Oid ret_type,
         return psprintf(_("A result of type %s requires at least one input of type %s."),
                         format_type_be(ret_type), format_type_be(ret_type));
     }
-    else if (IsPolymorphicType(ret_type))
+    else if (IsPolymorphicTypeFamily1(ret_type))
     {
-        /* Otherwise, any polymorphic type can be deduced from any other */
+        /* Otherwise, any family-1 type can be deduced from any other */
         for (int i = 0; i < nargs; i++)
         {
-            if (IsPolymorphicType(declared_arg_types[i]))
+            if (IsPolymorphicTypeFamily1(declared_arg_types[i]))
                 return NULL;    /* OK */
         }
+        /* Keep this list in sync with IsPolymorphicTypeFamily1! */
         return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray,
anyenum,or anyrange."), 
                         format_type_be(ret_type));
     }
+    else if (IsPolymorphicTypeFamily2(ret_type))
+    {
+        /* Otherwise, any family-2 type can be deduced from any other */
+        for (int i = 0; i < nargs; i++)
+        {
+            if (IsPolymorphicTypeFamily2(declared_arg_types[i]))
+                return NULL;    /* OK */
+        }
+        /* Keep this list in sync with IsPolymorphicTypeFamily2! */
+        return psprintf(_("A result of type %s requires at least one input of type anycompatible, anycompatiblearray,
anycompatiblenonarray,or anycompatiblerange."), 
+                        format_type_be(ret_type));
+    }
     else
         return NULL;            /* OK, ret_type is not polymorphic */
 }
@@ -2113,8 +2535,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 */
@@ -2125,13 +2548,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;

@@ -2140,8 +2563,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 9eee03c..3d6b2f9 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -169,6 +169,26 @@ anyarray_send(PG_FUNCTION_ARGS)
 }

 /*
+ * anycompatiblearray
+ *
+ * We may as well allow output, since we do for anyarray.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblearray);
+PSEUDOTYPE_DUMMY_RECEIVE_FUNC(anycompatiblearray);
+
+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.
@@ -195,6 +215,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
  *
  * We support void_in so that PL functions can return VOID without any
@@ -316,3 +349,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 4e9d21b..78ed857 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -552,7 +552,9 @@ resolve_anyrange_from_others(polymorphic_actuals *actuals)
  * with concrete data types deduced from the input arguments.
  * declared_args is an oidvector of the function's declared input arg types
  * (showing which are polymorphic), and call_expr is the call expression.
- * Returns true if able to deduce all types, false if not.
+ *
+ * Returns true if able to deduce all types, false if necessary information
+ * is not provided (call_expr is NULL or arg types aren't identifiable).
  */
 static bool
 resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
@@ -564,8 +566,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
     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;
     polymorphic_actuals poly_actuals;
+    polymorphic_actuals anyc_actuals;
     Oid            anycollation = InvalidOid;
+    Oid            anycompatcollation = InvalidOid;
     int            i;

     /* See if there are any polymorphic outputs; quick out if not */
@@ -587,6 +594,19 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                 have_polymorphic_result = true;
                 have_anyrange_result = true;
                 break;
+            case ANYCOMPATIBLEOID:
+            case ANYCOMPATIBLENONARRAYOID:
+                have_polymorphic_result = true;
+                have_anycompatible_result = true;
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                have_polymorphic_result = true;
+                have_anycompatible_array_result = true;
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                have_polymorphic_result = true;
+                have_anycompatible_range_result = true;
+                break;
             default:
                 break;
         }
@@ -596,12 +616,16 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,

     /*
      * 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 */

     memset(&poly_actuals, 0, sizeof(poly_actuals));
+    memset(&anyc_actuals, 0, sizeof(anyc_actuals));

     for (i = 0; i < nargs; i++)
     {
@@ -636,6 +660,34 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                         return false;
                 }
                 break;
+            case ANYCOMPATIBLEOID:
+            case ANYCOMPATIBLENONARRAYOID:
+                if (!OidIsValid(anyc_actuals.anyelement_type))
+                {
+                    anyc_actuals.anyelement_type =
+                        get_call_expr_argtype(call_expr, i);
+                    if (!OidIsValid(anyc_actuals.anyelement_type))
+                        return false;
+                }
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                if (!OidIsValid(anyc_actuals.anyarray_type))
+                {
+                    anyc_actuals.anyarray_type =
+                        get_call_expr_argtype(call_expr, i);
+                    if (!OidIsValid(anyc_actuals.anyarray_type))
+                        return false;
+                }
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                if (!OidIsValid(anyc_actuals.anyrange_type))
+                {
+                    anyc_actuals.anyrange_type =
+                        get_call_expr_argtype(call_expr, i);
+                    if (!OidIsValid(anyc_actuals.anyrange_type))
+                        return false;
+                }
+                break;
             default:
                 break;
         }
@@ -651,18 +703,33 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
     if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
         resolve_anyrange_from_others(&poly_actuals);

+    if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
+        resolve_anyelement_from_others(&anyc_actuals);
+
+    if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
+        resolve_anyarray_from_others(&anyc_actuals);
+
+    if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
+        resolve_anyrange_from_others(&anyc_actuals);
+
     /*
      * Identify the collation to use for polymorphic OUT parameters. (It'll
-     * necessarily be the same for both anyelement and anyarray.)  Note that
-     * range types are not collatable, so any possible internal collation of a
-     * range type is not considered here.
+     * necessarily be the same for both anyelement and anyarray, likewise for
+     * anycompatible and anycompatiblearray.)  Note that range types are not
+     * collatable, so any possible internal collation of a range type is not
+     * considered here.
      */
     if (OidIsValid(poly_actuals.anyelement_type))
         anycollation = get_typcollation(poly_actuals.anyelement_type);
     else if (OidIsValid(poly_actuals.anyarray_type))
         anycollation = get_typcollation(poly_actuals.anyarray_type);

-    if (OidIsValid(anycollation))
+    if (OidIsValid(anyc_actuals.anyelement_type))
+        anycompatcollation = get_typcollation(anyc_actuals.anyelement_type);
+    else if (OidIsValid(anyc_actuals.anyarray_type))
+        anycompatcollation = get_typcollation(anyc_actuals.anyarray_type);
+
+    if (OidIsValid(anycollation) || OidIsValid(anycompatcollation))
     {
         /*
          * The types are collatable, so consider whether to use a nondefault
@@ -672,7 +739,12 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
         Oid            inputcollation = exprInputCollation(call_expr);

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

     /* And finally replace the tuple column types as needed */
@@ -708,6 +780,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),
+                                   anyc_actuals.anyelement_type,
+                                   -1,
+                                   0);
+                TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                TupleDescInitEntry(tupdesc, i + 1,
+                                   NameStr(att->attname),
+                                   anyc_actuals.anyarray_type,
+                                   -1,
+                                   0);
+                TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                TupleDescInitEntry(tupdesc, i + 1,
+                                   NameStr(att->attname),
+                                   anyc_actuals.anyrange_type,
+                                   -1,
+                                   0);
+                /* no collation should be attached to a range type */
+                break;
             default:
                 break;
         }
@@ -720,7 +817,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
  * Given the declared argument types and modes for a function, replace any
  * polymorphic types (ANYELEMENT etc) in argtypes[] with concrete data types
  * deduced from the input arguments found in call_expr.
- * Returns true if able to deduce all types, false if not.
+ *
+ * Returns true if able to deduce all types, false if necessary information
+ * is not provided (call_expr is NULL or arg types aren't identifiable).
  *
  * This is the same logic as resolve_polymorphic_tupdesc, but with a different
  * argument representation, and slightly different output responsibilities.
@@ -735,16 +834,21 @@ 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;
     polymorphic_actuals poly_actuals;
+    polymorphic_actuals anyc_actuals;
     int            inargno;
     int            i;

     /*
      * First pass: resolve polymorphic inputs, check for outputs.  As in
      * resolve_polymorphic_tupdesc, we rely on the parser to have enforced
-     * type consistency.
+     * type consistency and coerced ANYCOMPATIBLE args to a common supertype.
      */
     memset(&poly_actuals, 0, sizeof(poly_actuals));
+    memset(&anyc_actuals, 0, sizeof(anyc_actuals));
     inargno = 0;
     for (i = 0; i < numargs; i++)
     {
@@ -808,6 +912,61 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
                     argtypes[i] = poly_actuals.anyrange_type;
                 }
                 break;
+            case ANYCOMPATIBLEOID:
+            case ANYCOMPATIBLENONARRAYOID:
+                if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+                {
+                    have_polymorphic_result = true;
+                    have_anycompatible_result = true;
+                }
+                else
+                {
+                    if (!OidIsValid(anyc_actuals.anyelement_type))
+                    {
+                        anyc_actuals.anyelement_type =
+                            get_call_expr_argtype(call_expr, inargno);
+                        if (!OidIsValid(anyc_actuals.anyelement_type))
+                            return false;
+                    }
+                    argtypes[i] = anyc_actuals.anyelement_type;
+                }
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+                {
+                    have_polymorphic_result = true;
+                    have_anycompatible_array_result = true;
+                }
+                else
+                {
+                    if (!OidIsValid(anyc_actuals.anyarray_type))
+                    {
+                        anyc_actuals.anyarray_type =
+                            get_call_expr_argtype(call_expr, inargno);
+                        if (!OidIsValid(anyc_actuals.anyarray_type))
+                            return false;
+                    }
+                    argtypes[i] = anyc_actuals.anyarray_type;
+                }
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+                {
+                    have_polymorphic_result = true;
+                    have_anycompatible_range_result = true;
+                }
+                else
+                {
+                    if (!OidIsValid(anyc_actuals.anyrange_type))
+                    {
+                        anyc_actuals.anyrange_type =
+                            get_call_expr_argtype(call_expr, inargno);
+                        if (!OidIsValid(anyc_actuals.anyrange_type))
+                            return false;
+                    }
+                    argtypes[i] = anyc_actuals.anyrange_type;
+                }
+                break;
             default:
                 break;
         }
@@ -829,6 +988,15 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
     if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
         resolve_anyrange_from_others(&poly_actuals);

+    if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
+        resolve_anyelement_from_others(&anyc_actuals);
+
+    if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
+        resolve_anyarray_from_others(&anyc_actuals);
+
+    if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
+        resolve_anyrange_from_others(&anyc_actuals);
+
     /* And finally replace the output column types as needed */
     for (i = 0; i < numargs; i++)
     {
@@ -845,6 +1013,16 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
             case ANYRANGEOID:
                 argtypes[i] = poly_actuals.anyrange_type;
                 break;
+            case ANYCOMPATIBLEOID:
+            case ANYCOMPATIBLENONARRAYOID:
+                argtypes[i] = anyc_actuals.anyelement_type;
+                break;
+            case ANYCOMPATIBLEARRAYOID:
+                argtypes[i] = anyc_actuals.anyarray_type;
+                break;
+            case ANYCOMPATIBLERANGEOID:
+                argtypes[i] = anyc_actuals.anyrange_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 331562d..9084dce 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -1621,3 +1621,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 9c7b43e..529aa5b 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -868,3 +868,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 по дате отправления:

Предыдущее
От: yuzuko
Дата:
Сообщение: Re: Autovacuum on partitioned table
Следующее
От: Alvaro Herrera
Дата:
Сообщение: Re: WIP: WAL prefetch (another approach)