Обсуждение: proposal: new polymorphic types - commontype and commontypearray

Поиск
Список
Период
Сортировка

proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:
Hi,

the possibility to use polymorphic types is a specific interesting PostgreSQL feature. The polymorphic types allows to use almost all types, but when some type is selected, then this type is required strictly without possibility to use some implicit casting.

So if I have a fx(anyelement, anyelement), then I can call function fx with parameters (int, int), (numeric, numeric), but I cannot to use parameters (int, numeric). The strict design has sense, but for few important cases is too restrictive. We are not able to implement (with plpgsql) functions like coalesce, greatest, least where all numeric types can be used.

Alternative solution can be based on usage "any" type. But we can work with this type only from "C" extensions, and there is some performance penalization due dynamic casting inside function.

Four years ago I proposed implicit casting to common type of arguments with anyelement type.


My proposal was rejected, because it introduce compatibility issues.

Now I have a solution that doesn't break anything. With two new polymorphic types: commontype and commontypearray we can write functions like coalesce, greatest, ..

More, these types are independent on current polymorphic types - and can be used with current polymorphic types together to cover some new use cases.

CREATE OR REPLACE FUNCTION fx(anyelement, commontype, anyelement, commontype)
RETURNS commontype

or

CREATE OR REPLACE FUNCTION fx(anyelement, commontype, anyelement, commontype)
RETURNS anyelement

and commontype and anyelement types can be really independent.

Comments, notes?

Regards

Pavel


Вложения

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> Four years ago I proposed implicit casting to common type of arguments with
> anyelement type.
> https://www.postgresql.org/message-id/CAFj8pRCZVo_xoW0cfxt%3DmmgjXKBgr3Gm1VMGL_zx9wDRHmm6Cw%40mail.gmail.com
> My proposal was rejected, because it introduce compatibility issues.

Yup.

> Now I have a solution that doesn't break anything. With two new polymorphic
> types: commontype and commontypearray we can write functions like coalesce,
> greatest, ..

I think this is a good idea at its core, but I don't like the specifics
too much.

I agree with the basic idea of introducing a second, independent set of
polymorphic-type variables.  Way back when we first discussed polymorphic
types, we thought maybe we should invent anyelement2 and anyarray2, and
perhaps even more pairs, to allow polymorphic functions to deal with two
or more base types.  We didn't do that for lack of convincing examples of
the need for it, but I expected some would emerge; I'm rather astonished
that we've gotten by for so many years without adding a second set.
So where I think we should go with this is to solve that need while
we're at it.

