Skip to content

Commit c6df3a2

Browse files
committed
Fix improper interaction of FULL JOINs with lateral references.
join_is_legal() needs to reject forming certain outer joins in cases where that would lead the planner down a blind alley. However, it mistakenly supposed that the way to handle full joins was to treat them as applying the same constraints as for left joins, only to both sides. That doesn't work, as shown in bug #15741 from Anthony Skorski: given a lateral reference out of a join that's fully enclosed by a full join, the code would fail to believe that any join ordering is legal, resulting in errors like "failed to build any N-way joins". However, we don't really need to consider full joins at all for this purpose, because we effectively force them to be evaluated in syntactic order, and that order is always legal for lateral references. Hence, get rid of this broken logic for full joins and just ignore them instead. This seems to have been an oversight in commit 7e19db0. Back-patch to all supported branches, as that was. Discussion: https://postgr.es/m/15741-276f1f464b3f40eb@postgresql.org
1 parent 0777696 commit c6df3a2

File tree

3 files changed

+44
-9
lines changed

3 files changed

+44
-9
lines changed

src/backend/optimizer/path/joinrels.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -625,22 +625,17 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
625625
{
626626
SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l);
627627

628+
/* ignore full joins --- their ordering is predetermined */
629+
if (sjinfo->jointype == JOIN_FULL)
630+
continue;
631+
628632
if (bms_overlap(sjinfo->min_lefthand, join_plus_rhs) &&
629633
!bms_is_subset(sjinfo->min_righthand, join_plus_rhs))
630634
{
631635
join_plus_rhs = bms_add_members(join_plus_rhs,
632636
sjinfo->min_righthand);
633637
more = true;
634638
}
635-
/* full joins constrain both sides symmetrically */
636-
if (sjinfo->jointype == JOIN_FULL &&
637-
bms_overlap(sjinfo->min_righthand, join_plus_rhs) &&
638-
!bms_is_subset(sjinfo->min_lefthand, join_plus_rhs))
639-
{
640-
join_plus_rhs = bms_add_members(join_plus_rhs,
641-
sjinfo->min_lefthand);
642-
more = true;
643-
}
644639
}
645640
} while (more);
646641
if (bms_overlap(join_plus_rhs, join_lateral_rels))

src/test/regress/expected/rangefuncs.out

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,31 @@ SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
14071407
3 | 3 | 30 | 8
14081408
(45 rows)
14091409

1410+
-- check handling of FULL JOIN with multiple lateral references (bug #15741)
1411+
SELECT *
1412+
FROM (VALUES (1),(2)) v1(r1)
1413+
LEFT JOIN LATERAL (
1414+
SELECT *
1415+
FROM generate_series(1, v1.r1) AS gs1
1416+
LEFT JOIN LATERAL (
1417+
SELECT *
1418+
FROM generate_series(1, gs1) AS gs2
1419+
LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE
1420+
) AS ss1 ON TRUE
1421+
FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE
1422+
) AS ss0 ON TRUE;
1423+
r1 | gs1 | gs2 | gs3 | gs4
1424+
----+-----+-----+-----+-----
1425+
1 | | | | 1
1426+
1 | 1 | 1 | 1 |
1427+
2 | | | | 1
1428+
2 | | | | 2
1429+
2 | 1 | 1 | 1 |
1430+
2 | 2 | 1 | 1 |
1431+
2 | 2 | 2 | 1 |
1432+
2 | 2 | 2 | 2 |
1433+
(8 rows)
1434+
14101435
DROP FUNCTION foo_sql(int,int);
14111436
DROP FUNCTION foo_mat(int,int);
14121437
DROP SEQUENCE foo_rescan_seq1;

src/test/regress/sql/rangefuncs.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,21 @@ SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
321321
LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2)
322322
LEFT JOIN generate_series(r1,2+r2/5) f(i) ON ((r2+i)<100) OFFSET 0) s1;
323323

324+
-- check handling of FULL JOIN with multiple lateral references (bug #15741)
325+
326+
SELECT *
327+
FROM (VALUES (1),(2)) v1(r1)
328+
LEFT JOIN LATERAL (
329+
SELECT *
330+
FROM generate_series(1, v1.r1) AS gs1
331+
LEFT JOIN LATERAL (
332+
SELECT *
333+
FROM generate_series(1, gs1) AS gs2
334+
LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE
335+
) AS ss1 ON TRUE
336+
FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE
337+
) AS ss0 ON TRUE;
338+
324339
DROP FUNCTION foo_sql(int,int);
325340
DROP FUNCTION foo_mat(int,int);
326341
DROP SEQUENCE foo_rescan_seq1;

0 commit comments

Comments
 (0)