Commit 24e2885e authored by Tom Lane's avatar Tom Lane

Introduce "anycompatible" family of polymorphic types.

This patch adds the pseudo-types anycompatible, anycompatiblearray,
anycompatiblenonarray, and anycompatiblerange.  They work much like
anyelement, anyarray, anynonarray, and anyrange respectively, except
that the actual input values need not match precisely in type.
Instead, if we can find a common supertype (using the same rules
as for UNION/CASE type resolution), then the parser automatically
promotes the input values to that type.  For example,
"myfunc(anycompatible, anycompatible)" can match a call with one
integer and one bigint argument, with the integer automatically
promoted to bigint.  With anyelement in the definition, the user
would have had to cast the integer explicitly.

The new types also provide a second, independent set of type variables
for function matching; thus with "myfunc(anyelement, anyelement,
anycompatible) returns anycompatible" the first two arguments are
constrained to be the same type, but the third can be some other
type, and the result has the type of the third argument.  The need
for more than one set of type variables was foreseen back when we
first invented the polymorphic types, but we never did anything
about it.

Pavel Stehule, revised a bit by me

Discussion: https://postgr.es/m/CAFj8pRDna7VqNi8gR+Tt2Ktmz0cq5G93guc3Sbn_NVPLdXAkqA@mail.gmail.com
parent fab13dc5
......@@ -4798,6 +4798,22 @@ SELECT * FROM pg_attribute
<primary>anyrange</primary>
</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>
......@@ -4822,6 +4838,10 @@ SELECT * FROM pg_attribute
<primary>fdw_handler</primary>
</indexterm>
<indexterm zone="datatype-pseudo">
<primary>table_am_handler</primary>
</indexterm>
<indexterm zone="datatype-pseudo">
<primary>index_am_handler</primary>
</indexterm>
......@@ -4903,6 +4923,35 @@ SELECT * FROM pg_attribute
<xref linkend="rangetypes"/>).</entry>
</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>
......@@ -4924,6 +4973,11 @@ SELECT * FROM pg_attribute
<entry>A foreign-data wrapper handler is declared to return <type>fdw_handler</type>.</entry>
</row>
<row>
<entry><type>table_am_handler</type></entry>
<entry>A table access method handler is declared to return <type>table_am_handler</type>.</entry>
</row>
<row>
<entry><type>index_am_handler</type></entry>
<entry>An index access method handler is declared to return <type>index_am_handler</type>.</entry>
......@@ -4970,7 +5024,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>
......@@ -4981,10 +5035,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>
......
......@@ -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
......@@ -311,14 +406,70 @@
both actual arguments have to be the same enum type.
</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>
......
......@@ -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">
......
......@@ -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,43 @@ 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>
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>
......@@ -1284,7 +1315,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, or anyrange.
</screen>
</para>
......@@ -3157,11 +3188,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>
......
......@@ -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)
{
......
......@@ -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))
......
......@@ -320,6 +320,7 @@ interpret_function_parameter_list(ParseState *pstate,
switch (toid)
{
case ANYARRAYOID:
case ANYCOMPATIBLEARRAYOID:
case ANYOID:
/* okay */
break;
......
This diff is collapsed.
......@@ -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;
......
......@@ -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;
......
......@@ -168,6 +168,26 @@ anyarray_send(PG_FUNCTION_ARGS)
return array_send(fcinfo);
}
/*
* 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
*
......@@ -194,6 +214,19 @@ anyrange_out(PG_FUNCTION_ARGS)
return range_out(fcinfo);
}
/*
* 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
*
......@@ -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);
This diff is collapsed.
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202003181
#define CATALOG_VERSION_NO 202003191
#endif
......@@ -6673,8 +6673,9 @@
proname => 'regcollationout', provolatile => 's', prorettype => 'cstring',
proargtypes => 'regcollation', prosrc => 'regcollationout' },
{ oid => '4195', descr => 'convert classname to regcollation',
proname => 'to_regcollation', provolatile => 's', prorettype => 'regcollation',
proargtypes => 'text', prosrc => 'to_regcollation' },
proname => 'to_regcollation', provolatile => 's',
prorettype => 'regcollation', proargtypes => 'text',
prosrc => 'to_regcollation' },
{ oid => '2220', descr => 'I/O',
proname => 'regtypein', provolatile => 's', prorettype => 'regtype',
proargtypes => 'cstring', prosrc => 'regtypein' },
......@@ -7115,6 +7116,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',
......@@ -7459,8 +7496,8 @@
proname => 'regcollationrecv', prorettype => 'regcollation',
proargtypes => 'internal', prosrc => 'regcollationrecv' },
{ oid => '4197', descr => 'I/O',
proname => 'regcollationsend', prorettype => 'bytea', proargtypes => 'regcollation',
prosrc => 'regcollationsend' },
proname => 'regcollationsend', prorettype => 'bytea',
proargtypes => 'regcollation', prosrc => 'regcollationsend' },
{ oid => '2454', descr => 'I/O',
proname => 'regtyperecv', prorettype => 'regtype', proargtypes => 'internal',
prosrc => 'regtyperecv' },
......
......@@ -382,7 +382,8 @@
{ oid => '4191', array_type_oid => '4192', descr => 'registered collation',
typname => 'regcollation', typlen => '4', typbyval => 't', typcategory => 'N',
typinput => 'regcollationin', typoutput => 'regcollationout',
typreceive => 'regcollationrecv', typsend => 'regcollationsend', typalign => 'i' },
typreceive => 'regcollationrecv', typsend => 'regcollationsend',
typalign => 'i' },
{ oid => '2206', array_type_oid => '2211', descr => 'registered type',
typname => 'regtype', typlen => '4', typbyval => 't', typcategory => 'N',
typinput => 'regtypein', typoutput => 'regtypeout',
......@@ -590,9 +591,34 @@
typoutput => 'table_am_handler_out', typreceive => '-', typsend => '-',
typalign => 'i' },
{ oid => '3831',
descr => 'pseudo-type representing a polymorphic base type that is a range',
descr => 'pseudo-type representing a range over a polymorphic base type',
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 range over a polymorphic common type',
typname => 'anycompatiblerange', typlen => '-1', typbyval => 'f',
typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
typalign => 'd', typstorage => 'x' },
]
......@@ -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 */
......
......@@ -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:
......
......@@ -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);
......
......@@ -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,
......
......@@ -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
......
......@@ -1823,6 +1823,88 @@ 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);
create function f1(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
as $$
begin
x := a || b;
y := array[c, d];
end$$ language plpgsql;
select x, pg_typeof(x), y, pg_typeof(y)
from f1(11, array[1, 2], 42, 34.5);
x | pg_typeof | y | pg_typeof
----------+-----------+-----------+-----------
{11,1,2} | integer[] | {42,34.5} | numeric[]
(1 row)
select x, pg_typeof(x), y, pg_typeof(y)
from f1(11, array[1, 2], point(1,2), point(3,4));
x | pg_typeof | y | pg_typeof
----------+-----------+-------------------+-----------
{11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[]
(1 row)
select x, pg_typeof(x), y, pg_typeof(y)
from f1(11, '{1,2}', point(1,2), '(3,4)');
x | pg_typeof | y | pg_typeof
----------+-----------+-------------------+-----------
{11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[]
(1 row)
select x, pg_typeof(x), y, pg_typeof(y)
from f1(11, array[1, 2.2], 42, 34.5); -- fail
ERROR: function f1(integer, numeric[], integer, numeric) does not exist
LINE 2: from f1(11, array[1, 2.2], 42, 34.5);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function f1(a anyelement, b anyarray,
c anycompatible, d anycompatible);
--
-- Test handling of OUT parameters, including polymorphic cases.
-- Note that RETURN is optional with OUT params; we try both ways.
......@@ -1940,6 +2022,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
--
......
This diff is collapsed.
......@@ -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, or anyrange.
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
--
......
......@@ -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
--
......
......@@ -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])
......
......@@ -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;
......
......@@ -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,
......
......@@ -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;
......
......@@ -1618,6 +1618,62 @@ 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);
create function f1(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
as $$
begin
x := a || b;
y := array[c, d];
end$$ language plpgsql;
select x, pg_typeof(x), y, pg_typeof(y)
from f1(11, array[1, 2], 42, 34.5);
select x, pg_typeof(x), y, pg_typeof(y)
from f1(11, array[1, 2], point(1,2), point(3,4));
select x, pg_typeof(x), y, pg_typeof(y)
from f1(11, '{1,2}', point(1,2), '(3,4)');
select x, pg_typeof(x), y, pg_typeof(y)
from f1(11, array[1, 2.2], 42, 34.5); -- fail
drop function f1(a anyelement, b anyarray,
c anycompatible, d anycompatible);
--
-- Test handling of OUT parameters, including polymorphic cases.
-- Note that RETURN is optional with OUT params; we try both ways.
......@@ -1699,6 +1755,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
--
......
......@@ -53,6 +53,56 @@ 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);
create function polyf(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
as $$
select a || b, array[c, d]
$$ language sql;
select x, pg_typeof(x), y, pg_typeof(y)
from polyf(11, array[1, 2], 42, 34.5);
select x, pg_typeof(x), y, pg_typeof(y)
from polyf(11, array[1, 2], point(1,2), point(3,4));
select x, pg_typeof(x), y, pg_typeof(y)
from polyf(11, '{1,2}', point(1,2), '(3,4)');
select x, pg_typeof(x), y, pg_typeof(y)
from polyf(11, array[1, 2.2], 42, 34.5); -- fail
drop function polyf(a anyelement, b anyarray,
c anycompatible, d anycompatible);
--
-- Polymorphic aggregate tests
......@@ -868,3 +918,112 @@ 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; -- defaults to text
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(a anyelement, b anyarray,
c anycompatible, d anycompatible)
returns anycompatiblearray as $$
select array[c, d]
$$ language sql;
select x, pg_typeof(x) from anyctest(11, array[1, 2], 42, 34.5) x;
select x, pg_typeof(x) from anyctest(11, array[1, 2], point(1,2), point(3,4)) x;
select x, pg_typeof(x) from anyctest(11, '{1,2}', point(1,2), '(3,4)') x;
select x, pg_typeof(x) from anyctest(11, array[1, 2.2], 42, 34.5) x; -- fail
drop function anyctest(a anyelement, b anyarray,
c anycompatible, d anycompatible);
create function anyctest(variadic anycompatiblearray)
returns anycompatiblearray as $$
select $1
$$ language sql;
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);
......@@ -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
--
......
......@@ -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
--
......
......@@ -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])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment