]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Doc: improve user docs and code comments about EXISTS(SELECT * ...).
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 27 Feb 2026 20:20:16 +0000 (15:20 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 27 Feb 2026 20:20:16 +0000 (15:20 -0500)
Point out that Postgres automatically optimizes away the target list
of an EXISTS' subquery, except in weird cases such as target lists
containing set-returning functions.  Thus, both common conventions
EXISTS(SELECT * FROM ...) and EXISTS(SELECT 1 FROM ...) are
overhead-free and there's little reason to prefer one over the other.

In the code comments, mention that the SQL spec says that
EXISTS(SELECT * FROM ...) should be interpreted as EXISTS(SELECT
some-literal FROM ...), but we don't choose to do it exactly that way.

Author: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/9b301c70-3909-4f0f-98ca-9e3c4d142f3e@eisentraut.org

doc/src/sgml/func/func-subquery.sgml
src/backend/optimizer/plan/subselect.c

index a9f2b12e48c66a1c14f0cfce6f78d99cd9d3373b..f954f3bf1339ea1d4656cd558cce4ef9db42cdd9 100644 (file)
@@ -70,8 +70,14 @@ EXISTS (<replaceable>subquery</replaceable>)
    and not on the contents of those rows, the output list of the
    subquery is normally unimportant.  A common coding convention is
    to write all <literal>EXISTS</literal> tests in the form
-   <literal>EXISTS(SELECT 1 WHERE ...)</literal>.  There are exceptions to
-   this rule however, such as subqueries that use <token>INTERSECT</token>.
+   <literal>EXISTS(SELECT * FROM ... WHERE ...)</literal>, another common
+   convention is to write <literal>EXISTS(SELECT 1 FROM ... WHERE
+   ...)</literal> or some other dummy constant.  These conventions are
+   actually equivalent in <productname>PostgreSQL</productname>, which
+   will optimize away evaluation of the subquery's output list altogether
+   when it cannot affect the number of rows returned.  (An example
+   that cannot be optimized away is an output list containing a
+   set-returning function, since the function might return zero rows.)
   </para>
 
   <para>
index e6bc7023562cb0b5ae31ded903bc57a31a37c8ff..d7f3cedf3d58660b17df142345f01ddcc715f8b2 100644 (file)
@@ -1643,7 +1643,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
  * Note: by suppressing the targetlist we could cause an observable behavioral
  * change, namely that any errors that might occur in evaluating the tlist
  * won't occur, nor will other side-effects of volatile functions.  This seems
- * unlikely to bother anyone in practice.
+ * unlikely to bother anyone in practice.  Note that any column privileges are
+ * still checked even if the reference is removed here.
+ *
+ * The SQL standard specifies that a SELECT * immediately inside EXISTS
+ * expands to not all columns but an arbitrary literal.  That is kind of the
+ * same idea, but our optimization goes further in that it throws away the
+ * entire targetlist, and not only if it was written as *.
  *
  * Returns true if was able to discard the targetlist, else false.
  */