However, this proposal doesn't do so, because it omits "commonrange".
I'm prepared to believe that we don't need "commonenum"; that would
presumably have the semantics of "resolve the common type and then
it must be an enum".  And that seems pretty useless, because there
are no type resolution rules that would let us choose one enum out of
a set.  (I suppose somebody might create implicit casts between some
enum types, but it doesn't seem very likely.)  I also suspect that
we could get away without "commonnonarray".  Anynonarray is really
just a hack that we invented to avoid ambiguity around the ||
operator, and an equivalent need would likely not come up for this
second set of types.  (I could be wrong though; I'm not sure right
now whether array_agg's use of anynonarray rather than anyelement
is essential or just randomness.)  But neither of those arguments
apply to commonrange; in fact it's highly likely that somebody would
want to have "myfunc(commontype, commontype) returns commonrange"
as a customized range constructor that can deal with slightly
different input types.

My second problem with this proposal is that it simply ignores
the naming precedent of the existing polymorphic types.  We have
a convention that polymorphic types are named "any-something",
and I do not think we should just toss that overboard.  Moreover,
if we do end up needing "commonnonarray" or "commonenum", those
names are ugly, typo-prone, and unreasonably long.

We could do worse than to call these types anyelement2, anyarray2,
anyrange2 and just document that their resolution rule depends
on finding a common type rather than identical base types.
I suppose that's not too pretty --- it reminds one of Oracle finally
getting varchar semantics right with varchar2 :-(.  Another idea
is anyelementc, anyarrayc, anyrangec ("c" for "common") but that's
not pretty either.  Anyway I think the names need to be any-something.

I haven't particularly studied the patch code, but I will note that
this sort of change seems pretty dumb:

@@ -953,7 +953,7 @@ make_scalar_array_op(ParseState *pstate, List *opname,
      * enforce_generic_type_consistency may or may not have replaced a
      * polymorphic type with a real one.
      */
-    if (IsPolymorphicType(declared_arg_types[1]))
+    if (IsPolymorphicTypeAny(declared_arg_types[1]))
     {
         /* assume the actual array type is OK */
         res_atypeId = atypeId;

Why would we want to reject the new poly types here?  Or just about
anyplace else that tests IsPolymorphicType?  The argument-type resolution
functions themselves need to distinguish the two groups of types,
at least for some purposes, but it's very hard to believe anyplace
else should do so.

            regards, tom lane


Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


so 26. 1. 2019 v 1:20 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> Four years ago I proposed implicit casting to common type of arguments with
> anyelement type.
> https://www.postgresql.org/message-id/CAFj8pRCZVo_xoW0cfxt%3DmmgjXKBgr3Gm1VMGL_zx9wDRHmm6Cw%40mail.gmail.com
> My proposal was rejected, because it introduce compatibility issues.

Yup.

> Now I have a solution that doesn't break anything. With two new polymorphic
> types: commontype and commontypearray we can write functions like coalesce,
> greatest, ..

I think this is a good idea at its core, but I don't like the specifics
too much.

I agree with the basic idea of introducing a second, independent set of
polymorphic-type variables.  Way back when we first discussed polymorphic
types, we thought maybe we should invent anyelement2 and anyarray2, and
perhaps even more pairs, to allow polymorphic functions to deal with two
or more base types.  We didn't do that for lack of convincing examples of
the need for it, but I expected some would emerge; I'm rather astonished
that we've gotten by for so many years without adding a second set.
So where I think we should go with this is to solve that need while
we're at it.

I still expect we can have a more polymorphic types like any***(N)

There are important questions

1. Has a sense to have more distinct polymorphic types with same be behave? - I see a benefit of this possibility - we can introduce anyelement2 .. AN2 and we can have a function

  fx(AN,AN, AN2,AN2) .. that means P2 and P4 should to have a same types like P1 and P3.

2. What are a strategy of choosing real type for polymorphic types? - now only equivalence is supported, but I can see more possibilities

  * common type - I did it
  * first win - often used in Oracle

The common type strategy is more important, because it is typical for some "pseudo" functions like coalesce, least, greatest, .. in Postgres - and extension's developers can design functions more compatible with core functionality.

first win can be interesting for me (like Orafce creator and maintainer). It can increase level of similarity implemented functions there, and reduce work when queries are ported to Postgres. But this is important for smaller group of PostgreSQL users.
 

However, this proposal doesn't do so, because it omits "commonrange".
I'm prepared to believe that we don't need "commonenum"; that would
presumably have the semantics of "resolve the common type and then
it must be an enum".  And that seems pretty useless, because there
are no type resolution rules that would let us choose one enum out of
a set.  (I suppose somebody might create implicit casts between some
enum types, but it doesn't seem very likely.)  I also suspect that
we could get away without "commonnonarray".  Anynonarray is really
just a hack that we invented to avoid ambiguity around the ||
operator, and an equivalent need would likely not come up for this
second set of types.  (I could be wrong though; I'm not sure right
now whether array_agg's use of anynonarray rather than anyelement
is essential or just randomness.)  But neither of those arguments
apply to commonrange; in fact it's highly likely that somebody would
want to have "myfunc(commontype, commontype) returns commonrange"
as a customized range constructor that can deal with slightly
different input types.

I implemented just minimal set of new polymorphic types, just for demonstration of my idea. Better coverage of other variants (where it has a sense) is not a problem. Now, mostly I am searching a design where can be a some agreement.
 

My second problem with this proposal is that it simply ignores
the naming precedent of the existing polymorphic types.  We have
a convention that polymorphic types are named "any-something",
and I do not think we should just toss that overboard.  Moreover,
if we do end up needing "commonnonarray" or "commonenum", those
names are ugly, typo-prone, and unreasonably long.

We could do worse than to call these types anyelement2, anyarray2,
anyrange2 and just document that their resolution rule depends
on finding a common type rather than identical base types.
I suppose that's not too pretty --- it reminds one of Oracle finally
getting varchar semantics right with varchar2 :-(.  Another idea
is anyelementc, anyarrayc, anyrangec ("c" for "common") but that's
not pretty either.  Anyway I think the names need to be any-something.

I am open to any ideas. I don't like anyelement2, anyarray2 because

a) it is not verbose - and really different behave should not be signed by number
b) I can imagine very well more anyelementX types.

I don't think so length is too important factor (but I fully agree - shorter is better here). The polymorphic types are not too common.

I though about your proposed anyelementc, but the "c" is not much visible. Can we use snake notation?

commontype, commottype_array, commontype_nonarray ..
common_type, common_type_array, ...

I am not fully happy with "commontype", but I didn't find better




I haven't particularly studied the patch code, but I will note that
this sort of change seems pretty dumb:

@@ -953,7 +953,7 @@ make_scalar_array_op(ParseState *pstate, List *opname,
         * enforce_generic_type_consistency may or may not have replaced a
         * polymorphic type with a real one.
         */
-       if (IsPolymorphicType(declared_arg_types[1]))
+       if (IsPolymorphicTypeAny(declared_arg_types[1]))
        {
                /* assume the actual array type is OK */
                res_atypeId = atypeId;

Why would we want to reject the new poly types here?  Or just about
anyplace else that tests IsPolymorphicType?  The argument-type resolution
functions themselves need to distinguish the two groups of types,
at least for some purposes, but it's very hard to believe anyplace
else should do so.

Just I use original behave everywhere where I had not a stronger idea to use new polymorphic types there.

Regards

Pavel

                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


My second problem with this proposal is that it simply ignores
the naming precedent of the existing polymorphic types.  We have
a convention that polymorphic types are named "any-something",
and I do not think we should just toss that overboard.  Moreover,
if we do end up needing "commonnonarray" or "commonenum", those
names are ugly, typo-prone, and unreasonably long.

the convention "any-something" is joined with just currently implemented families of polymorphic types. I propose new family, so I think so it should not be named "any-xxxx"

Maybe we can use some form of typemod - but typemod is ignored for function parameters - so it can be much more significant change

a alternative, probably very simple, but less power solution can be some special flag for function parameters - at the end, it is similar to previous solution.

I can imagine

create or replace function fx(p1 anyelement use_common_type, p2 anyelement, ...)
create or replace function fx2(p1 int, p2 int, variadic p3 anyarray use_common_type)

or maybe

create or replace function fx(p1 anyelement, p2 anyelement ...) ... language plpgsql options (use_common_type = true)

or we can drop it - on other thread you propose supported functions - can be some function, that can preproces parameters - and can replace polymorphic types by real types.

Comments, notes?

Pavel




 
                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Robert Haas
Дата:
On Fri, Jan 25, 2019 at 7:21 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Anyway I think the names need to be any-something.

To me, that seems unnecessarily rigid.  Not a bad idea if we can come
up with something that is otherwise acceptable.  But all of your
suggestions sound worse than Pavel's proposal, so...

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


po 28. 1. 2019 v 20:47 odesílatel Robert Haas <robertmhaas@gmail.com> napsal:
On Fri, Jan 25, 2019 at 7:21 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Anyway I think the names need to be any-something.

To me, that seems unnecessarily rigid.  Not a bad idea if we can come
up with something that is otherwise acceptable.  But all of your
suggestions sound worse than Pavel's proposal, so...

I implemented commontypenonarray, and commontyperange types. Now, a SQL functions are supported too.

The naming is same - I had not a better idea. But it can be changed without any problems, if somebody come with some more acceptable.

I don't think so the name is too important. The polymorphic types are important, interesting for extension's developers what is small group of Postgres users. 

And personally, I think so commontype and commontypearray are good enough for not native speakers like me. But I am opened any variant - I think so this functionality is interesting
and partially coverage one gap in our implementation of polymorphic types.

Regards

Pavel



--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Вложения

Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


st 30. 1. 2019 v 17:00 odesílatel Pavel Stehule <pavel.stehule@gmail.com> napsal:


po 28. 1. 2019 v 20:47 odesílatel Robert Haas <robertmhaas@gmail.com> napsal:
On Fri, Jan 25, 2019 at 7:21 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Anyway I think the names need to be any-something.

To me, that seems unnecessarily rigid.  Not a bad idea if we can come
up with something that is otherwise acceptable.  But all of your
suggestions sound worse than Pavel's proposal, so...

I implemented commontypenonarray, and commontyperange types. Now, a SQL functions are supported too.

The naming is same - I had not a better idea. But it can be changed without any problems, if somebody come with some more acceptable.

I don't think so the name is too important. The polymorphic types are important, interesting for extension's developers what is small group of Postgres users. 

And personally, I think so commontype and commontypearray are good enough for not native speakers like me. But I am opened any variant - I think so this functionality is interesting
and partially coverage one gap in our implementation of polymorphic types.

maybe "supertype". It is one char shorter .. somewhere is term "supperclass, ..."

In Czech language this term is short, "nadtyp", but probably it is not acceptable :)



Regards

Pavel



--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Re: proposal: new polymorphic types - commontype and commontypearray

От
Michael Paquier
Дата:
On Wed, Jan 30, 2019 at 05:08:03PM +0100, Pavel Stehule wrote:
> maybe "supertype". It is one char shorter .. somewhere is term
> "supperclass, ..."
>
> In Czech language this term is short, "nadtyp", but probably it is not
> acceptable :)

Moved to next CF.
--
Michael

Вложения

Re: Re: proposal: new polymorphic types - commontype andcommontypearray

От
David Steele
Дата:
On 2/4/19 4:21 AM, Michael Paquier wrote:
> On Wed, Jan 30, 2019 at 05:08:03PM +0100, Pavel Stehule wrote:
>> maybe "supertype". It is one char shorter .. somewhere is term
>> "supperclass, ..."
>>
>> In Czech language this term is short, "nadtyp", but probably it is not
>> acceptable :)
> 
> Moved to next CF.

This thread has been very quiet for a month.  I agree with Andres [1] 
that we should push this to PG13.

-- 
-David
david@pgmasters.net

[1] 
https://www.postgresql.org/message-id/raw/20190214203752.t4hl574k6jlu4t25%40alap3.anarazel.de 



Re: Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


út 5. 3. 2019 v 14:38 odesílatel David Steele <david@pgmasters.net> napsal:
On 2/4/19 4:21 AM, Michael Paquier wrote:
> On Wed, Jan 30, 2019 at 05:08:03PM +0100, Pavel Stehule wrote:
>> maybe "supertype". It is one char shorter .. somewhere is term
>> "supperclass, ..."
>>
>> In Czech language this term is short, "nadtyp", but probably it is not
>> acceptable :)
>
> Moved to next CF.

This thread has been very quiet for a month.  I agree with Andres [1]
that we should push this to PG13.

ok

Pavel


--
-David
david@pgmasters.net

[1]
https://www.postgresql.org/message-id/raw/20190214203752.t4hl574k6jlu4t25%40alap3.anarazel.de

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
David Steele <david@pgmasters.net> writes:
> This thread has been very quiet for a month.  I agree with Andres [1] 
> that we should push this to PG13.

I think the main thing it's blocked on is disagreement on what the
type name should be, which is kind of a silly thing to get blocked on,
but nonetheless it's important ...

            regards, tom lane


Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


út 5. 3. 2019 v 15:35 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
David Steele <david@pgmasters.net> writes:
> This thread has been very quiet for a month.  I agree with Andres [1]
> that we should push this to PG13.

I think the main thing it's blocked on is disagreement on what the
type name should be, which is kind of a silly thing to get blocked on,
but nonetheless it's important ...

I sent some others possible names, but probably this mail was forgotten

What about "ctype" like shortcut for common type? carraytype, cnonarraytype?

Regards

Pavel

                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:
Hi

út 5. 3. 2019 v 18:37 odesílatel Pavel Stehule <pavel.stehule@gmail.com> napsal:


út 5. 3. 2019 v 15:35 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
David Steele <david@pgmasters.net> writes:
> This thread has been very quiet for a month.  I agree with Andres [1]
> that we should push this to PG13.

I think the main thing it's blocked on is disagreement on what the
type name should be, which is kind of a silly thing to get blocked on,
but nonetheless it's important ...

I sent some others possible names, but probably this mail was forgotten

What about "ctype" like shortcut for common type? carraytype, cnonarraytype?

rebase for PostgreSQL 13

Regards

Pavel


Regards

Pavel

                        regards, tom lane
Вложения

Re: proposal: new polymorphic types - commontype and commontypearray

От
Greg Stark
Дата:
The proposals I see above are "commontype", "supertype", "anycommontype", and various abbreviations of those. I would humbly add "compatibletype".

Fwiw I kind of like commontype.

Alternately an argument could be made that length and typing convenience isn't really a factor here since database users never have to type these types. The only place they get written is when defining polymorphic functions which is a pretty uncommon operation.

In which case a very explicit "anycompatibletype" may be better.

On Tue., Mar. 5, 2019, 12:38 p.m. Pavel Stehule, <pavel.stehule@gmail.com> wrote:


út 5. 3. 2019 v 15:35 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
David Steele <david@pgmasters.net> writes:
> This thread has been very quiet for a month.  I agree with Andres [1]
> that we should push this to PG13.

I think the main thing it's blocked on is disagreement on what the
type name should be, which is kind of a silly thing to get blocked on,
but nonetheless it's important ...

I sent some others possible names, but probably this mail was forgotten

What about "ctype" like shortcut for common type? carraytype, cnonarraytype?

Regards

Pavel

                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
Greg Stark <stark@mit.edu> writes:
> The proposals I see above are "commontype", "supertype", "anycommontype",
> and various abbreviations of those. I would humbly add "compatibletype".
> Fwiw I kind of like commontype.
> Alternately an argument could be made that length and typing convenience
> isn't really a factor here since database users never have to type these
> types. The only place they get written is when defining polymorphic
> functions which is a pretty uncommon operation.
> In which case a very explicit "anycompatibletype" may be better.

I could go with "anycompatibletype".  That would lead us to needing
related names like "anycompatiblearraytype", which is getting annoyingly
long, but you might be right that people wouldn't have to type it that
often.

Also, given the precedent of "anyarray" and "anyrange", it might be
okay to make these just "anycompatible" and "anycompatiblearray".

[ wanders away wondering if psql can tab-complete type names in
function definitions ... ]

            regards, tom lane



Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


čt 13. 6. 2019 v 2:37 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Greg Stark <stark@mit.edu> writes:
> The proposals I see above are "commontype", "supertype", "anycommontype",
> and various abbreviations of those. I would humbly add "compatibletype".
> Fwiw I kind of like commontype.
> Alternately an argument could be made that length and typing convenience
> isn't really a factor here since database users never have to type these
> types. The only place they get written is when defining polymorphic
> functions which is a pretty uncommon operation.
> In which case a very explicit "anycompatibletype" may be better.

I could go with "anycompatibletype".  That would lead us to needing
related names like "anycompatiblearraytype", which is getting annoyingly
long, but you might be right that people wouldn't have to type it that
often.

Also, given the precedent of "anyarray" and "anyrange", it might be
okay to make these just "anycompatible" and "anycompatiblearray".

I like anycompatible and anycompatiblearray.

I'll update the patch

Regards

Pavel


[ wanders away wondering if psql can tab-complete type names in
function definitions ... ]

                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:
Hi

pá 14. 6. 2019 v 6:09 odesílatel Pavel Stehule <pavel.stehule@gmail.com> napsal:


čt 13. 6. 2019 v 2:37 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Greg Stark <stark@mit.edu> writes:
> The proposals I see above are "commontype", "supertype", "anycommontype",
> and various abbreviations of those. I would humbly add "compatibletype".
> Fwiw I kind of like commontype.
> Alternately an argument could be made that length and typing convenience
> isn't really a factor here since database users never have to type these
> types. The only place they get written is when defining polymorphic
> functions which is a pretty uncommon operation.
> In which case a very explicit "anycompatibletype" may be better.

I could go with "anycompatibletype".  That would lead us to needing
related names like "anycompatiblearraytype", which is getting annoyingly
long, but you might be right that people wouldn't have to type it that
often.

Also, given the precedent of "anyarray" and "anyrange", it might be
okay to make these just "anycompatible" and "anycompatiblearray".

I like anycompatible and anycompatiblearray.

I'll update the patch

and here it is

Regards

Pavel
 

Regards

Pavel


[ wanders away wondering if psql can tab-complete type names in
function definitions ... ]

                        regards, tom lane
Вложения

Re: proposal: new polymorphic types - commontype and commontypearray

От
Dmitry Dolgov
Дата:
> On Mon, Jun 17, 2019 at 05:31:40AM +0200, Pavel Stehule wrote:
>
> > I like anycompatible and anycompatiblearray.
> >
> > I'll update the patch
> >
>
> and here it is

Thanks for the patch! I've reviewed it a bit, and have a few small
commentaries:

* There are few traces of copy paste in comments

    +static Oid
    +select_common_type_from_vector(int nargs, Oid *typeids, bool noerror)
    ...
    +    /*
    +     * Nope, so set up for the full algorithm.  Note that at this point, lc
    +     * points to the first list item with type different from pexpr's; we need
    +     * not re-examine any items the previous loop advanced over.
    +     */

Seems like it was taken from select_common_type, but in
select_common_type_from_vector there is no `lc`, since it doesn't
accept a list.

* I guess it's would be beneficial to update also commentaries for
check_generic_type_consistency and enforce_generic_type_consistency

     * The argument consistency rules are:
     *
     * 1) All arguments declared ANYELEMENT must have the same datatype.
     * ...

Since they do not reflect the current state of things in this patch.

* I've noticed that there is a small difference in how anyelement and
anycompatible behave, namely anycompatible do not handle unknowns:

    =# select 'aaa'::anyelement;
     anyelement
    ------------
     aaa

    =# select 'aaa'::anycompatible;
    ERROR:  42846: cannot cast type unknown to anycompatible
    LINE 1: select 'aaa'::anycompatible;
                        ^
    LOCATION:  transformTypeCast, parse_expr.c:2823

It happens due to unknowns being filtered out quite early in
check_generic_type_consistency and similar. By itself this difference it not a
problem, but it causes different error messages in functions:

    -- this function accepts anycompatible
    =# select test_anycompatible('aaa');
    ERROR:  42883: function test_anycompatible(unknown) does not exist
    LINE 1: select test_anycompatible('aaa');
                   ^
    HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
    LOCATION:  ParseFuncOrColumn, parse_func.c:627

    -- this function accepts anyelement
    =# select test_anyelement('aaa');
    ERROR:  42804: could not determine polymorphic type because input has type unknown
    LOCATION:  enforce_generic_type_consistency, parse_coerce.c:2177

Although of course it's not that serious.

* I'm also curious about the following situation:

    =# create function test_both(a anycompatible) returns anycompatible as $$
    begin
        return a;
    end$$ language plpgsql;
    CREATE FUNCTION

    =# create function test_both(a anyelement) returns anyelement as $$
    begin
        return a;
    end$$ language plpgsql;
    CREATE FUNCTION

    =# select test_both('aaa'::text);
    ERROR:  42725: function test_both(text) is not unique
    LINE 1: select test_both('aaa'::text);
                   ^
    HINT:  Could not choose a best candidate function. You might need to add explicit type casts.
    LOCATION:  ParseFuncOrColumn, parse_func.c:568

    =# select test_both('aaa'::anyelement);
    ERROR:  42804: could not determine polymorphic type because input has type unknown
    LOCATION:  enforce_generic_type_consistency, parse_coerce.c:2177

Is it possible somehow to invoke any of these functions?

Other than that the functionality looks pretty solid. It may look obvious, but
I've also tested performance in different use cases for anycompatible, looks
the same as for anyelement.



Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


po 25. 11. 2019 v 14:35 odesílatel Dmitry Dolgov <9erthalion6@gmail.com> napsal:
> On Mon, Jun 17, 2019 at 05:31:40AM +0200, Pavel Stehule wrote:
>
> > I like anycompatible and anycompatiblearray.
> >
> > I'll update the patch
> >
>
> and here it is

Thanks for the patch! I've reviewed it a bit, and have a few small
commentaries:

* There are few traces of copy paste in comments

    +static Oid
    +select_common_type_from_vector(int nargs, Oid *typeids, bool noerror)
    ...
    +   /*
    +    * Nope, so set up for the full algorithm.  Note that at this point, lc
    +    * points to the first list item with type different from pexpr's; we need
    +    * not re-examine any items the previous loop advanced over.
    +    */

Seems like it was taken from select_common_type, but in
select_common_type_from_vector there is no `lc`, since it doesn't
accept a list.

fixed 

* I guess it's would be beneficial to update also commentaries for
check_generic_type_consistency and enforce_generic_type_consistency

     * The argument consistency rules are:
     *
     * 1) All arguments declared ANYELEMENT must have the same datatype.
     * ...

Since they do not reflect the current state of things in this patch.

I add rules 8 and 9 about ANYCOMPATIBLE types


* I've noticed that there is a small difference in how anyelement and
anycompatible behave, namely anycompatible do not handle unknowns:

        =# select 'aaa'::anyelement;
         anyelement
        ------------
         aaa

        =# select 'aaa'::anycompatible;
        ERROR:  42846: cannot cast type unknown to anycompatible
        LINE 1: select 'aaa'::anycompatible;
                                                ^
        LOCATION:  transformTypeCast, parse_expr.c:2823
 
It happens due to unknowns being filtered out quite early in
check_generic_type_consistency and similar. By itself this difference it not a
problem, but it causes different error messages in functions:

        -- this function accepts anycompatible
        =# select test_anycompatible('aaa');
        ERROR:  42883: function test_anycompatible(unknown) does not exist
        LINE 1: select test_anycompatible('aaa');
                                   ^
        HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
        LOCATION:  ParseFuncOrColumn, parse_func.c:627

        -- this function accepts anyelement
        =# select test_anyelement('aaa');
        ERROR:  42804: could not determine polymorphic type because input has type unknown
        LOCATION:  enforce_generic_type_consistency, parse_coerce.c:2177

