From fabef3044adbf167e08ff3015923a2ba0dc98546 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 14 May 2005 21:29:23 +0000
Subject: [PATCH] Minor refactoring to eliminate duplicate code and make
 startup a tad faster.

---
 src/backend/executor/nodeMergejoin.c | 369 +++++++++++----------------
 src/include/nodes/execnodes.h        |   6 +-
 2 files changed, 161 insertions(+), 214 deletions(-)

diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index be4a97574e5..6eb2bcc364d 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.72 2005/05/13 21:20:16 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.73 2005/05/14 21:29:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -525,6 +525,86 @@ MJCompare(MergeJoinState *mergestate)
 	return result;
 }
 
+
+/*
+ * Generate a fake join tuple with nulls for the inner tuple,
+ * and return it if it passes the non-join quals.
+ */
+static TupleTableSlot *
+MJFillOuter(MergeJoinState *node)
+{
+	ExprContext *econtext = node->js.ps.ps_ExprContext;
+	List	   *otherqual = node->js.ps.qual;
+
+	ResetExprContext(econtext);
+
+	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
+	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
+
+	if (ExecQual(otherqual, econtext, false))
+	{
+		/*
+		 * qualification succeeded.  now form the desired projection tuple
+		 * and return the slot containing it.
+		 */
+		TupleTableSlot *result;
+		ExprDoneCond isDone;
+
+		MJ_printf("ExecMergeJoin: returning outer fill tuple\n");
+
+		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+
+		if (isDone != ExprEndResult)
+		{
+			node->js.ps.ps_TupFromTlist =
+				(isDone == ExprMultipleResult);
+			return result;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Generate a fake join tuple with nulls for the outer tuple,
+ * and return it if it passes the non-join quals.
+ */
+static TupleTableSlot *
+MJFillInner(MergeJoinState *node)
+{
+	ExprContext *econtext = node->js.ps.ps_ExprContext;
+	List	   *otherqual = node->js.ps.qual;
+
+	ResetExprContext(econtext);
+
+	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
+	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
+
+	if (ExecQual(otherqual, econtext, false))
+	{
+		/*
+		 * qualification succeeded.  now form the desired projection tuple
+		 * and return the slot containing it.
+		 */
+		TupleTableSlot *result;
+		ExprDoneCond isDone;
+
+		MJ_printf("ExecMergeJoin: returning inner fill tuple\n");
+
+		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+
+		if (isDone != ExprEndResult)
+		{
+			node->js.ps.ps_TupFromTlist =
+				(isDone == ExprMultipleResult);
+			return result;
+		}
+	}
+
+	return NULL;
+}
+
+
 /* ----------------------------------------------------------------
  *		ExecMergeTupleDump
  *
@@ -612,33 +692,8 @@ ExecMergeJoin(MergeJoinState *node)
 	econtext = node->js.ps.ps_ExprContext;
 	joinqual = node->js.joinqual;
 	otherqual = node->js.ps.qual;
-
-	switch (node->js.jointype)
-	{
-		case JOIN_INNER:
-		case JOIN_IN:
-			doFillOuter = false;
-			doFillInner = false;
-			break;
-		case JOIN_LEFT:
-			doFillOuter = true;
-			doFillInner = false;
-			break;
-		case JOIN_FULL:
-			doFillOuter = true;
-			doFillInner = true;
-			break;
-		case JOIN_RIGHT:
-			doFillOuter = false;
-			doFillInner = true;
-			break;
-		default:
-			elog(ERROR, "unrecognized join type: %d",
-				 (int) node->js.jointype);
-			doFillOuter = false;	/* keep compiler quiet */
-			doFillInner = false;
-			break;
-	}
+	doFillOuter = node->mj_FillOuter;
+	doFillInner = node->mj_FillInner;
 
 	/*
 	 * Check to see if we're still projecting out tuples from a previous
@@ -707,15 +762,27 @@ ExecMergeJoin(MergeJoinState *node)
 				}
 
 				/* Compute join values and check for unmatchability */
-				if (!MJEvalOuterValues(node) && !doFillOuter)
+				if (MJEvalOuterValues(node))
 				{
-					/* Stay in same state to fetch next outer tuple */
-					node->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER;
+					/* OK to go get the first inner tuple */
+					node->mj_JoinState = EXEC_MJ_INITIALIZE_INNER;
 				}
 				else
 				{
-					/* OK to go get the first inner tuple */
-					node->mj_JoinState = EXEC_MJ_INITIALIZE_INNER;
+					/* Stay in same state to fetch next outer tuple */
+					if (doFillOuter)
+					{
+						/*
+						 * Generate a fake join tuple with nulls for the inner
+						 * tuple, and return it if it passes the non-join
+						 * quals.
+						 */
+						TupleTableSlot *result;
+
+						result = MJFillOuter(node);
+						if (result)
+							return result;
+					}
 				}
 				break;
 
@@ -745,12 +812,7 @@ ExecMergeJoin(MergeJoinState *node)
 				}
 
 				/* Compute join values and check for unmatchability */
-				if (!MJEvalInnerValues(node, innerTupleSlot) && !doFillInner)
-				{
-					/* Stay in same state to fetch next inner tuple */
-					node->mj_JoinState = EXEC_MJ_INITIALIZE_INNER;
-				}
-				else
+				if (MJEvalInnerValues(node, innerTupleSlot))
 				{
 					/*
 					 * OK, we have the initial tuples.	Begin by skipping
@@ -758,6 +820,23 @@ ExecMergeJoin(MergeJoinState *node)
 					 */
 					node->mj_JoinState = EXEC_MJ_SKIP_TEST;
 				}
+				else
+				{
+					/* Stay in same state to fetch next inner tuple */
+					if (doFillInner)
+					{
+						/*
+						 * Generate a fake join tuple with nulls for the outer
+						 * tuple, and return it if it passes the non-join
+						 * quals.
+						 */
+						TupleTableSlot *result;
+
+						result = MJFillInner(node);
+						if (result)
+							return result;
+					}
+				}
 				break;
 
 				/*
@@ -856,37 +935,13 @@ ExecMergeJoin(MergeJoinState *node)
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					node->mj_MatchedInner = true;		/* do it only once */
-
-					ResetExprContext(econtext);
-
-					outerTupleSlot = node->mj_NullOuterTupleSlot;
-					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = node->mj_InnerTupleSlot;
-					econtext->ecxt_innertuple = innerTupleSlot;
+					TupleTableSlot *result;
 
-					if (ExecQual(otherqual, econtext, false))
-					{
-						/*
-						 * qualification succeeded.  now form the desired
-						 * projection tuple and return the slot containing
-						 * it.
-						 */
-						TupleTableSlot *result;
-						ExprDoneCond isDone;
-
-						MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
-						result = ExecProject(node->js.ps.ps_ProjInfo,
-											 &isDone);
+					node->mj_MatchedInner = true;		/* do it only once */
 
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
-					}
+					result = MJFillInner(node);
+					if (result)
+						return result;
 				}
 
 				/*
@@ -961,37 +1016,13 @@ ExecMergeJoin(MergeJoinState *node)
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					node->mj_MatchedOuter = true;		/* do it only once */
+					TupleTableSlot *result;
 
