1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

Improve the handling of result type coercions in SQL functions.

Use the parser's standard type coercion machinery to convert the
output column(s) of a SQL function's final SELECT or RETURNING
to the type(s) they should have according to the function's declared
result type.  We'll allow any case where an assignment-level
coercion is available.  Previously, we failed unless the required
coercion was a binary-compatible one (and the documentation ignored
this, falsely claiming that the types must match exactly).

Notably, the coercion now accounts for typmods, so that cases where
a SQL function is declared to return a composite type whose columns
are typmod-constrained now behave as one would expect.  Arguably
this aspect is a bug fix, but the overall behavioral change here
seems too large to consider back-patching.

A nice side-effect is that functions can now be inlined in a
few cases where we previously failed to do so because of type
mismatches.

Discussion: https://postgr.es/m/18929.1574895430@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2020-01-08 11:07:53 -05:00
parent 8dd1511e39
commit 913bbd88dc
7 changed files with 656 additions and 332 deletions

View File

@ -388,11 +388,15 @@ $$ LANGUAGE SQL;
</para>
<para>
A <acronym>SQL</acronym> function must return exactly its declared
result type. This may require inserting an explicit cast.
If the final <literal>SELECT</literal> or <literal>RETURNING</literal>
clause in a <acronym>SQL</acronym> function does not return exactly
the function's declared result
type, <productname>PostgreSQL</productname> will automatically cast
the value to the required type, if that is possible with an implicit
or assignment cast. Otherwise, you must write an explicit cast.
For example, suppose we wanted the
previous <function>add_em</function> function to return
type <type>float8</type> instead. This won't work:
type <type>float8</type> instead. It's sufficient to write
<programlisting>
CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$
@ -400,16 +404,10 @@ CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$
$$ LANGUAGE SQL;
</programlisting>
even though in other contexts <productname>PostgreSQL</productname>
would be willing to insert an implicit cast to
convert <type>integer</type> to <type>float8</type>.
We need to write it as
<programlisting>
CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$
SELECT ($1 + $2)::float8;
$$ LANGUAGE SQL;
</programlisting>
since the <type>integer</type> sum can be implicitly cast
to <type>float8</type>.
(See <xref linkend="typeconv"/> or <xref linkend="sql-createcast"/>
for more about casts.)
</para>
</sect2>
@ -503,23 +501,24 @@ $$ LANGUAGE SQL;
<listitem>
<para>
The select list order in the query must be exactly the same as
that in which the columns appear in the table associated
with the composite type. (Naming the columns, as we did above,
that in which the columns appear in the composite type.
(Naming the columns, as we did above,
is irrelevant to the system.)
</para>
</listitem>
<listitem>
<para>
We must ensure each expression's type matches the corresponding
column of the composite type, inserting a cast if necessary.
We must ensure each expression's type can be cast to that of
the corresponding column of the composite type.
Otherwise we'll get errors like this:
<screen>
<computeroutput>
ERROR: function declared to return emp returns varchar instead of text at column 1
ERROR: return type mismatch in function declared to return emp
DETAIL: Final statement returns text instead of point at column 4.
</computeroutput>
</screen>
As with the base-type case, the function will not insert any casts
automatically.
As with the base-type case, the system will not insert explicit
casts automatically, only implicit or assignment casts.
</para>
</listitem>
</itemizedlist>
@ -542,8 +541,7 @@ $$ LANGUAGE SQL;
Another example is that if we are trying to write a function that
returns a domain over composite, rather than a plain composite type,
it is always necessary to write it as returning a single column,
since there is no other way to produce a value that is exactly of
the domain type.
since there is no way to cause a coercion of the whole row result.
</para>
<para>
@ -1263,7 +1261,7 @@ SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;
Without the typecast, you will get errors like this:
<screen>
<computeroutput>
ERROR: could not determine polymorphic type because input has type "unknown"
ERROR: could not determine polymorphic type because input has type unknown
</computeroutput>
</screen>
</para>