diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index f44c9cc8768b0687474adb7ddad74d04e25b1d60..76f76ae0992fb9677a687a59f3f3174cda64e88d 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -10062,11 +10062,13 @@ SELECT ... WHERE CASE WHEN x <> 0 THEN y/x > 1.5 ELSE false END; <note> <para> - As described in <xref linkend="xfunc-volatility">, functions and - operators marked <literal>IMMUTABLE</literal> can be evaluated when - the query is planned rather than when it is executed. This means - that constant parts of a subexpression that is not evaluated during - query execution might still be evaluated during query planning. + As described in <xref linkend="syntax-express-eval">, there are various + situations in which subexpressions of an expression are evaluated at + different times, so that the principle that <quote><token>CASE</token> + evaluates only necessary subexpressions</quote> is not ironclad. For + example a constant <literal>1/0</> subexpression will usually result in + a division-by-zero failure at planning time, even if it's within + a <token>CASE</token> arm that would never be entered at run time. </para> </note> </sect2> diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index bf740e0520529624514fd4180cd1cc94f2f352f3..80d2859ba934379c22499436bfd07e9abe463be2 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -2299,6 +2299,55 @@ SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END; example, it would be better to sidestep the problem by writing <literal>y > 1.5*x</> instead.) </para> + + <para> + <literal>CASE</> is not a cure-all for such issues, however. + One limitation of the technique illustrated above is that it does not + prevent early evaluation of constant subexpressions. + As described in <xref linkend="xfunc-volatility">, functions and + operators marked <literal>IMMUTABLE</literal> can be evaluated when + the query is planned rather than when it is executed. Thus for example +<programlisting> +SELECT CASE WHEN x > 0 THEN x ELSE 1/0 END FROM tab; +</programlisting> + is likely to result in a division-by-zero failure due to the planner + trying to simplify the constant subexpression, + even if every row in the table has <literal>x > 0</> so that the + <literal>ELSE</> arm would never be entered at run time. + </para> + + <para> + While that particular example might seem silly, related cases that don't + obviously involve constants can occur in queries executed within + functions, since the values of function arguments and local variables + can be inserted into queries as constants for planning purposes. + Within <application>PL/pgSQL</> functions, for example, using an + <literal>IF</>-<literal>THEN</>-<literal>ELSE</> statement to protect + a risky computation is much safer than just nesting it in a + <literal>CASE</> expression. + </para> + + <para> + Another limitation of the same kind is that a <literal>CASE</> cannot + prevent evaluation of an aggregate expression contained within it, + because aggregate expressions are computed before other + expressions in a <literal>SELECT</> list or <literal>HAVING</> clause + are considered. For example, the following query can cause a + division-by-zero error despite seemingly having protected against it: +<programlisting> +SELECT CASE WHEN min(employees) > 0 + THEN avg(expenses / employees) + END + FROM departments; +</programlisting> + The <function>min()</> and <function>avg()</> aggregates are computed + concurrently over all the input rows, so if any row + has <structfield>employees</> equal to zero, the division-by-zero error + will occur before there is any opportunity to test the result of + <function>min()</>. Instead, use a <literal>WHERE</> + clause to prevent problematic input rows from + reaching an aggregate function in the first place. + </para> </sect2> </sect1>