fixed


Although of course it's not that serious.

* I'm also curious about the following situation:

        =# create function test_both(a anycompatible) returns anycompatible as $$
        begin
                return a;
        end$$ language plpgsql;
        CREATE FUNCTION

        =# create function test_both(a anyelement) returns anyelement as $$
        begin
                return a;
        end$$ language plpgsql;
        CREATE FUNCTION

        =# select test_both('aaa'::text);
        ERROR:  42725: function test_both(text) is not unique
        LINE 1: select test_both('aaa'::text);
                                   ^
        HINT:  Could not choose a best candidate function. You might need to add explicit type casts.
        LOCATION:  ParseFuncOrColumn, parse_func.c:568

        =# select test_both('aaa'::anyelement);
        ERROR:  42804: could not determine polymorphic type because input has type unknown
        LOCATION:  enforce_generic_type_consistency, parse_coerce.c:2177

fixed


Is it possible somehow to invoke any of these functions?

unfortunately - it's not possible - the construct 'aaa'::"polymorphic type" doesn't create a value of this type - result type is a text type, and then it doesn't help in this situation.

It is similar if you create fx(anyelement) and fx(anyarray) - and you cannot to call fx(anyelement) by fx(1::anyelement)



Other than that the functionality looks pretty solid. It may look obvious, but
I've also tested performance in different use cases for anycompatible, looks
the same as for anyelement.

Thank you for review, I am sending fixed patch

Regards

Pavel
Вложения

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
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]);

Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


pá 13. 3. 2020 v 23:42 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
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.

Unfortunately the error message " A function returning "anyrange" must have at least one "anyrange" argument." will be missing.

This prerequisite is not intuitive. Second question is if we need special rule for anyrange.

Regards

Pavel


Thoughts?

                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> pá 13. 3. 2020 v 23:42 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> 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.)

> Unfortunately the error message " A function returning "anyrange" must have
> at least one "anyrange" argument." will be missing.

Yeah, that's what I said.  But does it really add anything beyond the
proposed text "A function returning a polymorphic type must have at least
one matching polymorphic argument"?  I don't think it'd be terribly
helpful to say "A function returning anyelement must have at least one
anyelement, anyarray, anynonarray, anyenum, or anyrange argument", and
for sure such an error message would be a pain to maintain.

            regards, tom lane



Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


so 14. 3. 2020 v 14:26 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> pá 13. 3. 2020 v 23:42 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> 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.)

> Unfortunately the error message " A function returning "anyrange" must have
> at least one "anyrange" argument." will be missing.

Yeah, that's what I said.  But does it really add anything beyond the
proposed text "A function returning a polymorphic type must have at least
one matching polymorphic argument"?  I don't think it'd be terribly
helpful to say "A function returning anyelement must have at least one
anyelement, anyarray, anynonarray, anyenum, or anyrange argument", and
for sure such an error message would be a pain to maintain.

The error message in your first patch is ok for all types without anyrange. A behave of this type is more strict and +/- different than from other polymorphic types.

Pavel


                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> Yeah, that's what I said.  But does it really add anything beyond the
>> proposed text "A function returning a polymorphic type must have at least
>> one matching polymorphic argument"?  I don't think it'd be terribly
>> helpful to say "A function returning anyelement must have at least one
>> anyelement, anyarray, anynonarray, anyenum, or anyrange argument", and
>> for sure such an error message would be a pain to maintain.

> The error message in your first patch is ok for all types without anyrange.
> A behave of this type is more strict and +/- different than from other
> polymorphic types.

Well, here's a version that does it like that, but personally I find these
messages too verbose and not an improvement on what I had before.

(This is also rebased over the stuff I committed yesterday.)

            regards, tom lane

diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 0b7face..7d887ea 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;
@@ -103,6 +101,7 @@ AggregateCreate(const char *aggName,
     int            nargs_finalfn;
     Oid            procOid;
     TupleDesc    tupDesc;
+    char       *detailmsg;
     int            i;
     ObjectAddress myself,
                 referenced;
@@ -131,36 +130,33 @@ 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)
+    detailmsg = check_valid_polymorphic_signature(aggTransType,
+                                                  aggArgTypes,
+                                                  numArgs);
+    if (detailmsg)
         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_internal("%s", detailmsg)));

     /*
      * Likewise for moving-aggregate transtype, if any
      */
-    if (OidIsValid(aggmTransType) &&
-        IsPolymorphicType(aggmTransType) && !hasPolyArg)
-        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.")));
+    if (OidIsValid(aggmTransType))
+    {
+        detailmsg = check_valid_polymorphic_signature(aggmTransType,
+                                                      aggArgTypes,
+                                                      numArgs);
+        if (detailmsg)
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                     errmsg("cannot determine transition data type"),
+                     errdetail_internal("%s", detailmsg)));
+    }

     /*
      * An ordered-set aggregate that is VARIADIC must be VARIADIC ANY.  In
@@ -492,12 +488,14 @@ AggregateCreate(const char *aggName,
      * that itself violates the rule against polymorphic result with no
      * polymorphic input.)
      */
-    if (IsPolymorphicType(finaltype) && !hasPolyArg)
+    detailmsg = check_valid_polymorphic_signature(finaltype,
+                                                  aggArgTypes,
+                                                  numArgs);
+    if (detailmsg)
         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_internal("%s", detailmsg)));

     /*
      * Also, the return type can't be INTERNAL unless there's at least one
@@ -505,11 +503,14 @@ 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)
+    detailmsg = check_valid_internal_signature(finaltype,
+                                               aggArgTypes,
+                                               numArgs);
+    if (detailmsg)
         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.")));
+                 errdetail_internal("%s", detailmsg)));

     /*
      * If a moving-aggregate implementation is supplied, look up its finalfn
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 423fd79..0cac936 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;
@@ -116,6 +111,7 @@ ProcedureCreate(const char *procedureName,
     bool        is_update;
     ObjectAddress myself,
                 referenced;
+    char       *detailmsg;
     int            i;
     Oid            trfid;

@@ -178,29 +174,34 @@ 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;
-        }
-    }
+    detailmsg = check_valid_polymorphic_signature(returnType,
+                                                  parameterTypes->values,
+                                                  parameterCount);
+    if (detailmsg)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                 errmsg("cannot determine result data type"),
+                 errdetail_internal("%s", detailmsg)));

+    /*
+     * Also, do not allow return type INTERNAL unless at least one input
+     * argument is INTERNAL.
+     */
+    detailmsg = check_valid_internal_signature(returnType,
+                                               parameterTypes->values,
+                                               parameterCount);
+    if (detailmsg)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                 errmsg("unsafe use of pseudo-type \"internal\""),
+                 errdetail_internal("%s", detailmsg)));
+
+    /*
+     * Apply the same tests to any OUT arguments.
+     */
     if (allParameterTypes != PointerGetDatum(NULL))
     {
         for (i = 0; i < allParamCount; i++)
@@ -210,52 +211,26 @@ 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;
-            }
+            detailmsg = check_valid_polymorphic_signature(allParams[i],
+                                                          parameterTypes->values,
+                                                          parameterCount);
+            if (detailmsg)
+                ereport(ERROR,
+                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                         errmsg("cannot determine result data type"),
+                         errdetail_internal("%s", detailmsg)));
+            detailmsg = check_valid_internal_signature(allParams[i],
+                                                       parameterTypes->values,
+                                                       parameterCount);
+            if (detailmsg)
+                ereport(ERROR,
+                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                         errmsg("unsafe use of pseudo-type \"internal\""),
+                         errdetail_internal("%s", detailmsg)));
         }
     }

-    /*
-     * 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 4a2b463..7a9b303 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1966,6 +1966,77 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
     return rettype;
 }

+/*
+ * check_valid_polymorphic_signature()
+ *        Is a proposed function signature valid per polymorphism rules?
+ *
+ * Returns NULL if the signature is valid (either ret_type is not polymorphic,
+ * or it can be deduced from the given declared argument types).  Otherwise,
+ * returns a palloc'd, already translated errdetail string saying why not.
+ */
+char *
+check_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 NULL;    /* OK */
+        }
+        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))
+    {
+        /* Otherwise, any polymorphic type can be deduced from any other */
+        for (int i = 0; i < nargs; i++)
+        {
+            if (IsPolymorphicType(declared_arg_types[i]))
+                return NULL;    /* OK */
+        }
+        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
+        return NULL;            /* OK, ret_type is not polymorphic */
+}
+
+/*
+ * check_valid_internal_signature()
+ *        Is a proposed function signature valid per INTERNAL safety rules?
+ *
+ * Returns NULL if OK, or a suitable error message 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.)  It's overkill to return the error detail message, since there
+ * is only one possibility, but we do it like this to keep the API similar to
+ * check_valid_polymorphic_signature().
+ */
+char *
+check_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 NULL;    /* OK */
+        }
+        return pstrdup(_("A result of type internal requires at least one input of type internal."));
+    }
+    else
+        return NULL;            /* OK, ret_type is not INTERNAL */
+}
+

 /* TypeCategory()
  *        Assign a category to the specified type OID.
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index ff9219d..8686eaa 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -80,6 +80,13 @@ extern Oid    enforce_generic_type_consistency(const Oid *actual_arg_types,
                                              Oid rettype,
                                              bool allow_poly);

+extern char *check_valid_polymorphic_signature(Oid ret_type,
+                                               const Oid *declared_arg_types,
+                                               int nargs);
+extern char *check_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..1573753 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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --     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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --     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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or
anyrange.
 --    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..cdfc43e 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 result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum,
oranyrange. 
 --
 -- table functions
 --
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 348235a..a28741a 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 result of type anyrange requires at least one input of type anyrange.
 -- 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 result of type anyrange requires at least one input of type anyrange.
 create function range_add_bounds(anyrange)
   returns anyelement as 'select lower($1) + upper($1)' language sql;
 select range_add_bounds(int4range(1, 17));
@@ -1521,14 +1521,14 @@ select * from table_succeed(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 result of type anyrange requires at least one input of type anyrange.
 --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 result of type anyrange requires at least one input of type anyrange.
 --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 result of type anyrange requires at least one input of type anyrange.
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 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 7a9b303..cf199cb 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,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.
  *
@@ -1445,6 +1542,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
@@ -1482,6 +1586,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.
@@ -1525,6 +1635,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 */
@@ -1591,6 +1769,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;
 }
@@ -1643,6 +1851,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
@@ -1673,11 +1891,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 ||
@@ -1685,6 +1907,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.
@@ -1699,7 +1926,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)
@@ -1722,14 +1949,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,
@@ -1742,7 +1973,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;
@@ -1760,128 +1991,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;
+        }
     }

     /*
@@ -1962,6 +2365,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;
 }
@@ -1979,11 +2411,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++)
         {
@@ -1993,17 +2426,28 @@ 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 */
         }
         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 */
+        }
+        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 */
 }
@@ -2108,8 +2552,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 */
@@ -2120,13 +2565,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;

@@ -2135,8 +2580,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 1573753..8057c20 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]);

Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


ne 15. 3. 2020 v 17:48 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> Yeah, that's what I said.  But does it really add anything beyond the
>> proposed text "A function returning a polymorphic type must have at least
>> one matching polymorphic argument"?  I don't think it'd be terribly
>> helpful to say "A function returning anyelement must have at least one
>> anyelement, anyarray, anynonarray, anyenum, or anyrange argument", and
>> for sure such an error message would be a pain to maintain.

> The error message in your first patch is ok for all types without anyrange.
> A behave of this type is more strict and +/- different than from other
> polymorphic types.

Well, here's a version that does it like that, but personally I find these
messages too verbose and not an improvement on what I had before.

There was a problem just with anyrange type. This last version looks perfect.

Regards

Pavel


(This is also rebased over the stuff I committed yesterday.)

                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> ne 15. 3. 2020 v 17:48 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> Well, here's a version that does it like that, but personally I find these
>> messages too verbose and not an improvement on what I had before.

> There was a problem just with anyrange type. This last version looks
> perfect.

If you think that "matching polymorphic types" is too vague, I'm
not sure there's much daylight between there and spelling it out
in full as this latest patch does.  "anyrange is the only problem"
might be a tenable viewpoint today, but once this patchset goes
in there's going to be much more scope for confusion about which
arguments potentially match a polymorphic result.

            regards, tom lane



Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
I wrote:
> Pavel Stehule <pavel.stehule@gmail.com> writes:
>> There was a problem just with anyrange type. This last version looks
>> perfect.

> If you think that "matching polymorphic types" is too vague, I'm
> not sure there's much daylight between there and spelling it out
> in full as this latest patch does.  "anyrange is the only problem"
> might be a tenable viewpoint today, but once this patchset goes
> in there's going to be much more scope for confusion about which
> arguments potentially match a polymorphic result.

On further reflection it seems like that's actually a fairly convincing
argument for going with the more-verbose style.  Hence, I pushed 0001
that way.

