diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c index 881950e5264..9b694104aa3 100644 --- a/src/backend/optimizer/plan/analyzejoins.c +++ b/src/backend/optimizer/plan/analyzejoins.c @@ -1280,8 +1280,9 @@ rel_is_distinct_for(PlannerInfo *root, RelOptInfo *rel, List *clause_list, bool query_supports_distinctness(Query *query) { - /* SRFs break distinctness except with DISTINCT, see below */ - if (query->hasTargetSRFs && query->distinctClause == NIL) + /* SRFs break distinctness except with plain DISTINCT, see below */ + if (query->hasTargetSRFs && + (query->distinctClause == NIL || query->hasDistinctOn)) return false; /* check for features we can prove distinctness with */ @@ -1333,10 +1334,15 @@ query_is_distinct_for(Query *query, List *distinct_cols) /* * DISTINCT (including DISTINCT ON) guarantees uniqueness if all the * columns in the DISTINCT clause appear in colnos and operator semantics - * match. This is true even if there are SRFs in the DISTINCT columns or - * elsewhere in the tlist. + * match. With plain DISTINCT this is true even if there are SRFs in the + * tlist, since they are all DISTINCT columns and hence get expanded + * before the Unique step. But with DISTINCT ON, the planner may postpone + * SRFs that are not DISTINCT ON or ORDER BY columns until after the + * Unique step, which can produce duplicates of the DISTINCT ON columns; + * so we can't rely on DISTINCT ON if there are any tlist SRFs. */ - if (query->distinctClause) + if (query->distinctClause && + !(query->hasTargetSRFs && query->hasDistinctOn)) { foreach(l, query->distinctClause) { diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 05f359d3aa7..deca5617ac8 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -6497,6 +6497,39 @@ select d.* from d left join (select distinct * from b) s -> Seq Scan on d (9 rows) +-- join removal is not possible when the subquery has DISTINCT ON and a +-- set-returning function that is not a DISTINCT ON column +explain (costs off) +select d.* from d left join + (select distinct on (id) id, generate_series(1, 2) as g from b order by id) s + on d.a = s.id + order by 1, 2; + QUERY PLAN +----------------------------------------------------------------------- + Sort + Sort Key: d.a, d.b + -> Hash Left Join + Hash Cond: (d.a = s.id) + -> Seq Scan on d + -> Hash + -> Subquery Scan on s + -> ProjectSet + -> Unique + -> Index Only Scan using b_pkey on b +(10 rows) + +select d.* from d left join + (select distinct on (id) id, generate_series(1, 2) as g from b order by id) s + on d.a = s.id + order by 1, 2; + a | b +---+--- + 1 | 3 + 1 | 3 + 2 | 2 + 3 | 1 +(4 rows) + -- join removal is not possible here explain (costs off) select 1 from a t1 diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 450bd5bbf2c..c0ff6c945b0 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -2385,6 +2385,18 @@ explain (costs off) select d.* from d left join (select distinct * from b) s on d.a = s.id; +-- join removal is not possible when the subquery has DISTINCT ON and a +-- set-returning function that is not a DISTINCT ON column +explain (costs off) +select d.* from d left join + (select distinct on (id) id, generate_series(1, 2) as g from b order by id) s + on d.a = s.id + order by 1, 2; +select d.* from d left join + (select distinct on (id) id, generate_series(1, 2) as g from b order by id) s + on d.a = s.id + order by 1, 2; + -- join removal is not possible here explain (costs off) select 1 from a t1