]> granicus.if.org Git - postgresql/commitdiff
Fix improper interaction of FULL JOINs with lateral references.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 8 Apr 2019 20:09:06 +0000 (16:09 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 8 Apr 2019 20:09:26 +0000 (16:09 -0400)
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 7e19db0c0.
Back-patch to all supported branches, as that was.

Discussion: https://postgr.es/m/15741-276f1f464b3f40eb@postgresql.org

src/backend/optimizer/path/joinrels.c
src/test/regress/expected/rangefuncs.out
src/test/regress/sql/rangefuncs.sql

index e66cf328beaec21f213da975817fccfc97b33071..46623c33c636f9403995fd2c7699d08128358b21 100644 (file)
@@ -623,6 +623,10 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
                                {
                                        SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l);
 
+                                       /* ignore full joins --- their ordering is predetermined */
+                                       if (sjinfo->jointype == JOIN_FULL)
+                                               continue;
+
                                        if (bms_overlap(sjinfo->min_lefthand, join_plus_rhs) &&
                                                !bms_is_subset(sjinfo->min_righthand, join_plus_rhs))
                                        {
@@ -630,15 +634,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
                                                                                                                sjinfo->min_righthand);
                                                more = true;
                                        }
-                                       /* full joins constrain both sides symmetrically */
-                                       if (sjinfo->jointype == JOIN_FULL &&
-                                               bms_overlap(sjinfo->min_righthand, join_plus_rhs) &&
-                                               !bms_is_subset(sjinfo->min_lefthand, join_plus_rhs))
-                                       {
-                                               join_plus_rhs = bms_add_members(join_plus_rhs,
-                                                                                                               sjinfo->min_lefthand);
-                                               more = true;
-                                       }
                                }
                        } while (more);
                        if (bms_overlap(join_plus_rhs, join_lateral_rels))
index 34ca0ef890e693af66c7aa15e81956dde616417e..36a5929113928a7a613b8572bebdb998f180ed2d 100644 (file)
@@ -1391,6 +1391,31 @@ SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
   3 |  3 | 30 | 8
 (45 rows)
 
+-- check handling of FULL JOIN with multiple lateral references (bug #15741)
+SELECT *
+FROM (VALUES (1),(2)) v1(r1)
+    LEFT JOIN LATERAL (
+        SELECT *
+        FROM generate_series(1, v1.r1) AS gs1
+        LEFT JOIN LATERAL (
+            SELECT *
+            FROM generate_series(1, gs1) AS gs2
+            LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE
+        ) AS ss1 ON TRUE
+        FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE
+    ) AS ss0 ON TRUE;
+ r1 | gs1 | gs2 | gs3 | gs4 
+----+-----+-----+-----+-----
+  1 |     |     |     |   1
+  1 |   1 |   1 |   1 |    
+  2 |     |     |     |   1
+  2 |     |     |     |   2
+  2 |   1 |   1 |   1 |    
+  2 |   2 |   1 |   1 |    
+  2 |   2 |   2 |   1 |    
+  2 |   2 |   2 |   2 |    
+(8 rows)
+
 DROP FUNCTION rngfunc_sql(int,int);
 DROP FUNCTION rngfunc_mat(int,int);
 DROP SEQUENCE rngfunc_rescan_seq1;
index fc8ad9a158d7b3029382e216d9b2071a3416886c..5d29d2e40124c0a87913fa31413373e4bfa931f5 100644 (file)
@@ -319,6 +319,21 @@ SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
               LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2)
                                          LEFT JOIN generate_series(r1,2+r2/5) f(i) ON ((r2+i)<100) OFFSET 0) s1;
 
+-- check handling of FULL JOIN with multiple lateral references (bug #15741)
+
+SELECT *
+FROM (VALUES (1),(2)) v1(r1)
+    LEFT JOIN LATERAL (
+        SELECT *
+        FROM generate_series(1, v1.r1) AS gs1
+        LEFT JOIN LATERAL (
+            SELECT *
+            FROM generate_series(1, gs1) AS gs2
+            LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE
+        ) AS ss1 ON TRUE
+        FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE
+    ) AS ss0 ON TRUE;
+
 DROP FUNCTION rngfunc_sql(int,int);
 DROP FUNCTION rngfunc_mat(int,int);
 DROP SEQUENCE rngfunc_rescan_seq1;