Skip to content

Commit 729fe69

Browse files
committed
postgres_fdw: Avoid pulling up restrict infos from subqueries
Semi-join joins below left/right join are deparsed as subqueries. Thus, we can't refer to subqueries vars from upper relations. This commit avoids pulling conditions from them. Reported-by: Robins Tharakan <tharakan@gmail.com> Bug: #18852 Discussion: https://postgr.es/m/CAEP4nAzryLd3gwcUpFBAG9MWyDfMRX8ZjuyY2XXjyC_C6k%2B_Zw%40mail.gmail.com Author: Alexander Pyhalov <a.pyhalov@postgrespro.ru> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Backpatch-through: 17
1 parent 302ce5b commit 729fe69

File tree

3 files changed

+79
-8
lines changed

3 files changed

+79
-8
lines changed

contrib/postgres_fdw/expected/postgres_fdw.out

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4926,6 +4926,44 @@ SELECT ft2.*, ft4.* FROM ft2 INNER JOIN
49264926
----+----+----+----+----+----+----+----+----+----+----
49274927
(0 rows)
49284928

4929+
-- Semi-join conditions shouldn't pop up as left/right join clauses.
4930+
SET enable_material TO off;
4931+
EXPLAIN (verbose, costs off)
4932+
SELECT x1.c1 FROM
4933+
(SELECT * FROM ft2 WHERE EXISTS (SELECT 1 FROM ft4 WHERE ft4.c1 = ft2.c1 AND ft2.c2 < 10)) x1
4934+
RIGHT JOIN
4935+
(SELECT * FROM ft2 WHERE EXISTS (SELECT 1 FROM ft4 WHERE ft4.c1 = ft2.c1 AND ft2.c2 < 10)) x2
4936+
ON (x1.c1 = x2.c1)
4937+
ORDER BY x1.c1 LIMIT 10;
4938+
QUERY PLAN
4939+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4940+
Foreign Scan
4941+
Output: ft2.c1
4942+
Relations: ((public.ft2 ft2_1) LEFT JOIN ((public.ft2) SEMI JOIN (public.ft4))) SEMI JOIN (public.ft4 ft4_1)
4943+
Remote SQL: SELECT s9.c1 FROM ("S 1"."T 1" r6 LEFT JOIN (SELECT r4."C 1" FROM "S 1"."T 1" r4 WHERE ((r4.c2 < 10)) AND EXISTS (SELECT NULL FROM "S 1"."T 3" r5 WHERE ((r4."C 1" = r5.c1)))) s9(c1) ON (((s9.c1 = r6."C 1")))) WHERE ((r6.c2 < 10)) AND EXISTS (SELECT NULL FROM "S 1"."T 3" r7 WHERE ((r6."C 1" = r7.c1))) ORDER BY s9.c1 ASC NULLS LAST LIMIT 10::bigint
4944+
(4 rows)
4945+
4946+
SELECT x1.c1 FROM
4947+
(SELECT * FROM ft2 WHERE EXISTS (SELECT 1 FROM ft4 WHERE ft4.c1 = ft2.c1 AND ft2.c2 < 10)) x1
4948+
RIGHT JOIN
4949+
(SELECT * FROM ft2 WHERE EXISTS (SELECT 1 FROM ft4 WHERE ft4.c1 = ft2.c1 AND ft2.c2 < 10)) x2
4950+
ON (x1.c1 = x2.c1)
4951+
ORDER BY x1.c1 LIMIT 10;
4952+
c1
4953+
----
4954+
2
4955+
4
4956+
6
4957+
8
4958+
10
4959+
12
4960+
14
4961+
16
4962+
18
4963+
20
4964+
(10 rows)
4965+
4966+
RESET enable_material;
49294967
-- Can't push down semi-join with inner rel vars in targetlist
49304968
EXPLAIN (verbose, costs off)
49314969
SELECT ft1.c1 FROM ft1 JOIN ft2 on ft1.c1 = ft2.c1 WHERE

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5953,17 +5953,33 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
59535953
break;
59545954

59555955
case JOIN_LEFT:
5956-
fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5957-
fpinfo_i->remote_conds);
5958-
fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5959-
fpinfo_o->remote_conds);
5956+
5957+
/*
5958+
* When semi-join is involved in the inner or outer part of the
5959+
* left join, it's deparsed as a subquery, and we can't refer to
5960+
* its vars on the upper level.
5961+
*/
5962+
if (bms_is_empty(fpinfo_i->hidden_subquery_rels))
5963+
fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5964+
fpinfo_i->remote_conds);
5965+
if (bms_is_empty(fpinfo_o->hidden_subquery_rels))
5966+
fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5967+
fpinfo_o->remote_conds);
59605968
break;
59615969

59625970
case JOIN_RIGHT:
5963-
fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5964-
fpinfo_o->remote_conds);
5965-
fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5966-
fpinfo_i->remote_conds);
5971+
5972+
/*
5973+
* When semi-join is involved in the inner or outer part of the
5974+
* right join, it's deparsed as a subquery, and we can't refer to
5975+
* its vars on the upper level.
5976+
*/
5977+
if (bms_is_empty(fpinfo_o->hidden_subquery_rels))
5978+
fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5979+
fpinfo_o->remote_conds);
5980+
if (bms_is_empty(fpinfo_i->hidden_subquery_rels))
5981+
fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5982+
fpinfo_i->remote_conds);
59675983
break;
59685984

59695985
case JOIN_SEMI:

contrib/postgres_fdw/sql/postgres_fdw.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,23 @@ SELECT ft2.*, ft4.* FROM ft2 INNER JOIN
14551455
WHERE ft2.c1 > 900
14561456
ORDER BY ft2.c1 LIMIT 10;
14571457

1458+
-- Semi-join conditions shouldn't pop up as left/right join clauses.
1459+
SET enable_material TO off;
1460+
EXPLAIN (verbose, costs off)
1461+
SELECT x1.c1 FROM
1462+
(SELECT * FROM ft2 WHERE EXISTS (SELECT 1 FROM ft4 WHERE ft4.c1 = ft2.c1 AND ft2.c2 < 10)) x1
1463+
RIGHT JOIN
1464+
(SELECT * FROM ft2 WHERE EXISTS (SELECT 1 FROM ft4 WHERE ft4.c1 = ft2.c1 AND ft2.c2 < 10)) x2
1465+
ON (x1.c1 = x2.c1)
1466+
ORDER BY x1.c1 LIMIT 10;
1467+
SELECT x1.c1 FROM
1468+
(SELECT * FROM ft2 WHERE EXISTS (SELECT 1 FROM ft4 WHERE ft4.c1 = ft2.c1 AND ft2.c2 < 10)) x1
1469+
RIGHT JOIN
1470+
(SELECT * FROM ft2 WHERE EXISTS (SELECT 1 FROM ft4 WHERE ft4.c1 = ft2.c1 AND ft2.c2 < 10)) x2
1471+
ON (x1.c1 = x2.c1)
1472+
ORDER BY x1.c1 LIMIT 10;
1473+
RESET enable_material;
1474+
14581475
-- Can't push down semi-join with inner rel vars in targetlist
14591476
EXPLAIN (verbose, costs off)
14601477
SELECT ft1.c1 FROM ft1 JOIN ft2 on ft1.c1 = ft2.c1 WHERE

0 commit comments

Comments
 (0)