Home Explore Blog CI



postgresql

14th chunk of `doc/src/sgml/xfunc.sgml`
0a031fe6f0e2272eeabf25920d920920926dc7737433bc4d0000000100000fa4
 until they are all exhausted, and then
     execution continues with the next underlying row.
    </para>

    <para>
     Set-returning functions can be nested in a select list, although that is
     not allowed in <literal>FROM</literal>-clause items.  In such cases, each level
     of nesting is treated separately, as though it were
     a separate <literal>LATERAL ROWS FROM( ... )</literal> item.  For example, in
<programlisting>
SELECT srf1(srf2(x), srf3(y)), srf4(srf5(z)) FROM tab;
</programlisting>
     the set-returning functions <function>srf2</function>, <function>srf3</function>,
     and <function>srf5</function> would be run in lockstep for each row
     of <structname>tab</structname>, and then <function>srf1</function> and <function>srf4</function>
     would be applied in lockstep to each row produced by the lower
     functions.
    </para>

    <para>
     Set-returning functions cannot be used within conditional-evaluation
     constructs, such as <literal>CASE</literal> or <literal>COALESCE</literal>.  For
     example, consider
<programlisting>
SELECT x, CASE WHEN x &gt; 0 THEN generate_series(1, 5) ELSE 0 END FROM tab;
</programlisting>
     It might seem that this should produce five repetitions of input rows
     that have <literal>x &gt; 0</literal>, and a single repetition of those that do
     not; but actually, because <function>generate_series(1, 5)</function> would be
     run in an implicit <literal>LATERAL FROM</literal> item before
     the <literal>CASE</literal> expression is ever evaluated, it would produce five
     repetitions of every input row.  To reduce confusion, such cases produce
     a parse-time error instead.
    </para>

    <note>
     <para>
      If a function's last command is <command>INSERT</command>,
      <command>UPDATE</command>, <command>DELETE</command>, or
      <command>MERGE</command> with <literal>RETURNING</literal>, that command will
      always be executed to completion, even if the function is not declared
      with <literal>SETOF</literal> or the calling query does not fetch all the
      result rows.  Any extra rows produced by the <literal>RETURNING</literal>
      clause are silently dropped, but the commanded table modifications
      still happen (and are all completed before returning from the function).
     </para>
    </note>

    <note>
     <para>
      Before <productname>PostgreSQL</productname> 10, putting more than one
      set-returning function in the same select list did not behave very
      sensibly unless they always produced equal numbers of rows.  Otherwise,
      what you got was a number of output rows equal to the least common
      multiple of the numbers of rows produced by the set-returning
      functions.  Also, nested set-returning functions did not work as
      described above; instead, a set-returning function could have at most
      one set-returning argument, and each nest of set-returning functions
      was run independently.  Also, conditional execution (set-returning
      functions inside <literal>CASE</literal> etc.) was previously allowed,
      complicating things even more.
      Use of the <literal>LATERAL</literal> syntax is recommended when writing
      queries that need to work in older <productname>PostgreSQL</productname> versions,
      because that will give consistent results across different versions.
      If you have a query that is relying on conditional execution of a
      set-returning function, you may be able to fix it by moving the
      conditional test into a custom set-returning function.  For example,
<programlisting>
SELECT x, CASE WHEN y &gt; 0 THEN generate_series(1, z) ELSE 5 END FROM tab;
</programlisting>
      could become
<programlisting>
CREATE FUNCTION case_generate_series(cond bool, start int, fin int, els int)
  RETURNS SETOF int AS $$
BEGIN
  IF cond THEN
    RETURN QUERY SELECT generate_series(start, fin);
  ELSE
    RETURN QUERY SELECT els;
  END IF;
END$$ LANGUAGE plpgsql;

Title: Limitations, Notes, and Historical Behavior of Set-Returning Functions
Summary
This section outlines limitations of set-returning functions, noting they can't be used within conditional constructs like `CASE` or `COALESCE` due to the implicit `LATERAL FROM` behavior. A note clarifies that `INSERT`, `UPDATE`, `DELETE`, or `MERGE` commands with `RETURNING` within a function always execute completely, regardless of the `SETOF` declaration or query fetching all results. The section also details historical behavior changes pre-PostgreSQL 10, where multiple or nested set-returning functions behaved unpredictably. It recommends using `LATERAL` for compatibility and suggests workarounds, such as custom set-returning functions, for conditional execution scenarios.