From 1ca695db380063d73b0579ffb36a159f8af589bf Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 23 Jul 2009 17:42:06 +0000
Subject: [PATCH] Fix another thinko in join_is_legal's handling of semijoins:
 we have to test for the case that the semijoin was implemented within either
 input by unique-ifying its RHS before we test to see if it appears to match
 the current join situation.  The previous coding would select semijoin logic
 in situations where we'd already unique-ified the RHS and joined it to some
 unrelated relation(s), and then came to join it to the semijoin's LHS.  That
 still gave the right answer as far as the semijoin itself was concerned, but
 would lead to incorrectly examining only an arbitrary one of the matchable
 rows from the unrelated relation(s).  The cause of this thinko was incorrect
 unification of the pre-8.4 logic for IN joins and OUTER joins --- the
 comparable case for outer joins can be handled after making the match test,
 but that's because there is nothing like the unique-ification escape hatch
 for outer joins. Per bug #4934 from Benjamin Reed.

---
 src/backend/optimizer/path/joinrels.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 350fde29c21..49e8a1222aa 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.101 2009/07/19 20:32:48 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.102 2009/07/23 17:42:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -399,6 +399,22 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 			bms_is_subset(sjinfo->min_righthand, rel2->relids))
 			continue;
 
+		/*
+		 * If it's a semijoin and we already joined the RHS to any other
+		 * rels within either input, then we must have unique-ified the RHS
+		 * at that point (see below).  Therefore the semijoin is no longer
+		 * relevant in this join path.
+		 */
+		if (sjinfo->jointype == JOIN_SEMI)
+		{
+			if (bms_is_subset(sjinfo->syn_righthand, rel1->relids) &&
+				!bms_equal(sjinfo->syn_righthand, rel1->relids))
+				continue;
+			if (bms_is_subset(sjinfo->syn_righthand, rel2->relids) &&
+				!bms_equal(sjinfo->syn_righthand, rel2->relids))
+				continue;
+		}
+
 		/*
 		 * If one input contains min_lefthand and the other contains
 		 * min_righthand, then we can perform the SJ at this join.
@@ -491,9 +507,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 			 * We assume that make_outerjoininfo() set things up correctly
 			 * so that we'll only match to some SJ if the join is valid.
 			 * Set flag here to check at bottom of loop.
-			 *
-			 * For a semijoin, assume it's okay if either side fully contains
-			 * the RHS (per the unique-ification case above).
 			 *----------
 			 */
 			if (sjinfo->jointype != JOIN_SEMI &&
@@ -503,12 +516,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 				/* seems OK */
 				Assert(!bms_overlap(joinrelids, sjinfo->min_lefthand));
 			}
-			else if (sjinfo->jointype == JOIN_SEMI &&
-					 (bms_is_subset(sjinfo->syn_righthand, rel1->relids) ||
-					  bms_is_subset(sjinfo->syn_righthand, rel2->relids)))
-			{
-				/* seems OK */
-			}
 			else
 				is_valid_inner = false;
 		}
-- 
GitLab