-					ResetExprContext(econtext);
-
-					outerTupleSlot = node->mj_OuterTupleSlot;
-					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = node->mj_NullInnerTupleSlot;
-					econtext->ecxt_innertuple = innerTupleSlot;
-
-					if (ExecQual(otherqual, econtext, false))
-					{
-						/*
-						 * qualification succeeded.  now form the desired
-						 * projection tuple and return the slot containing
-						 * it.
-						 */
-						TupleTableSlot *result;
-						ExprDoneCond isDone;
-
-						MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
-						result = ExecProject(node->js.ps.ps_ProjInfo,
-											 &isDone);
+					node->mj_MatchedOuter = true;		/* do it only once */
 
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
-					}
+					result = MJFillOuter(node);
+					if (result)
+						return result;
 				}
 
 				/*
@@ -1223,37 +1254,13 @@ ExecMergeJoin(MergeJoinState *node)
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					node->mj_MatchedOuter = true;		/* do it only once */
-
-					ResetExprContext(econtext);
-
-					outerTupleSlot = node->mj_OuterTupleSlot;
-					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = node->mj_NullInnerTupleSlot;
-					econtext->ecxt_innertuple = innerTupleSlot;
-
-					if (ExecQual(otherqual, econtext, false))
-					{
-						/*
-						 * qualification succeeded.  now form the desired
-						 * projection tuple and return the slot containing
-						 * it.
-						 */
-						TupleTableSlot *result;
-						ExprDoneCond isDone;
+					TupleTableSlot *result;
 
-						MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
-						result = ExecProject(node->js.ps.ps_ProjInfo,
-											 &isDone);
+					node->mj_MatchedOuter = true;		/* do it only once */
 
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
-					}
+					result = MJFillOuter(node);
+					if (result)
+						return result;
 				}
 
 				/*
@@ -1311,37 +1318,13 @@ ExecMergeJoin(MergeJoinState *node)
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					node->mj_MatchedInner = true;		/* do it only once */
-
-					ResetExprContext(econtext);
-
-					outerTupleSlot = node->mj_NullOuterTupleSlot;
-					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = node->mj_InnerTupleSlot;
-					econtext->ecxt_innertuple = innerTupleSlot;
-
-					if (ExecQual(otherqual, econtext, false))
-					{
-						/*
-						 * qualification succeeded.  now form the desired
-						 * projection tuple and return the slot containing
-						 * it.
-						 */
-						TupleTableSlot *result;
-						ExprDoneCond isDone;
+					TupleTableSlot *result;
 
-						MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
-						result = ExecProject(node->js.ps.ps_ProjInfo,
-											 &isDone);
+					node->mj_MatchedInner = true;		/* do it only once */
 
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
-					}
+					result = MJFillInner(node);
+					if (result)
+						return result;
 				}
 
 				/*
@@ -1402,37 +1385,13 @@ ExecMergeJoin(MergeJoinState *node)
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					node->mj_MatchedInner = true;		/* do it only once */
-
-					ResetExprContext(econtext);
-
-					outerTupleSlot = node->mj_NullOuterTupleSlot;
-					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = node->mj_InnerTupleSlot;
-					econtext->ecxt_innertuple = innerTupleSlot;
+					TupleTableSlot *result;
 