The cfbot will be unhappy at this point, but I need to rebase the
main patch again ...

            regards, tom lane



Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
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]);

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
So ... there is a definitional question here that doesn't seem to have
been mentioned anywhere in the thread.  For the traditional polymorphic
types, we insist that at least one non-unknown input be supplied, thus
you get

regression=# create function foo(anyelement, anyelement) returns bool
regression-# language sql as 'select $1 = $2';
CREATE FUNCTION

regression=# select foo('a', 'b');
ERROR:  could not determine polymorphic type because input has type unknown

regression=# select foo('a', 'b'::text);
 foo
-----
 f
(1 row)

As this patch stands, the ANYCOMPATIBLE types also require that:

regression=# create function foo2(anycompatible, anycompatible) returns bool
language sql as 'select $1 = $2';
CREATE FUNCTION

regression=# select foo2('a', 'b');
ERROR:  could not determine polymorphic common type because input has type unknown

However, it seems to me that this is inconsistent with the definition,
namely that we resolve the common type the same way select_common_type()
does, because select_common_type() will choose TEXT when given all-unknown
inputs.  So shouldn't we choose TEXT here?

Admittedly, the fact that select_common_type() falls back to TEXT is a
bit of a wart, so maybe we don't want to propagate it here.  But if we
don't, we'll have to document the selection rule as almost but not
quite like what it says in section 10.5.  That seems confusing.

Documentation issues aside, I'm not quite sure whether this behavior
would be more or less preferable in practice than sticking with the
existing behavior.  It seems like it'd be convenient in some cases
but possibly allow mistakes to go undetected in others.

Thoughts?

            regards, tom lane



Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


st 18. 3. 2020 v 17:14 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
So ... there is a definitional question here that doesn't seem to have
been mentioned anywhere in the thread.  For the traditional polymorphic
types, we insist that at least one non-unknown input be supplied, thus
you get

regression=# create function foo(anyelement, anyelement) returns bool
regression-# language sql as 'select $1 = $2';
CREATE FUNCTION

regression=# select foo('a', 'b');
ERROR:  could not determine polymorphic type because input has type unknown

regression=# select foo('a', 'b'::text);
 foo
-----
 f
(1 row)

As this patch stands, the ANYCOMPATIBLE types also require that:

regression=# create function foo2(anycompatible, anycompatible) returns bool
language sql as 'select $1 = $2';
CREATE FUNCTION

regression=# select foo2('a', 'b');
ERROR:  could not determine polymorphic common type because input has type unknown

However, it seems to me that this is inconsistent with the definition,
namely that we resolve the common type the same way select_common_type()
does, because select_common_type() will choose TEXT when given all-unknown
inputs.  So shouldn't we choose TEXT here?
Admittedly, the fact that select_common_type() falls back to TEXT is a
bit of a wart, so maybe we don't want to propagate it here.  But if we
don't, we'll have to document the selection rule as almost but not
quite like what it says in section 10.5.  That seems confusing.

Documentation issues aside, I'm not quite sure whether this behavior
would be more or less preferable in practice than sticking with the
existing behavior.  It seems like it'd be convenient in some cases
but possibly allow mistakes to go undetected in others.

Thoughts?

It is difficult question. What I know, this issue is less than we can expect, because almost all functions are called with typed parameters (columns, variables).

the fallback to text is enticement but maybe better is consistency with other polymorphic types.

Maybe users can implement own fallback behave with next custom function

create function foo2(text, text) returns bool
language sql as 'select $1 = $2';

Regards

Pavel



                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> st 18. 3. 2020 v 17:14 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> However, it seems to me that this is inconsistent with the definition,
>> namely that we resolve the common type the same way select_common_type()
>> does, because select_common_type() will choose TEXT when given all-unknown
>> inputs.  So shouldn't we choose TEXT here?

> It is difficult question. What I know, this issue is less than we can
> expect, because almost all functions are called with typed parameters
> (columns, variables).

True, in actual production queries it's less likely that all the inputs
would be literal constants.  So this is mainly about surprise factor,
or lack of it, for handwritten test queries.

> Maybe users can implement own fallback behave with next custom function

> create function foo2(text, text) returns bool
> language sql as 'select $1 = $2';

No, because if you've got that alongside foo2(anycompatible,
anycompatible) then your queries will fail due to both functions
matching anything that's promotable to text.

            regards, tom lane



Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


st 18. 3. 2020 v 17:54 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> st 18. 3. 2020 v 17:14 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> However, it seems to me that this is inconsistent with the definition,
>> namely that we resolve the common type the same way select_common_type()
>> does, because select_common_type() will choose TEXT when given all-unknown
>> inputs.  So shouldn't we choose TEXT here?

> It is difficult question. What I know, this issue is less than we can
> expect, because almost all functions are called with typed parameters
> (columns, variables).

True, in actual production queries it's less likely that all the inputs
would be literal constants.  So this is mainly about surprise factor,
or lack of it, for handwritten test queries.

> Maybe users can implement own fallback behave with next custom function

> create function foo2(text, text) returns bool
> language sql as 'select $1 = $2';

No, because if you've got that alongside foo2(anycompatible,
anycompatible) then your queries will fail due to both functions
matching anything that's promotable to text.

It is working for anyelement

postgres=# create or replace function fx(anyelement, anyelement)
postgres-# returns bool as $$ select $1=$2 $$ language sql;
CREATE FUNCTION
postgres=# create or replace function fx(text, text)
returns bool as $$ select $1=$2 $$ language sql;
CREATE FUNCTION
postgres=# select fx(1,2);
┌────┐
│ fx │
╞════╡
│ f  │
└────┘
(1 row)

postgres=# select fx('ahoj','nazdar');
┌────┐
│ fx │
╞════╡
│ f  │
└────┘
(1 row)


                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> st 18. 3. 2020 v 17:54 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> No, because if you've got that alongside foo2(anycompatible,
>> anycompatible) then your queries will fail due to both functions
>> matching anything that's promotable to text.

> It is working for anyelement

[ pokes at that... ]  Hm, looks like you're getting away with that
because of the preference for functions taking preferred types.
Seems pretty shaky to me though --- you can probably invent
cases that will throw 'ambiguous function' if you try a bit harder.
In any case, I don't think users will understand why they have to
write two versions of the same function.

            regards, tom lane



Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


st 18. 3. 2020 v 18:09 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> st 18. 3. 2020 v 17:54 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> No, because if you've got that alongside foo2(anycompatible,
>> anycompatible) then your queries will fail due to both functions
>> matching anything that's promotable to text.

> It is working for anyelement

[ pokes at that... ]  Hm, looks like you're getting away with that
because of the preference for functions taking preferred types.
Seems pretty shaky to me though --- you can probably invent
cases that will throw 'ambiguous function' if you try a bit harder.
In any case, I don't think users will understand why they have to
write two versions of the same function.

yes, it is not for usual user.

Pavel

                        regards, tom lane

Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
Here's a pretty-nearly-final version of the patch.

In 0001 below, I've left it throwing an error for the case of all
ANYCOMPATIBLE inputs being unknown, but the documentation fails to
acknowledge that.  0002 below is a delta patch that switches to the
other approach of resolving as TEXT.  I'm pretty well convinced that
0002 is what we should do, so I have not bothered to write a doc
change that would explain 0001's behavior on this point.

            regards, tom lane

diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 157fe4e..aa634ea 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4789,6 +4789,22 @@ 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>anycompatiblenonarray</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-pseudo">
+    <primary>anycompatiblerange</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-pseudo">
     <primary>void</primary>
    </indexterm>

@@ -4894,6 +4910,35 @@ SELECT * FROM pg_attribute
        </row>

        <row>
+        <entry><type>anycompatible</type></entry>
+        <entry>Indicates that a function accepts any data type,
+        with automatic promotion of multiple arguments to a common data 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,
+        with automatic promotion of multiple arguments to a common data type
+        (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,
+        with automatic promotion of multiple arguments to a common 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,
+        with automatic promotion of multiple arguments to a common data type
+        (see <xref linkend="extend-types-polymorphic"/> and
+        <xref linkend="rangetypes"/>).</entry>
+       </row>
+
+       <row>
         <entry><type>cstring</type></entry>
         <entry>Indicates that a function accepts or returns a null-terminated C string.</entry>
        </row>
@@ -4960,7 +5005,7 @@ SELECT * FROM pg_attribute

    <para>
     Functions coded in C (whether built-in or dynamically loaded) can be
-    declared to accept or return any of these pseudo data types.  It is up to
+    declared to accept or return any of these pseudo-types.  It is up to
     the function author to ensure that the function will behave safely
     when a pseudo-type is used as an argument type.
    </para>
@@ -4971,10 +5016,9 @@ SELECT * FROM pg_attribute
     languages forbid use of a pseudo-type as an argument type, and allow
     only <type>void</type> and <type>record</type> as a result type (plus
     <type>trigger</type> or <type>event_trigger</type> when the function is used
-    as a trigger or event trigger).  Some also
-    support polymorphic functions using the types <type>anyelement</type>,
-    <type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>, and
-    <type>anyrange</type>.
+    as a trigger or event trigger).  Some also support polymorphic functions
+    using the polymorphic pseudo-types, which are shown above and discussed
+    in detail in <xref linkend="extend-types-polymorphic"/>.
    </para>

    <para>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 97706da..930aeb7 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,21 +229,114 @@
    </indexterm>

     <para>
-     Five pseudo-types of special interest are <type>anyelement</type>,
-     <type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
-     and <type>anyrange</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
-     operate on many different data types, with the specific data type(s)
-     being determined by the data types actually passed to it in a particular
-     call.
+     Some pseudo-types of special interest are the <firstterm>polymorphic
+     types</firstterm>, which are used to declare <firstterm>polymorphic
+     functions</firstterm>.  This powerful feature allows a single function
+     definition to operate on many different data types, with the specific
+     data type(s) being determined by the data types actually passed to it
+     in a particular call.  The polymorphic types are shown in
+     <xref linkend="extend-types-polymorphic-table"/>.  Some examples of
+     their use appear in <xref linkend="xfunc-sql-polymorphic-functions"/>.
     </para>

+    <table id="extend-types-polymorphic-table">
+     <title>Polymorphic Types</title>
+     <tgroup cols="3">
+      <thead>
+       <row>
+        <entry>Name</entry>
+        <entry>Family</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+
+      <tbody>
+       <row>
+        <entry><type>anyelement</type></entry>
+        <entry>Simple</entry>
+        <entry>Indicates that a function accepts any data type</entry>
+       </row>
+
+       <row>
+        <entry><type>anyarray</type></entry>
+        <entry>Simple</entry>
+        <entry>Indicates that a function accepts any array data type</entry>
+       </row>
+
+       <row>
+        <entry><type>anynonarray</type></entry>
+        <entry>Simple</entry>
+        <entry>Indicates that a function accepts any non-array data type</entry>
+       </row>
+
+       <row>
+        <entry><type>anyenum</type></entry>
+        <entry>Simple</entry>
+        <entry>Indicates that a function accepts any enum data type
+        (see <xref linkend="datatype-enum"/>)
+        </entry>
+       </row>
+
+       <row>
+        <entry><type>anyrange</type></entry>
+        <entry>Simple</entry>
+        <entry>Indicates that a function accepts any range data type
+        (see <xref linkend="rangetypes"/>)
+        </entry>
+       </row>
+
+       <row>
+        <entry><type>anycompatible</type></entry>
+        <entry>Common</entry>
+        <entry>Indicates that a function accepts any data type,
+        with automatic promotion of multiple arguments to a common data type
+        </entry>
+       </row>
+
+       <row>
+        <entry><type>anycompatiblearray</type></entry>
+        <entry>Common</entry>
+        <entry>Indicates that a function accepts any array data type,
+        with automatic promotion of multiple arguments to a common data type
+        </entry>
+       </row>
+
+       <row>
+        <entry><type>anycompatiblenonarray</type></entry>
+        <entry>Common</entry>
+        <entry>Indicates that a function accepts any non-array data type,
+        with automatic promotion of multiple arguments to a common data type
+        </entry>
+       </row>
+
+       <row>
+        <entry><type>anycompatiblerange</type></entry>
+        <entry>Common</entry>
+        <entry>Indicates that a function accepts any range data type,
+        with automatic promotion of multiple arguments to a common data type
+        </entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+
     <para>
      Polymorphic arguments and results are tied to each other and are resolved
-     to a specific data type when a query calling a polymorphic function is
-     parsed.  Each position (either argument or return value) declared as
+     to specific data types when a query calling a polymorphic function is
+     parsed.  When there is more than one polymorphic argument, the actual
+     data types of the input values must match up as described below.  If the
+     function's result type is polymorphic, or it has output parameters of
+     polymorphic types, the types of those results are deduced from the
+     actual types of the polymorphic inputs as described below.
+    </para>
+
+    <para>
+     For the <quote>simple</quote> family of polymorphic types, the
+     matching and deduction rules work like this:
+    </para>
+
+    <para>
+     Each position (either argument or return value) declared as
      <type>anyelement</type> is allowed to have any specific actual
      data type, but in any given call they must all be the
      <emphasis>same</emphasis> actual type. Each
