Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 913bbd8

Browse files
committed
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
1 parent 8dd1511 commit 913bbd8

File tree

7 files changed

+656
-332
lines changed

7 files changed

+656
-332
lines changed

doc/src/sgml/xfunc.sgml

+21-23
Original file line numberDiff line numberDiff line change
@@ -388,28 +388,26 @@ $$ LANGUAGE SQL;
388388
</para>
389389

390390
<para>
391-
A <acronym>SQL</acronym> function must return exactly its declared
392-
result type. This may require inserting an explicit cast.
391+
If the final <literal>SELECT</literal> or <literal>RETURNING</literal>
392+
clause in a <acronym>SQL</acronym> function does not return exactly
393+
the function's declared result
394+
type, <productname>PostgreSQL</productname> will automatically cast
395+
the value to the required type, if that is possible with an implicit
396+
or assignment cast. Otherwise, you must write an explicit cast.
393397
For example, suppose we wanted the
394398
previous <function>add_em</function> function to return
395-
type <type>float8</type> instead. This won't work:
399+
type <type>float8</type> instead. It's sufficient to write
396400

397401
<programlisting>
398402
CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$
399403
SELECT $1 + $2;
400404
$$ LANGUAGE SQL;
401405
</programlisting>
402406

403-
even though in other contexts <productname>PostgreSQL</productname>
404-
would be willing to insert an implicit cast to
405-
convert <type>integer</type> to <type>float8</type>.
406-
We need to write it as
407-
408-
<programlisting>
409-
CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$
410-
SELECT ($1 + $2)::float8;
411-
$$ LANGUAGE SQL;
412-
</programlisting>
407+
since the <type>integer</type> sum can be implicitly cast
408+
to <type>float8</type>.
409+
(See <xref linkend="typeconv"/> or <xref linkend="sql-createcast"/>
410+
for more about casts.)
413411
</para>
414412
</sect2>
415413

@@ -503,23 +501,24 @@ $$ LANGUAGE SQL;
503501
<listitem>
504502
<para>
505503
The select list order in the query must be exactly the same as
506-
that in which the columns appear in the table associated
507-
with the composite type. (Naming the columns, as we did above,
504+
that in which the columns appear in the composite type.
505+
(Naming the columns, as we did above,
508506
is irrelevant to the system.)
509507
</para>
510508
</listitem>
511509
<listitem>
512510
<para>
513-
We must ensure each expression's type matches the corresponding
514-
column of the composite type, inserting a cast if necessary.
511+
We must ensure each expression's type can be cast to that of
512+
the corresponding column of the composite type.
515513
Otherwise we'll get errors like this:
516514
<screen>
517515
<computeroutput>
518-
ERROR: function declared to return emp returns varchar instead of text at column 1
516+
ERROR: return type mismatch in function declared to return emp
517+
DETAIL: Final statement returns text instead of point at column 4.
519518
</computeroutput>
520519
</screen>
521-
As with the base-type case, the function will not insert any casts
522-
automatically.
520+
As with the base-type case, the system will not insert explicit
521+
casts automatically, only implicit or assignment casts.
523522
</para>
524523
</listitem>
525524
</itemizedlist>
@@ -542,8 +541,7 @@ $$ LANGUAGE SQL;
542541
Another example is that if we are trying to write a function that
543542
returns a domain over composite, rather than a plain composite type,
544543
it is always necessary to write it as returning a single column,
545-
since there is no other way to produce a value that is exactly of
546-
the domain type.
544+
since there is no way to cause a coercion of the whole row result.
547545
</para>
548546

549547
<para>
@@ -1263,7 +1261,7 @@ SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;
12631261
Without the typecast, you will get errors like this:
12641262
<screen>
12651263
<computeroutput>
1266-
ERROR: could not determine polymorphic type because input has type "unknown"
1264+
ERROR: could not determine polymorphic type because input has type unknown
12671265
</computeroutput>
12681266
</screen>
12691267
</para>

src/backend/catalog/pg_proc.c

+8-3
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
923923
* verify the result type.
924924
*/
925925
SQLFunctionParseInfoPtr pinfo;
926+
Oid rettype;
927+
TupleDesc rettupdesc;
926928

927929
/* But first, set up parameter information */
928930
pinfo = prepare_sql_fn_parse_info(tuple, NULL, InvalidOid);
@@ -943,9 +945,12 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
943945
}
944946

945947
check_sql_fn_statements(querytree_list);
946-
(void) check_sql_fn_retval(funcoid, proc->prorettype,
947-
querytree_list,
948-
NULL, NULL);
948+
949+
(void) get_func_result_type(funcoid, &rettype, &rettupdesc);
950+
951+
(void) check_sql_fn_retval(querytree_list,
952+
rettype, rettupdesc,
953+
false, NULL);
949954
}
950955

951956
error_context_stack = sqlerrcontext.previous;

0 commit comments

Comments
 (0)