From: Tom Lane Date: Fri, 27 Feb 2026 20:20:16 +0000 (-0500) Subject: Doc: improve user docs and code comments about EXISTS(SELECT * ...). X-Git-Tag: REL_19_BETA1~983 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=65a3ff8f1be0186a9c207821bcf0f543545a04d9;p=thirdparty%2Fpostgresql.git Doc: improve user docs and code comments about EXISTS(SELECT * ...). 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 Reviewed-by: Tom Lane Discussion: https://postgr.es/m/9b301c70-3909-4f0f-98ca-9e3c4d142f3e@eisentraut.org --- diff --git a/doc/src/sgml/func/func-subquery.sgml b/doc/src/sgml/func/func-subquery.sgml index a9f2b12e48c..f954f3bf133 100644 --- a/doc/src/sgml/func/func-subquery.sgml +++ b/doc/src/sgml/func/func-subquery.sgml @@ -70,8 +70,14 @@ EXISTS (subquery) 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 EXISTS tests in the form - EXISTS(SELECT 1 WHERE ...). There are exceptions to - this rule however, such as subqueries that use INTERSECT. + EXISTS(SELECT * FROM ... WHERE ...), another common + convention is to write EXISTS(SELECT 1 FROM ... WHERE + ...) or some other dummy constant. These conventions are + actually equivalent in PostgreSQL, 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.) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index e6bc7023562..d7f3cedf3d5 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -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. */