@@ -280,7 +373,8 @@
     <para>
      When the return value of a function is declared as a polymorphic type,
      there must be at least one argument position that is also polymorphic,
-     and the actual data type supplied as the argument determines the actual
+     and the actual data type(s) supplied for the polymorphic arguments
+     determine the actual
      result type for that call.  For example, if there were not already
      an array subscripting mechanism, one could define a function that
      implements subscripting as <literal>subscript(anyarray, integer)
@@ -294,8 +388,9 @@
     <para>
      In most cases, the parser can infer the actual data type for a
      polymorphic result type from arguments that are of a different
-     polymorphic type; for example <type>anyarray</type> can be deduced
-     from <type>anyelement</type> or vice versa.  The exception is that a
+     polymorphic type in the same family; for example <type>anyarray</type>
+     can be deduced from <type>anyelement</type> or vice versa.
+     An exception is that a
      polymorphic result of type <type>anyrange</type> requires an argument
      of type <type>anyrange</type>; it cannot be deduced
      from <type>anyarray</type> or <type>anyelement</type> arguments.  This
@@ -312,13 +407,69 @@
     </para>

     <para>
+     For the <quote>common</quote> family of polymorphic types, the
+     matching and deduction rules work approximately the same as for
+     the <quote>simple</quote> family, with one major difference: the
+     actual types of the arguments need not be identical, so long as they
+     can be implicitly cast to a single common type.  The common type is
+     selected following the same rules as for <literal>UNION</literal> and
+     related constructs (see <xref linkend="typeconv-union-case"/>).
+     Selection of the common type considers the actual types
+     of <type>anycompatible</type> and <type>anycompatiblenonarray</type>
+     inputs, the array element types of <type>anycompatiblearray</type>
+     inputs, and the range subtypes of <type>anycompatiblerange</type>
+     inputs.  If <type>anycompatiblenonarray</type> is present then the
+     common type is required to be a non-array type.  Once a common type is
+     identified, arguments in <type>anycompatible</type>
+     and <type>anycompatiblenonarray</type> positions are automatically
+     cast to that type, and arguments in <type>anycompatiblearray</type>
+     positions are automatically cast to the array type for that type.
+    </para>
+
+    <para>
+     Since there is no way to select a range type knowing only its subtype,
+     use of <type>anycompatiblerange</type> requires that all arguments
+     declared with that type have the same actual range type, and that that
+     type's subtype agree with the selected common type, so that no casting
+     of the range values is required.  As with <type>anyrange</type>, use
+     of <type>anycompatiblerange</type> as a function result type requires
+     that there be an <type>anycompatiblerange</type> argument.
+    </para>
+
+    <para>
+     Notice that there is no <type>anycompatibleenum</type> type.  Such a
+     type would not be very useful, since there normally are not any
+     implicit casts to enum types, meaning that there would be no way to
+     resolve a common type for dissimilar enum inputs.
+    </para>
+
+    <para>
+     The <quote>simple</quote> and <quote>common</quote> polymorphic
+     families represent two independent sets of type variables.  Consider
+     for example
+<programlisting>
+CREATE FUNCTION myfunc(a anyelement, b anyelement,
+                       c anycompatible, d anycompatible)
+RETURNS anycompatible AS ...
+</programlisting>
+     In an actual call of this function, the first two inputs must have
+     exactly the same type.  The last two inputs must be promotable to a
+     common type, but this type need not have anything to do with the type
+     of the first two inputs.  The result will have the common type of the
+     last two inputs.
+    </para>
+
+    <para>
      A variadic function (one taking a variable number of arguments, as in
      <xref linkend="xfunc-sql-variadic-functions"/>) can be
      polymorphic: this is accomplished by declaring its last parameter as
-     <literal>VARIADIC</literal> <type>anyarray</type>.  For purposes of argument
+     <literal>VARIADIC</literal> <type>anyarray</type> or
+     <literal>VARIADIC</literal> <type>anycompatiblearray</type>.
+     For purposes of argument
      matching and determining the actual result type, such a function behaves
      the same as if you had written the appropriate number of
-     <type>anynonarray</type> parameters.
+     <type>anynonarray</type> or <type>anycompatiblenonarray</type>
+     parameters.
     </para>
    </sect2>
   </sect1>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 2987a55..cc99aca 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -138,13 +138,11 @@
     </para>

     <para>
-     <application>PL/pgSQL</application> functions can also be declared to accept
-     and return the polymorphic types
-     <type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
-     <type>anyenum</type>, and <type>anyrange</type>.  The actual
-     data types handled by a polymorphic function can vary from call to
-     call, as discussed in <xref linkend="extend-types-polymorphic"/>.
-     An example is shown in <xref linkend="plpgsql-declaration-parameters"/>.
+     <application>PL/pgSQL</application> functions can also be declared to
+     accept and return the polymorphic types described in
+     <xref linkend="extend-types-polymorphic"/>, thus allowing the actual data
+     types handled by the function to vary from call to call.
+     Examples appear in <xref linkend="plpgsql-declaration-parameters"/>.
     </para>

     <para>
@@ -519,13 +517,11 @@ $$ LANGUAGE plpgsql;
      </para>

      <para>
-      When the return type of a <application>PL/pgSQL</application>
-      function is declared as a polymorphic type (<type>anyelement</type>,
-      <type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
-      or <type>anyrange</type>), a special parameter <literal>$0</literal>
-      is created.  Its data type is the actual return type of the function,
-      as deduced from the actual input types (see <xref
-      linkend="extend-types-polymorphic"/>).
+      When the return type of a <application>PL/pgSQL</application> function
+      is declared as a polymorphic type (see
+      <xref linkend="extend-types-polymorphic"/>), a special
+      parameter <literal>$0</literal> is created.  Its data type is the actual
+      return type of the function, as deduced from the actual input types.
       This allows the function to access its actual return type
       as shown in <xref linkend="plpgsql-declaration-type"/>.
       <literal>$0</literal> is initialized to null and can be modified by
@@ -563,6 +559,32 @@ END;
 $$ LANGUAGE plpgsql;
 </programlisting>
      </para>
+
+     <para>
+      In practice it might be more useful to declare a polymorphic function
+      using the <type>anycompatible</type> family of types, so that automatic
+      promotion of the input arguments to a common type will occur.
+      For example:
+
+<programlisting>
+CREATE FUNCTION add_three_values(v1 anycompatible, v2 anycompatible, v3 anycompatible)
+RETURNS anycompatible AS $$
+BEGIN
+    RETURN v1 + v2 + v3;
+END;
+$$ LANGUAGE plpgsql;
+</programlisting>
+
+      With this example, a call such as
+
+<programlisting>
+SELECT add_three_values(1, 2, 4.7);
+</programlisting>
+
+      will work, automatically promoting the integer inputs to numeric.
+      The function using <type>anyelement</type> would require you to
+      cast the three inputs to the same type manually.
+     </para>
     </sect2>

   <sect2 id="plpgsql-declaration-alias">
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 6350f92..ff51660 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1226,16 +1226,13 @@ $$ LANGUAGE SQL;
     </para>
    </sect2>

-   <sect2>
+   <sect2 id="xfunc-sql-polymorphic-functions">
     <title>Polymorphic <acronym>SQL</acronym> Functions</title>

     <para>
      <acronym>SQL</acronym> functions can be declared to accept and
-     return the polymorphic types <type>anyelement</type>,
-     <type>anyarray</type>, <type>anynonarray</type>,
-     <type>anyenum</type>, and <type>anyrange</type>.  See <xref
-     linkend="extend-types-polymorphic"/> for a more detailed
-     explanation of polymorphic functions. Here is a polymorphic
+     return the polymorphic types described in <xref
+     linkend="extend-types-polymorphic"/>.  Here is a polymorphic
      function <function>make_array</function> that builds up an array
      from two arbitrary data type elements:
 <screen>
@@ -1260,9 +1257,33 @@ SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;
      type.
      Without the typecast, you will get errors like this:
 <screen>
-<computeroutput>
 ERROR:  could not determine polymorphic type because input has type unknown
-</computeroutput>
+</screen>
+    </para>
+
+    <para>
+     With <function>make_array</function> declared as above, you must
+     provide two arguments that are of exactly the same data type; the
+     system will not attempt to resolve any type differences.  Thus for
+     example this does not work:
+<screen>
+SELECT make_array(1, 2.5) AS numericarray;
+ERROR:  function make_array(integer, numeric) does not exist
+</screen>
+     An alternative approach is to use the <quote>common</quote> family of
+     polymorphic types, which allows the system to try to identify a
+     suitable common type:
+<screen>
+CREATE FUNCTION make_array2(anycompatible, anycompatible)
+RETURNS anycompatiblearray AS $$
+    SELECT ARRAY[$1, $2];
+$$ LANGUAGE SQL;
+
+SELECT make_array2(1, 2.5) AS numericarray;
+ numericarray
+--------------
+ {1,2.5}
+(1 row)
 </screen>
     </para>

@@ -1284,7 +1305,7 @@ CREATE FUNCTION invalid_func() RETURNS anyelement AS $$
     SELECT 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 result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum,
oranyrange. 
 </screen>
     </para>

@@ -3157,11 +3178,9 @@ CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,

     <para>
      C-language functions can be declared to accept and
-     return the polymorphic types
-     <type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
-     <type>anyenum</type>, and <type>anyrange</type>.
-     See <xref linkend="extend-types-polymorphic"/> for a more detailed explanation
-     of polymorphic functions. When function arguments or return types
+     return the polymorphic types described in <xref
+     linkend="extend-types-polymorphic"/>.
+     When a function's arguments or return types
      are defined as polymorphic types, the function author cannot know
      in advance what data type it will be called with, or
      need to return. There are two routines provided in <filename>fmgr.h</filename>
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..1e53f7c 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,103 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
 }

 /*
+ * select_common_type_from_oids()
+ *        Determine the common supertype of an array 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.
+ *
+ * Note: neither caller will pass any UNKNOWNOID entries, so the tests
+ * for that in this function are dead code.  However, they don't cost much,
+ * and it seems better to keep this logic as close to select_common_type()
+ * as possible.
+ */
+static Oid
+select_common_type_from_oids(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 +1543,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 or subtypes are
+ *      included while making 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 +1582,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 +1632,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 subtype 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 +1756,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_oids(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 +1807,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 +1833,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/subtypes, 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 equal 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 +1863,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 +1877,29 @@ 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;
+    Oid            anycompatible_range_typelem = 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,18 +1970,87 @@ 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)
+        {
+            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 subtype 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;
     }

+    /* Check matching of family-1 polymorphic arguments, if any */
     if (n_poly_args)
     {
         /* Get the element type based on the array type, if we have one */
@@ -1766,13 +2062,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 +2185,105 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
         }
     }

+    /* Check matching of family-2 polymorphic arguments, if any */
+    if (have_poly_anycompatible)
+    {
+        if (n_anycompatible_args > 0)
+        {
+            anycompatible_typeid =
+                select_common_type_from_oids(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))));
+            }
+
+            if (have_anycompatible_range)
+            {
+                /* we can't infer a range type from the others */
+                if (!OidIsValid(anycompatible_range_typeid))
+                    ereport(ERROR,
+                            (errcode(ERRCODE_DATATYPE_MISMATCH),
+                             errmsg("could not determine polymorphic type %s because input has type %s",
+                                    "anycompatiblerange", "unknown")));
+
+                /*
+                 * the anycompatible type must exactly match the range element
+                 * type
+                 */
+                if (anycompatible_range_typelem != anycompatible_typeid)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_DATATYPE_MISMATCH),
+                             errmsg("anycompatiblerange type %s does not match anycompatible type %s",
+                                    format_type_be(anycompatible_range_typeid),
+                                    format_type_be(anycompatible_typeid))));
+            }
+
+            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?  But not TEXTRANGE.
+                 */
+                ereport(ERROR,
+                        (errcode(ERRCODE_DATATYPE_MISMATCH),
+                         errmsg("could not determine polymorphic type because input has type %s",
+                                "unknown")));
+            }
+        }
+
+        /* replace polymorphic types by selected types */
+        for (int j = 0; j < nargs; j++)
+        {
+            Oid            decl_type = declared_arg_types[j];
+
+            if (decl_type == ANYCOMPATIBLEOID ||
+                decl_type == ANYCOMPATIBLENONARRAYOID)
+                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)
     {
@@ -1923,10 +2316,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
             {
                 if (!OidIsValid(range_typeid))
                 {
+                    /* we can't infer a range type from the others */
                     ereport(ERROR,
-                            (errcode(ERRCODE_UNDEFINED_OBJECT),
-                             errmsg("could not find range type for data type %s",
-                                    format_type_be(elem_typeid))));
+                            (errcode(ERRCODE_DATATYPE_MISMATCH),
+                             errmsg("could not determine polymorphic type %s because input has type %s",
+                                    "anyrange", "unknown")));
                 }
                 declared_arg_types[j] = range_typeid;
             }