-					if (ExecQual(otherqual, econtext, false))
-					{
-						/*
-						 * qualification succeeded.  now form the desired
-						 * projection tuple and return the slot containing
-						 * it.
-						 */
-						TupleTableSlot *result;
-						ExprDoneCond isDone;
-
-						MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
-						result = ExecProject(node->js.ps.ps_ProjInfo,
-											 &isDone);
+					node->mj_MatchedInner = true;		/* do it only once */
 
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
-					}
+					result = MJFillInner(node);
+					if (result)
+						return result;
 				}
 
 				/*
@@ -1469,37 +1428,13 @@ ExecMergeJoin(MergeJoinState *node)
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					node->mj_MatchedOuter = true;		/* do it only once */
+					TupleTableSlot *result;
 
-					ResetExprContext(econtext);
-
-					outerTupleSlot = node->mj_OuterTupleSlot;
-					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = node->mj_NullInnerTupleSlot;
-					econtext->ecxt_innertuple = innerTupleSlot;
-
-					if (ExecQual(otherqual, econtext, false))
-					{
-						/*
-						 * qualification succeeded.  now form the desired
-						 * projection tuple and return the slot containing
-						 * it.
-						 */
-						TupleTableSlot *result;
-						ExprDoneCond isDone;
-
-						MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
-						result = ExecProject(node->js.ps.ps_ProjInfo,
-											 &isDone);
+					node->mj_MatchedOuter = true;		/* do it only once */
 
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
-					}
+					result = MJFillOuter(node);
+					if (result)
+						return result;
 				}
 
 				/*
@@ -1601,13 +1536,19 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate)
 	{
 		case JOIN_INNER:
 		case JOIN_IN:
+			mergestate->mj_FillOuter = false;
+			mergestate->mj_FillInner = false;
 			break;
 		case JOIN_LEFT:
+			mergestate->mj_FillOuter = true;
+			mergestate->mj_FillInner = false;
 			mergestate->mj_NullInnerTupleSlot =
 				ExecInitNullTupleSlot(estate,
 						  ExecGetResultType(innerPlanState(mergestate)));
 			break;
 		case JOIN_RIGHT:
+			mergestate->mj_FillOuter = false;
+			mergestate->mj_FillInner = true;
 			mergestate->mj_NullOuterTupleSlot =
 				ExecInitNullTupleSlot(estate,
 						  ExecGetResultType(outerPlanState(mergestate)));
@@ -1622,6 +1563,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate)
 						 errmsg("RIGHT JOIN is only supported with merge-joinable join conditions")));
 			break;
 		case JOIN_FULL:
+			mergestate->mj_FillOuter = true;
+			mergestate->mj_FillInner = true;
 			mergestate->mj_NullOuterTupleSlot =
 				ExecInitNullTupleSlot(estate,
 						  ExecGetResultType(outerPlanState(mergestate)));
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 9d47c17ad23..b92eb1722f1 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.132 2005/05/13 21:20:16 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.133 2005/05/14 21:29:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1031,6 +1031,8 @@ typedef struct NestLoopState
  *		NumClauses		   number of mergejoinable join clauses
  *		Clauses			   info for each mergejoinable clause
  *		JoinState		   current "state" of join.  see execdefs.h
+ *		FillOuter		   true if should emit unjoined outer tuples anyway
+ *		FillInner		   true if should emit unjoined inner tuples anyway
  *		MatchedOuter	   true if found a join match for current outer tuple
  *		MatchedInner	   true if found a join match for current inner tuple
  *		OuterTupleSlot	   slot in tuple table for cur outer tuple
@@ -1051,6 +1053,8 @@ typedef struct MergeJoinState
 	int			mj_NumClauses;
 	MergeJoinClause mj_Clauses;	/* array of length mj_NumClauses */
 	int			mj_JoinState;
+	bool		mj_FillOuter;
+	bool		mj_FillInner;
 	bool		mj_MatchedOuter;
 	bool		mj_MatchedInner;
 	TupleTableSlot *mj_OuterTupleSlot;
-- 
GitLab