mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
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
This commit is contained in:
@ -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>
|
||||
|
Reference in New Issue
Block a user