@@ -1957,16 +2351,49 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
     /* if we return ANYRANGE use the appropriate argument type */
     if (rettype == ANYRANGEOID)
     {
+        /* this error is unreachable if the function signature is valid: */
         if (!OidIsValid(range_typeid))
-        {
             ereport(ERROR,
-                    (errcode(ERRCODE_UNDEFINED_OBJECT),
-                     errmsg("could not find range type for data type %s",
-                            format_type_be(elem_typeid))));
-        }
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg("could not determine polymorphic type %s because input has type %s",
+                            "anyrange", "unknown")));
         return range_typeid;
     }

+    /* if we return ANYCOMPATIBLE use the appropriate type */
+    if (rettype == ANYCOMPATIBLEOID ||
+        rettype == ANYCOMPATIBLENONARRAYOID)
+    {
+        /* this error is unreachable if the function signature is valid: */
+        if (!OidIsValid(anycompatible_typeid))
+            ereport(ERROR,
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg_internal("could not identify anycompatible type")));
+        return anycompatible_typeid;
+    }
+
+    /* if we return ANYCOMPATIBLEARRAY use the appropriate type */
+    if (rettype == ANYCOMPATIBLEARRAYOID)
+    {
+        /* this error is unreachable if the function signature is valid: */
+        if (!OidIsValid(anycompatible_array_typeid))
+            ereport(ERROR,
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg_internal("could not identify anycompatiblearray type")));
+        return anycompatible_array_typeid;
+    }
+
+    /* if we return ANYCOMPATIBLERANGE use the appropriate argument type */
+    if (rettype == ANYCOMPATIBLERANGEOID)
+    {
+        /* this error is unreachable if the function signature is valid: */
+        if (!OidIsValid(anycompatible_range_typeid))
+            ereport(ERROR,
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg_internal("could not identify anycompatiblerange type")));
+        return anycompatible_range_typeid;
+    }
+
     /* we don't return a generic type; send back the original return type */
     return rettype;
 }
@@ -1984,11 +2411,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 +2426,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 +2554,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 +2567,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 +2582,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/aggregates.out b/src/test/regress/expected/aggregates.out
index f457b5b..47b5bf7 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -1950,6 +1950,30 @@ select least_agg(variadic array[q1,q2]) from int8_tbl;
  -4567890123456789
 (1 row)

+select cleast_agg(q1,q2) from int8_tbl;
+    cleast_agg
+-------------------
+ -4567890123456789
+(1 row)
+
+select cleast_agg(4.5,f1) from int4_tbl;
+ cleast_agg
+-------------
+ -2147483647
+(1 row)
+
+select cleast_agg(variadic array[4.5,f1]) from int4_tbl;
+ cleast_agg
+-------------
+ -2147483647
+(1 row)
+
+select pg_typeof(cleast_agg(variadic array[4.5,f1])) from int4_tbl;
+ pg_typeof
+-----------
+ numeric
+(1 row)
+
 -- test aggregates with common transition functions share the same states
 begin work;
 create type avg_state as (total bigint, count bigint);
diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out
index a2eb999..dcf6909 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -59,13 +59,39 @@ create aggregate aggfns(integer,integer,text) (
    sfunc = aggfns_trans, stype = aggtype[], sspace = 10000,
    initcond = '{}'
 );
--- variadic aggregate
+-- check error cases that would require run-time type coercion
+create function least_accum(int8, int8) returns int8 language sql as
+  'select least($1, $2)';
+create aggregate least_agg(int4) (
+  stype = int8, sfunc = least_accum
+);  -- fails
+ERROR:  function least_accum(bigint, bigint) requires run-time type coercion
+drop function least_accum(int8, int8);
+create function least_accum(anycompatible, anycompatible)
+returns anycompatible language sql as
+  'select least($1, $2)';
+create aggregate least_agg(int4) (
+  stype = int8, sfunc = least_accum
+);  -- fails
+ERROR:  function least_accum(bigint, bigint) requires run-time type coercion
+create aggregate least_agg(int8) (
+  stype = int8, sfunc = least_accum
+);
+drop function least_accum(anycompatible, anycompatible) cascade;
+NOTICE:  drop cascades to function least_agg(bigint)
+-- variadic aggregates
 create function least_accum(anyelement, variadic anyarray)
 returns anyelement language sql as
   'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
 create aggregate least_agg(variadic items anyarray) (
   stype = anyelement, sfunc = least_accum
 );
+create function cleast_accum(anycompatible, variadic anycompatiblearray)
+returns anycompatible language sql as
+  'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
+create aggregate cleast_agg(variadic items anycompatiblearray) (
+  stype = anycompatible, sfunc = cleast_accum
+);
 -- test ordered-set aggs using built-in support functions
 create aggregate my_percentile_disc(float8 ORDER BY anyelement) (
   stype = internal,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 40468e8..3c0b21d 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -329,7 +329,7 @@ SELECT p1.oid, p1.proname
 FROM pg_proc as p1
 WHERE p1.prorettype IN
     ('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
-     'anyenum'::regtype, 'anyrange'::regtype)
+     'anyenum'::regtype)
   AND NOT
     ('anyelement'::regtype = ANY (p1.proargtypes) OR
      'anyarray'::regtype = ANY (p1.proargtypes) OR
@@ -337,22 +337,64 @@ WHERE p1.prorettype IN
      'anyenum'::regtype = ANY (p1.proargtypes) OR
      'anyrange'::regtype = ANY (p1.proargtypes))
 ORDER BY 2;
- oid  |     proname
-------+------------------
+ oid  |    proname
+------+----------------
  2296 | anyarray_in
  2502 | anyarray_recv
  2312 | anyelement_in
  3504 | anyenum_in
  2777 | anynonarray_in
- 3832 | anyrange_in
   750 | array_in
  2400 | array_recv
  3506 | enum_in
  3532 | enum_recv
+(9 rows)
+
+-- anyrange is tighter than the rest, can only resolve from anyrange input
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype = 'anyrange'::regtype
+  AND NOT
+     'anyrange'::regtype = ANY (p1.proargtypes)
+ORDER BY 2;
+ oid  |     proname
+------+------------------
+ 3832 | anyrange_in
  3876 | range_gist_union
  3834 | range_in
  3836 | range_recv
-(13 rows)
+(4 rows)
+
+-- similarly for the anycompatible family
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype IN
+    ('anycompatible'::regtype, 'anycompatiblearray'::regtype,
+     'anycompatiblenonarray'::regtype)
+  AND NOT
+    ('anycompatible'::regtype = ANY (p1.proargtypes) OR
+     'anycompatiblearray'::regtype = ANY (p1.proargtypes) OR
+     'anycompatiblenonarray'::regtype = ANY (p1.proargtypes) OR
+     'anycompatiblerange'::regtype = ANY (p1.proargtypes))
+ORDER BY 2;
+ oid  |         proname
+------+--------------------------
+ 9559 | anycompatible_in
+ 9561 | anycompatiblearray_in
+ 9563 | anycompatiblearray_recv
+ 9565 | anycompatiblenonarray_in
+(4 rows)
+
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype = 'anycompatiblerange'::regtype
+  AND NOT
+     'anycompatiblerange'::regtype = ANY (p1.proargtypes)
+ORDER BY 2;
+ oid  |        proname
+------+-----------------------
+ 9567 | anycompatiblerange_in
+(1 row)

 -- Look for functions that accept cstring and are neither datatype input
 -- functions nor encoding conversion functions.  It's almost never a good
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index dfc10e3..4599fc1 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1823,6 +1823,51 @@ select f1(int4range(42, 49)) as int, f1(float8range(4.5, 7.8)) as num;
 (1 row)

 drop function f1(x anyrange);
+create function f1(x anycompatible, y anycompatible) returns anycompatiblearray as $$
+begin
+  return array[x, y];
+end$$ language plpgsql;
+select f1(2, 4) as int, f1(2, 4.5) as num;
+  int  |   num
+-------+---------
+ {2,4} | {2,4.5}
+(1 row)
+
+drop function f1(x anycompatible, y anycompatible);
+create function f1(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+begin
+  return array[lower(x), upper(x), y, z];
+end$$ language plpgsql;
+select f1(int4range(42, 49), 11, 2::smallint) as int, f1(float8range(4.5, 7.8), 7.8, 11::real) as num;
+     int      |       num
+--------------+------------------
+ {42,49,11,2} | {4.5,7.8,7.8,11}
+(1 row)
+
+select f1(int4range(42, 49), 11, 4.5) as fail;  -- range type doesn't fit
+ERROR:  function f1(int4range, integer, numeric) does not exist
+LINE 1: select f1(int4range(42, 49), 11, 4.5) as fail;
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function f1(x anycompatiblerange, y anycompatible, z anycompatible);
+-- fail, can't infer type:
+create function f1(x anycompatible) returns anycompatiblerange as $$
+begin
+  return array[x + 1, x + 2];
+end$$ language plpgsql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
+begin
+  return x;
+end$$ language plpgsql;
+select f1(int4range(42, 49), array[11]) as int, f1(float8range(4.5, 7.8), array[7]) as num;
+   int   |    num
+---------+-----------
+ [42,49) | [4.5,7.8)
+(1 row)
+
+drop function f1(x anycompatiblerange, y anycompatiblearray);
 --
 -- Test handling of OUT parameters, including polymorphic cases.
 -- Note that RETURN is optional with OUT params; we try both ways.
@@ -1940,6 +1985,25 @@ select * from duplic('foo'::text);
 (1 row)

 drop function duplic(anyelement);
+create function duplic(in i anycompatiblerange, out j anycompatible, out k anycompatiblearray) as $$
+begin
+  j := lower(i);
+  k := array[lower(i),upper(i)];
+  return;
+end$$ language plpgsql;
+select * from duplic(int4range(42,49));
+ j  |    k
+----+---------
+ 42 | {42,49}
+(1 row)
+
+select * from duplic(textrange('aaa', 'bbb'));
+  j  |     k
+-----+-----------
+ aaa | {aaa,bbb}
+(1 row)
+
+drop function duplic(anycompatiblerange);
 --
 -- test PERFORM
 --
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 331562d..bd854e3 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -72,6 +72,47 @@ select polyf(int4range(42, 49)) as int, polyf(float8range(4.5, 7.8)) as num;
 (1 row)

 drop function polyf(x anyrange);
+create function polyf(x anycompatible, y anycompatible) returns anycompatiblearray as $$
+  select array[x, y]
+$$ language sql;
+select polyf(2, 4) as int, polyf(2, 4.5) as num;
+  int  |   num
+-------+---------
+ {2,4} | {2,4.5}
+(1 row)
+
+drop function polyf(x anycompatible, y anycompatible);
+create function polyf(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+  select array[lower(x), upper(x), y, z]
+$$ language sql;
+select polyf(int4range(42, 49), 11, 2::smallint) as int, polyf(float8range(4.5, 7.8), 7.8, 11::real) as num;
+     int      |       num
+--------------+------------------
+ {42,49,11,2} | {4.5,7.8,7.8,11}
+(1 row)
+
+select polyf(int4range(42, 49), 11, 4.5) as fail;  -- range type doesn't fit
+ERROR:  function polyf(int4range, integer, numeric) does not exist
+LINE 1: select polyf(int4range(42, 49), 11, 4.5) as fail;
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblerange as $$
+  select array[x + 1, x + 2]
+$$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+create function polyf(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
+  select x
+$$ language sql;
+select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8), array[7]) as num;
+   int   |    num
+---------+-----------
+ [42,49) | [4.5,7.8)
+(1 row)
+
+drop function polyf(x anycompatiblerange, y anycompatiblearray);
 --
 -- Polymorphic aggregate tests
 --
@@ -1621,3 +1662,212 @@ View definition:

 drop view dfview;
 drop function dfunc(anyelement, anyelement, bool);
+--
+-- Tests for ANYCOMPATIBLE polymorphism family
+--
+create function anyctest(anycompatible, anycompatible)
+returns anycompatible as $$
+  select greatest($1, $2)
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, 12) x;
+ x  | pg_typeof
+----+-----------
+ 12 | integer
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+  x   | pg_typeof
+------+-----------
+ 12.3 | numeric
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, point(1,2)) x;  -- fail
+ERROR:  function anyctest(integer, point) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, point(1,2)) x;
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest('11', '12.3') x;  -- fail, should it work?
+ERROR:  could not determine polymorphic type because input has type unknown
+select x, pg_typeof(x) from anyctest('11', '12.3'::text) x;
+  x   | pg_typeof
+------+-----------
+ 12.3 | text
+(1 row)
+
+drop function anyctest(anycompatible, anycompatible);
+create function anyctest(anycompatible, anycompatible)
+returns anycompatiblearray as $$
+  select array[$1, $2]
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, 12) x;
+    x    | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+     x     | pg_typeof
+-----------+-----------
+ {11,12.3} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, array[1,2]) x;  -- fail
+ERROR:  function anyctest(integer, integer[]) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, array[1,2]) x;
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatible, anycompatible);
+create function anyctest(anycompatible, anycompatiblearray)
+returns anycompatiblearray as $$
+  select array[$1] || $2
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, array[12]) x;
+    x    | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, array[12.3]) x;
+     x     | pg_typeof
+-----------+-----------
+ {11,12.3} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(12.3, array[13]) x;
+     x     | pg_typeof
+-----------+-----------
+ {12.3,13} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(12.3, '{13,14.4}') x;
+       x        | pg_typeof
+----------------+-----------
+ {12.3,13,14.4} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, array[point(1,2)]) x;  -- fail
+ERROR:  function anyctest(integer, point[]) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, array[point(1,2)]) ...
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11, 12) x;  -- fail
+ERROR:  function anyctest(integer, integer) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x;
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatible, anycompatiblearray);
+create function anyctest(anycompatible, anycompatiblerange)
+returns anycompatiblerange as $$
+  select $2
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, int4range(4,7)) x;
+   x   | pg_typeof
+-------+-----------
+ [4,7) | int4range
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, numrange(4,7)) x;
+   x   | pg_typeof
+-------+-----------
+ [4,7) | numrange
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12) x;  -- fail
+ERROR:  function anyctest(integer, integer) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x;
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, int4range(4,7)) x;  -- fail
+ERROR:  function anyctest(numeric, int4range) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11.2, int4range(4,7)) x...
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, '[4,7)') x;  -- fail
+ERROR:  could not determine polymorphic type anycompatiblerange because input has type unknown
+drop function anyctest(anycompatible, anycompatiblerange);
+create function anyctest(anycompatiblerange, anycompatiblerange)
+returns anycompatible as $$
+  select lower($1) + upper($2)
+$$ language sql;
+select x, pg_typeof(x) from anyctest(int4range(11,12), int4range(4,7)) x;
+ x  | pg_typeof
+----+-----------
+ 18 | integer
+(1 row)
+
+select x, pg_typeof(x) from anyctest(int4range(11,12), numrange(4,7)) x; -- fail
+ERROR:  function anyctest(int4range, numrange) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(int4range(11,12), numra...
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatiblerange, anycompatiblerange);
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblerange as $$
+  select $1
+$$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+  select array[$1, $2]
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, 12) x;
+    x    | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+     x     | pg_typeof
+-----------+-----------
+ {11,12.3} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(array[11], array[1,2]) x;  -- fail
+ERROR:  function anyctest(integer[], integer[]) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(array[11], array[1,2]) ...
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatiblenonarray, anycompatiblenonarray);
+create function anyctest(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+begin
+  return $1;
+end;
+$$ language plpgsql;
+select x, pg_typeof(x) from anyctest(11, 12) x;
+    x    | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12.2) x;
+     x     | pg_typeof
+-----------+-----------
+ {11,12.2} | numeric[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, '12') x;
+    x    | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, '12.2') x;  -- fail
+ERROR:  invalid input syntax for type integer: "12.2"
+LINE 1: select x, pg_typeof(x) from anyctest(11, '12.2') x;
+                                                 ^
+select x, pg_typeof(x) from anyctest(variadic array[11, 12]) x;
+    x    | pg_typeof
+---------+-----------
+ {11,12} | integer[]
+(1 row)
+
+select x, pg_typeof(x) from anyctest(variadic array[11, 12.2]) x;
+     x     | pg_typeof
+-----------+-----------
+ {11,12.2} | numeric[]
+(1 row)
+
+drop function anyctest(variadic anycompatiblearray);
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index cdfc43e..7eced28 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1557,6 +1557,59 @@ 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 result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum,
oranyrange. 
+CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
+AS 'select $1, $2' LANGUAGE sql;
+SELECT dup(22, array[44]);
+    dup
+-----------
+ (22,{44})
+(1 row)
+
+SELECT dup(4.5, array[44]);
+    dup
+------------
+ (4.5,{44})
+(1 row)
+
+SELECT dup(22, array[44::bigint]);
+    dup
+-----------
+ (22,{44})
+(1 row)
+
+SELECT *, pg_typeof(f3), pg_typeof(f4) FROM dup(22, array[44::bigint]);
+ f3 |  f4  | pg_typeof | pg_typeof
+----+------+-----------+-----------
+ 22 | {44} | bigint    | bigint[]
+(1 row)
+
+DROP FUNCTION dup(f1 anycompatible, f2 anycompatiblearray);
+CREATE FUNCTION dup (f1 anycompatiblerange, f2 out anycompatible, f3 out anycompatiblearray, f4 out
anycompatiblerange)
+AS 'select lower($1), array[lower($1), upper($1)], $1' LANGUAGE sql;
+SELECT dup(int4range(4,7));
+         dup
+---------------------
+ (4,"{4,7}","[4,7)")
+(1 row)
+
+SELECT dup(numrange(4,7));
+         dup
+---------------------
+ (4,"{4,7}","[4,7)")
+(1 row)
+
+SELECT dup(textrange('aaa', 'bbb'));
+              dup
+-------------------------------
+ (aaa,"{aaa,bbb}","[aaa,bbb)")
+(1 row)
+
+DROP FUNCTION dup(f1 anycompatiblerange);
+-- fails, no way to deduce outputs
+CREATE FUNCTION bad (f1 anyarray, out f2 anycompatible, out f3 anycompatiblearray)
+AS 'select $1, array[$1,$1]' LANGUAGE sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anycompatible requires at least one input of type anycompatible, anycompatiblearray,
anycompatiblenonarray,or anycompatiblerange. 
 --
 -- table functions
 --
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index a28741a..c421f53 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -1405,6 +1405,32 @@ ERROR:  function rangetypes_sql(numrange, integer[]) does not exist
 LINE 1: select rangetypes_sql(numrange(1,10), ARRAY[2,20]);
                ^
 HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+create function anycompatiblearray_anycompatiblerange_func(a anycompatiblearray, r anycompatiblerange)
+  returns anycompatible as 'select $1[1] + lower($2);' language sql;
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1,2], int4range(10,20));
+ anycompatiblearray_anycompatiblerange_func
+--------------------------------------------
+                                         11
+(1 row)
+
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1,2], numrange(10,20));
+ anycompatiblearray_anycompatiblerange_func
+--------------------------------------------
+                                         11
+(1 row)
+
+-- should fail
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1.1,2], int4range(10,20));
+ERROR:  function anycompatiblearray_anycompatiblerange_func(numeric[], int4range) does not exist
+LINE 1: select anycompatiblearray_anycompatiblerange_func(ARRAY[1.1,...
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anycompatiblearray_anycompatiblerange_func(anycompatiblearray, anycompatiblerange);
+-- should fail
+create function bogus_func(anycompatible)
+  returns anycompatiblerange as 'select int4range(1,10)' language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
 --
 -- Arrays of ranges
 --
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03..274130e 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -131,14 +131,16 @@ WHERE p1.typinput = p2.oid AND NOT

 -- Check for type of the variadic array parameter's elements.
 -- provariadic should be ANYOID if the type of the last element is ANYOID,
--- ANYELEMENTOID if the type of the last element is ANYARRAYOID, and otherwise
--- the element type corresponding to the array type.
+-- ANYELEMENTOID if the type of the last element is ANYARRAYOID,
+-- ANYCOMPATIBLEOID if the type of the last element is ANYCOMPATIBLEARRAYOID,
+-- and otherwise the element type corresponding to the array type.
 SELECT oid::regprocedure, provariadic::regtype, proargtypes::regtype[]
 FROM pg_proc
 WHERE provariadic != 0
 AND case proargtypes[array_length(proargtypes, 1)-1]
-    WHEN 2276 THEN 2276 -- any -> any
-    WHEN 2277 THEN 2283 -- anyarray -> anyelement
+    WHEN '"any"'::regtype THEN '"any"'::regtype
+    WHEN 'anyarray'::regtype THEN 'anyelement'::regtype
+    WHEN 'anycompatiblearray'::regtype THEN 'anycompatible'::regtype
     ELSE (SELECT t.oid
           FROM pg_type t
           WHERE t.typarray = proargtypes[array_length(proargtypes, 1)-1])
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 3e593f2..f04ac29 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -740,6 +740,10 @@ drop view aggordview1;
 select least_agg(q1,q2) from int8_tbl;
 select least_agg(variadic array[q1,q2]) from int8_tbl;

+select cleast_agg(q1,q2) from int8_tbl;
+select cleast_agg(4.5,f1) from int4_tbl;
+select cleast_agg(variadic array[4.5,f1]) from int4_tbl;
+select pg_typeof(cleast_agg(variadic array[4.5,f1])) from int4_tbl;

 -- test aggregates with common transition functions share the same states
 begin work;
diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql
index fd7cd40..d4b4036 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -72,7 +72,31 @@ create aggregate aggfns(integer,integer,text) (
    initcond = '{}'
 );

--- variadic aggregate
+-- check error cases that would require run-time type coercion
+create function least_accum(int8, int8) returns int8 language sql as
+  'select least($1, $2)';
+
+create aggregate least_agg(int4) (
+  stype = int8, sfunc = least_accum
+);  -- fails
+
+drop function least_accum(int8, int8);
+
+create function least_accum(anycompatible, anycompatible)
+returns anycompatible language sql as
+  'select least($1, $2)';
+
+create aggregate least_agg(int4) (
+  stype = int8, sfunc = least_accum
+);  -- fails
+
+create aggregate least_agg(int8) (
+  stype = int8, sfunc = least_accum
+);
+
+drop function least_accum(anycompatible, anycompatible) cascade;
+
+-- variadic aggregates
 create function least_accum(anyelement, variadic anyarray)
 returns anyelement language sql as
   'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
@@ -81,6 +105,14 @@ create aggregate least_agg(variadic items anyarray) (
   stype = anyelement, sfunc = least_accum
 );

+create function cleast_accum(anycompatible, variadic anycompatiblearray)
+returns anycompatible language sql as
+  'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
+
+create aggregate cleast_agg(variadic items anycompatiblearray) (
+  stype = anycompatible, sfunc = cleast_accum
+);
+
 -- test ordered-set aggs using built-in support functions
 create aggregate my_percentile_disc(float8 ORDER BY anyelement) (
   stype = internal,
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index f06f245..389d5b2 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -273,7 +273,7 @@ SELECT p1.oid, p1.proname
 FROM pg_proc as p1
 WHERE p1.prorettype IN
     ('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
-     'anyenum'::regtype, 'anyrange'::regtype)
+     'anyenum'::regtype)
   AND NOT
     ('anyelement'::regtype = ANY (p1.proargtypes) OR
      'anyarray'::regtype = ANY (p1.proargtypes) OR
@@ -282,6 +282,37 @@ WHERE p1.prorettype IN
      'anyrange'::regtype = ANY (p1.proargtypes))
 ORDER BY 2;

+-- anyrange is tighter than the rest, can only resolve from anyrange input
+
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype = 'anyrange'::regtype
+  AND NOT
+     'anyrange'::regtype = ANY (p1.proargtypes)
+ORDER BY 2;
+
+-- similarly for the anycompatible family
+
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype IN
+    ('anycompatible'::regtype, 'anycompatiblearray'::regtype,
+     'anycompatiblenonarray'::regtype)
+  AND NOT
+    ('anycompatible'::regtype = ANY (p1.proargtypes) OR
+     'anycompatiblearray'::regtype = ANY (p1.proargtypes) OR
+     'anycompatiblenonarray'::regtype = ANY (p1.proargtypes) OR
+     'anycompatiblerange'::regtype = ANY (p1.proargtypes))
+ORDER BY 2;
+
+SELECT p1.oid, p1.proname
+FROM pg_proc as p1
+WHERE p1.prorettype = 'anycompatiblerange'::regtype
+  AND NOT
+     'anycompatiblerange'::regtype = ANY (p1.proargtypes)
+ORDER BY 2;
+
+
 -- Look for functions that accept cstring and are neither datatype input
 -- functions nor encoding conversion functions.  It's almost never a good
 -- idea to use cstring input for a function meant to be called from SQL;
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index c5e6e6e..425ad8e 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -1618,6 +1618,41 @@ select f1(int4range(42, 49)) as int, f1(float8range(4.5, 7.8)) as num;

 drop function f1(x anyrange);

+create function f1(x anycompatible, y anycompatible) returns anycompatiblearray as $$
+begin
+  return array[x, y];
+end$$ language plpgsql;
+
+select f1(2, 4) as int, f1(2, 4.5) as num;
+
+drop function f1(x anycompatible, y anycompatible);
+
+create function f1(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+begin
+  return array[lower(x), upper(x), y, z];
+end$$ language plpgsql;
+
+select f1(int4range(42, 49), 11, 2::smallint) as int, f1(float8range(4.5, 7.8), 7.8, 11::real) as num;
+
+select f1(int4range(42, 49), 11, 4.5) as fail;  -- range type doesn't fit
+
+drop function f1(x anycompatiblerange, y anycompatible, z anycompatible);
+
+-- fail, can't infer type:
+create function f1(x anycompatible) returns anycompatiblerange as $$
+begin
+  return array[x + 1, x + 2];
+end$$ language plpgsql;
+
+create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
+begin
+  return x;
+end$$ language plpgsql;
+
+select f1(int4range(42, 49), array[11]) as int, f1(float8range(4.5, 7.8), array[7]) as num;
+
+drop function f1(x anycompatiblerange, y anycompatiblearray);
+
 --
 -- Test handling of OUT parameters, including polymorphic cases.
 -- Note that RETURN is optional with OUT params; we try both ways.
@@ -1699,6 +1734,18 @@ select * from duplic('foo'::text);

 drop function duplic(anyelement);

+create function duplic(in i anycompatiblerange, out j anycompatible, out k anycompatiblearray) as $$
+begin
+  j := lower(i);
+  k := array[lower(i),upper(i)];
+  return;
+end$$ language plpgsql;
+
+select * from duplic(int4range(42,49));
+select * from duplic(textrange('aaa', 'bbb'));
+
+drop function duplic(anycompatiblerange);
+
 --
 -- test PERFORM
 --
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index 9c7b43e..ba698b8 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -53,6 +53,37 @@ select polyf(int4range(42, 49)) as int, polyf(float8range(4.5, 7.8)) as num;

 drop function polyf(x anyrange);

+create function polyf(x anycompatible, y anycompatible) returns anycompatiblearray as $$
+  select array[x, y]
+$$ language sql;
+
+select polyf(2, 4) as int, polyf(2, 4.5) as num;
+
+drop function polyf(x anycompatible, y anycompatible);
+
+create function polyf(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+  select array[lower(x), upper(x), y, z]
+$$ language sql;
+
+select polyf(int4range(42, 49), 11, 2::smallint) as int, polyf(float8range(4.5, 7.8), 7.8, 11::real) as num;
+
+select polyf(int4range(42, 49), 11, 4.5) as fail;  -- range type doesn't fit
+
+drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblerange as $$
+  select array[x + 1, x + 2]
+$$ language sql;
+
+create function polyf(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
+  select x
+$$ language sql;
+
+select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8), array[7]) as num;
+
+drop function polyf(x anycompatiblerange, y anycompatiblearray);
+

 --
 -- Polymorphic aggregate tests
@@ -868,3 +899,101 @@ select * from dfview;

 drop view dfview;
 drop function dfunc(anyelement, anyelement, bool);
+
+--
+-- Tests for ANYCOMPATIBLE polymorphism family
+--
+
+create function anyctest(anycompatible, anycompatible)
+returns anycompatible as $$
+  select greatest($1, $2)
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, 12) x;
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+select x, pg_typeof(x) from anyctest(11, point(1,2)) x;  -- fail
+select x, pg_typeof(x) from anyctest('11', '12.3') x;  -- fail, should it work?
+select x, pg_typeof(x) from anyctest('11', '12.3'::text) x;
+
+drop function anyctest(anycompatible, anycompatible);
+
+create function anyctest(anycompatible, anycompatible)
+returns anycompatiblearray as $$
+  select array[$1, $2]
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, 12) x;
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+select x, pg_typeof(x) from anyctest(11, array[1,2]) x;  -- fail
+
+drop function anyctest(anycompatible, anycompatible);
+
+create function anyctest(anycompatible, anycompatiblearray)
+returns anycompatiblearray as $$
+  select array[$1] || $2
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, array[12]) x;
+select x, pg_typeof(x) from anyctest(11, array[12.3]) x;
+select x, pg_typeof(x) from anyctest(12.3, array[13]) x;
+select x, pg_typeof(x) from anyctest(12.3, '{13,14.4}') x;
+select x, pg_typeof(x) from anyctest(11, array[point(1,2)]) x;  -- fail
+select x, pg_typeof(x) from anyctest(11, 12) x;  -- fail
+
+drop function anyctest(anycompatible, anycompatiblearray);
+
+create function anyctest(anycompatible, anycompatiblerange)
+returns anycompatiblerange as $$
+  select $2
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, int4range(4,7)) x;
+select x, pg_typeof(x) from anyctest(11, numrange(4,7)) x;
+select x, pg_typeof(x) from anyctest(11, 12) x;  -- fail
+select x, pg_typeof(x) from anyctest(11.2, int4range(4,7)) x;  -- fail
+select x, pg_typeof(x) from anyctest(11.2, '[4,7)') x;  -- fail
+
+drop function anyctest(anycompatible, anycompatiblerange);
+
+create function anyctest(anycompatiblerange, anycompatiblerange)
+returns anycompatible as $$
+  select lower($1) + upper($2)
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(int4range(11,12), int4range(4,7)) x;
+select x, pg_typeof(x) from anyctest(int4range(11,12), numrange(4,7)) x; -- fail
+
+drop function anyctest(anycompatiblerange, anycompatiblerange);
+
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblerange as $$
+  select $1
+$$ language sql;
+
+create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+  select array[$1, $2]
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, 12) x;
+select x, pg_typeof(x) from anyctest(11, 12.3) x;
+select x, pg_typeof(x) from anyctest(array[11], array[1,2]) x;  -- fail
+
+drop function anyctest(anycompatiblenonarray, anycompatiblenonarray);
+
+create function anyctest(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+begin
+  return $1;
+end;
+$$ language plpgsql;
+
+select x, pg_typeof(x) from anyctest(11, 12) x;
+select x, pg_typeof(x) from anyctest(11, 12.2) x;
+select x, pg_typeof(x) from anyctest(11, '12') x;
+select x, pg_typeof(x) from anyctest(11, '12.2') x;  -- fail
+select x, pg_typeof(x) from anyctest(variadic array[11, 12]) x;
+select x, pg_typeof(x) from anyctest(variadic array[11, 12.2]) x;
+
+drop function anyctest(variadic anycompatiblearray);
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 476b4f2..ae3119a 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -407,6 +407,27 @@ DROP FUNCTION dup(anyelement);
 CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
 AS 'select $1, array[$1,$1]' LANGUAGE sql;

+CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
+AS 'select $1, $2' LANGUAGE sql;
+SELECT dup(22, array[44]);
+SELECT dup(4.5, array[44]);
+SELECT dup(22, array[44::bigint]);
+SELECT *, pg_typeof(f3), pg_typeof(f4) FROM dup(22, array[44::bigint]);
+
+DROP FUNCTION dup(f1 anycompatible, f2 anycompatiblearray);
+
+CREATE FUNCTION dup (f1 anycompatiblerange, f2 out anycompatible, f3 out anycompatiblearray, f4 out
anycompatiblerange)
+AS 'select lower($1), array[lower($1), upper($1)], $1' LANGUAGE sql;
+SELECT dup(int4range(4,7));
+SELECT dup(numrange(4,7));
+SELECT dup(textrange('aaa', 'bbb'));
+
+DROP FUNCTION dup(f1 anycompatiblerange);
+
+-- fails, no way to deduce outputs
+CREATE FUNCTION bad (f1 anyarray, out f2 anycompatible, out f3 anycompatiblearray)
+AS 'select $1, array[$1,$1]' LANGUAGE sql;
+
 --
 -- table functions
 --
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 85eaa9b..4048b1d 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -456,6 +456,22 @@ create function rangetypes_sql(q anyrange, b anyarray, out c anyelement)
 select rangetypes_sql(int4range(1,10), ARRAY[2,20]);
 select rangetypes_sql(numrange(1,10), ARRAY[2,20]);  -- match failure

+create function anycompatiblearray_anycompatiblerange_func(a anycompatiblearray, r anycompatiblerange)
+  returns anycompatible as 'select $1[1] + lower($2);' language sql;
+
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1,2], int4range(10,20));
+
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1,2], numrange(10,20));
+
+-- should fail
+select anycompatiblearray_anycompatiblerange_func(ARRAY[1.1,2], int4range(10,20));
+
+drop function anycompatiblearray_anycompatiblerange_func(anycompatiblearray, anycompatiblerange);
+
+-- should fail
+create function bogus_func(anycompatible)
+  returns anycompatiblerange as 'select int4range(1,10)' language sql;
+
 --
 -- Arrays of ranges
 --
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413b..4b492ce 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -106,15 +106,17 @@ WHERE p1.typinput = p2.oid AND NOT

 -- Check for type of the variadic array parameter's elements.
 -- provariadic should be ANYOID if the type of the last element is ANYOID,
