From b2c51e6eba1d8b3b139c529df3703b37952b5a3d Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 21 Jul 2009 02:02:44 +0000
Subject: [PATCH] Fix another semijoin-ordering bug.  We already knew that we
 couldn't reorder a semijoin into or out of the righthand side of another
 semijoin, but actually it doesn't work to reorder it into or out of the
 righthand side of a left or antijoin, either.  Per bug #4906 from Mathieu
 Fenniak.

This was sloppy thinking on my part.  This identity does work:

	( A left join B on (Pab) ) semijoin C on (Pac)
==
	( A semijoin C on (Pac) ) left join B on (Pab)

but I failed to see that that doesn't mean this does:

	( A left join B on (Pab) ) semijoin C on (Pbc)
!=
	A left join ( B semijoin C on (Pbc) ) on (Pab)
---
 src/backend/optimizer/README           | 10 +++++-----
 src/backend/optimizer/plan/initsplan.c | 11 ++++++-----
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index a3ce21b690e..f406e642db3 100644
--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/optimizer/README,v 1.49 2009/02/27 22:41:37 tgl Exp $
+$PostgreSQL: pgsql/src/backend/optimizer/README,v 1.50 2009/07/21 02:02:44 tgl Exp $
 
 Optimizer
 =========
@@ -214,10 +214,10 @@ out of the nullable side of an outer join:
 	!= (A leftjoin B on (Pab)) join C on (Pbc)
 
 SEMI joins work a little bit differently.  A semijoin can be reassociated
-into or out of the lefthand side of another semijoin, but not into or out
-of the righthand side.  Likewise, an inner join, left join, or antijoin
-can be reassociated into or out of the lefthand side of a semijoin, but
-not into or out of the righthand side.
+into or out of the lefthand side of another semijoin, left join, or
+antijoin, but not into or out of the righthand side.  Likewise, an inner
+join, left join, or antijoin can be reassociated into or out of the
+lefthand side of a semijoin, but not into or out of the righthand side.
 
 ANTI joins work approximately like LEFT joins, except that identity 3
 fails if the join to C is an antijoin (even if Pbc is strict, and in
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 8a189d4443d..9ea928ed9d4 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.154 2009/06/11 14:48:59 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.155 2009/07/21 02:02:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -630,8 +630,8 @@ make_outerjoininfo(PlannerInfo *root,
 		 * min_lefthand + min_righthand.  This is because there might be other
 		 * OJs below this one that this one can commute with, but we cannot
 		 * commute with them if we don't with this one.)  Also, if the current
-		 * join is an antijoin, we must preserve ordering regardless of
-		 * strictness.
+		 * join is a semijoin or antijoin, we must preserve ordering
+		 * regardless of strictness.
 		 *
 		 * Note: I believe we have to insist on being strict for at least one
 		 * rel in the lower OJ's min_righthand, not its whole syn_righthand.
@@ -639,7 +639,7 @@ make_outerjoininfo(PlannerInfo *root,
 		if (bms_overlap(left_rels, otherinfo->syn_righthand))
 		{
 			if (bms_overlap(clause_relids, otherinfo->syn_righthand) &&
-				(jointype == JOIN_ANTI ||
+				(jointype == JOIN_SEMI || jointype == JOIN_ANTI ||
 				 !bms_overlap(strict_relids, otherinfo->min_righthand)))
 			{
 				min_lefthand = bms_add_members(min_lefthand,
@@ -655,7 +655,7 @@ make_outerjoininfo(PlannerInfo *root,
 		 * can interchange the ordering of the two OJs; otherwise we must add
 		 * lower OJ's full syntactic relset to min_righthand.  Here, we must
 		 * preserve ordering anyway if either the current join is a semijoin,
-		 * or the lower OJ is an antijoin.
+		 * or the lower OJ is either a semijoin or an antijoin.
 		 *
 		 * Here, we have to consider that "our join condition" includes any
 		 * clauses that syntactically appeared above the lower OJ and below
@@ -672,6 +672,7 @@ make_outerjoininfo(PlannerInfo *root,
 		{
 			if (bms_overlap(clause_relids, otherinfo->syn_righthand) ||
 				jointype == JOIN_SEMI ||
+				otherinfo->jointype == JOIN_SEMI ||
 				otherinfo->jointype == JOIN_ANTI ||
 				!otherinfo->lhs_strict || otherinfo->delay_upper_joins)
 			{
-- 
GitLab