--- ANYELEMENTOID if the type of the last element is ANYARRAYOID, and otherwise
--- the element type corresponding to the array type.
+-- ANYELEMENTOID if the type of the last element is ANYARRAYOID,
+-- ANYCOMPATIBLEOID if the type of the last element is ANYCOMPATIBLEARRAYOID,
+-- and otherwise the element type corresponding to the array type.

 SELECT oid::regprocedure, provariadic::regtype, proargtypes::regtype[]
 FROM pg_proc
 WHERE provariadic != 0
 AND case proargtypes[array_length(proargtypes, 1)-1]
-    WHEN 2276 THEN 2276 -- any -> any
-    WHEN 2277 THEN 2283 -- anyarray -> anyelement
+    WHEN '"any"'::regtype THEN '"any"'::regtype
+    WHEN 'anyarray'::regtype THEN 'anyelement'::regtype
+    WHEN 'anycompatiblearray'::regtype THEN 'anycompatible'::regtype
     ELSE (SELECT t.oid
           FROM pg_type t
           WHERE t.typarray = proargtypes[array_length(proargtypes, 1)-1])
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index ff51660..0bec854 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1285,6 +1285,16 @@ SELECT make_array2(1, 2.5) AS numericarray;
  {1,2.5}
 (1 row)
 </screen>
+     Because the rules for common type resolution default to choosing
+     type <type>text</type> when all inputs are of unknown types, this
+     also works:
+<screen>
+SELECT make_array2('a', 'b') AS textarray;
+ textarray
+-----------
+ {a,b}
+(1 row)
+</screen>
     </para>

     <para>
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 1e53f7c..645e4aa 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -2251,14 +2251,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
             {
                 /*
                  * Only way to get here is if all the ANYCOMPATIBLE args have
-                 * UNKNOWN inputs
-                 *
-                 * XXX shouldn't we use TEXT?  But not TEXTRANGE.
+                 * UNKNOWN inputs.  Resolve to TEXT as select_common_type()
+                 * would do.  That doesn't license us to use TEXTRANGE,
+                 * though.
                  */
-                ereport(ERROR,
-                        (errcode(ERRCODE_DATATYPE_MISMATCH),
-                         errmsg("could not determine polymorphic type because input has type %s",
-                                "unknown")));
+                anycompatible_typeid = TEXTOID;
+                anycompatible_array_typeid = TEXTARRAYOID;
+                if (have_anycompatible_range)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_DATATYPE_MISMATCH),
+                             errmsg("could not determine polymorphic type %s because input has type %s",
+                                    "anycompatiblerange", "unknown")));
             }
         }

diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index bd854e3..fd4ff91 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -1686,9 +1686,7 @@ ERROR:  function anyctest(integer, point) does not exist
 LINE 1: select x, pg_typeof(x) from anyctest(11, point(1,2)) x;
                                     ^
 HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
-select x, pg_typeof(x) from anyctest('11', '12.3') x;  -- fail, should it work?
-ERROR:  could not determine polymorphic type because input has type unknown
-select x, pg_typeof(x) from anyctest('11', '12.3'::text) x;
+select x, pg_typeof(x) from anyctest('11', '12.3') x;  -- defaults to text
   x   | pg_typeof
 ------+-----------
  12.3 | text
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index ba698b8..ec89f3c 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -912,8 +912,7 @@ $$ language sql;
 select x, pg_typeof(x) from anyctest(11, 12) x;
 select x, pg_typeof(x) from anyctest(11, 12.3) x;
 select x, pg_typeof(x) from anyctest(11, point(1,2)) x;  -- fail
-select x, pg_typeof(x) from anyctest('11', '12.3') x;  -- fail, should it work?
-select x, pg_typeof(x) from anyctest('11', '12.3'::text) x;
+select x, pg_typeof(x) from anyctest('11', '12.3') x;  -- defaults to text

 drop function anyctest(anycompatible, anycompatible);


Re: proposal: new polymorphic types - commontype and commontypearray

От
Tom Lane
Дата:
I wrote:
> Here's a pretty-nearly-final version of the patch.
> In 0001 below, I've left it throwing an error for the case of all
> ANYCOMPATIBLE inputs being unknown, but the documentation fails to
> acknowledge that.  0002 below is a delta patch that switches to the
> other approach of resolving as TEXT.  I'm pretty well convinced that
> 0002 is what we should do, so I have not bothered to write a doc
> change that would explain 0001's behavior on this point.

Pushed with the resolve-to-TEXT mod, and some last-minute
polishing from a final re-read of the patch.

            regards, tom lane



Re: proposal: new polymorphic types - commontype and commontypearray

От
Pavel Stehule
Дата:


čt 19. 3. 2020 v 16:44 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
I wrote:
> Here's a pretty-nearly-final version of the patch.
> In 0001 below, I've left it throwing an error for the case of all
> ANYCOMPATIBLE inputs being unknown, but the documentation fails to
> acknowledge that.  0002 below is a delta patch that switches to the
> other approach of resolving as TEXT.  I'm pretty well convinced that
> 0002 is what we should do, so I have not bothered to write a doc
> change that would explain 0001's behavior on this point.

Pushed with the resolve-to-TEXT mod, and some last-minute
polishing from a final re-read of the patch.

great, thank you very much

Pavel


                        regards, tom lane