>From f6de7eabdeaebd8272126b210c2a0c3d93285004 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 26 Nov 2015 10:02:54 +0900
Subject: [PATCH] Refactor create_rinfo_from_check_constr so that it reflects
 what should be done exactly.

---
 src/backend/optimizer/path/joinpath.c | 161 ++++++++++++++++++----------------
 src/include/optimizer/plancat.h       |   4 +
 2 files changed, 91 insertions(+), 74 deletions(-)

diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 6dec33c..8764e98 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -16,6 +16,7 @@
 
 #include <math.h>
 
+#include "access/sysattr.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "nodes/nodeFuncs.h"
@@ -27,14 +28,9 @@
 #include "optimizer/plancat.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/var.h"
 #include "utils/lsyscache.h"
 
-typedef struct
-{
-	List	*joininfo;
-	bool	 is_substituted;
-} substitution_node_context;
-
 /* Hook for plugins to get control in add_paths_to_joinrel() */
 set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
 
@@ -1506,77 +1502,89 @@ select_mergejoin_clauses(PlannerInfo *root,
 }
 
 /*
- * Try to substitute Var node according to join conditions.
- * This process is from following steps.
- *
- * 1. Try to find whether Var node matches to left/right Var node of
- *    one join condition.
- * 2. If found, replace Var node with the opposite expression node of
- *    the join condition.
- *
- * For example, let's assume that we have following expression and
- * join condition.
- * Expression       : A.num % 4 = 1
- * Join condition   : A.num = B.data + 2
- * In this case, we can get following expression.
- *    (B.data + 2) % 4 = 1
+ * Substitute vars with corresponding nodes.
  */
 static Node *
-substitute_node_with_join_cond(Node *node, substitution_node_context *context)
+substitute_nodes(Node *node, List *replacements)
 {
-	/* Failed to substitute. Abort. */
-	if (!context->is_substituted)
-		return (Node *) copyObject(node);
-
 	if (node == NULL)
 		return NULL;
 
 	if (IsA(node, Var))
 	{
-		List		*join_cond = context->joininfo;
 		ListCell	*lc;
+		Node		*replacement = NULL;
 
-		Assert(list_length(join_cond) > 0);
+		Assert(list_length(replacements) > 0);
 
-		foreach (lc, join_cond)
+		foreach (lc, replacements)
 		{
-			RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
-			Expr *expr = rinfo->clause;
+			List *ent = (List *) lfirst(lc);
+			Var *target = (Var *) linitial(ent);
 
-			/*
-			 * Make sure whether OpExpr of Join clause means "=".
-			 */
-			if (!rinfo->can_join ||
-				!IsA(expr, OpExpr) ||
-				!op_hashjoinable(((OpExpr *) expr)->opno,
-								exprType(get_leftop(expr))))
+			if (!equal(target, node))
 				continue;
 
-			if (equal(get_leftop(expr), node))
-			{
-				/*
-				 * This node is equal to LEFT node of join condition,
-				 * thus will be replaced with RIGHT clause.
-				 */
-				return (Node *) copyObject(get_rightop(expr));
-			}
-			else
-			if (equal(get_rightop(expr), node))
-			{
-				/*
-				 * This node is equal to RIGHT node of join condition,
-				 * thus will be replaced with LEFT clause.
-				 */
-				return (Node *) copyObject(get_leftop(expr));
-			}
+			replacement = (Node *) lsecond(ent);
+			break;
 		}
 
-		/* Unfortunately, substituting is failed. */
-		context->is_substituted = false;
-		return (Node *) copyObject(node);
+		/* All vars must be replaced  */
+		Assert(replacement != NULL);
+
+		return replacement;
 	}
 
-	return expression_tree_mutator(node, substitute_node_with_join_cond, context);
+	return expression_tree_mutator(node, substitute_nodes, replacements);
+}
+
+/*
+ * Extract replacements for the relation from joinoinfo
+ */
+static List *
+extract_replacements(List *joininfo, Index relid, Bitmapset **attnos)
+{
+	List	 *substitutes = NIL;
+	ListCell *lc;
+
+	/*
+	 * clauses in joininfo are assumed to be in conjunction so any of them can
+	 * be a substitution independently from others.
+	 */
+	foreach (lc, joininfo)
+	{
+		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		Expr *expr = rinfo->clause;
+		Var *leftop = (Var *)get_leftop(expr);
+		Var *rightop = (Var *)get_rightop(expr);
+
+		/*
+		 * We could solve join expression for the relation, but we don't and
+		 * just find matching vars in either side for now.
+		 */
+		if (!rinfo->can_join ||
+			!IsA(expr, OpExpr) ||
+			!(IsA(leftop, Var) || IsA(rightop, Var)) ||
+			!op_hashjoinable(((OpExpr *) expr)->opno, exprType((Node *)leftop)))
+			continue;
+
+		if (IsA(leftop, Var) && leftop->varno == relid)
+		{
+			substitutes = lappend(substitutes, 
+								  list_make2(leftop, rightop));
+			*attnos = bms_add_member(*attnos,
+					 leftop->varattno - FirstLowInvalidHeapAttributeNumber);
+		}
+		else if (IsA(rightop, Var) && rightop->varno == relid)
+		{
+			substitutes = lappend(substitutes, 
+								  list_make2(rightop, leftop));
+			*attnos = bms_add_member(*attnos,
+					 rightop->varattno - FirstLowInvalidHeapAttributeNumber);
+		}
+	}
+
+	return substitutes;
 }
 
 /*
@@ -1606,8 +1614,11 @@ create_rinfo_from_check_constr(PlannerInfo *root, List *joininfo,
 	List			*check_constr =
 						get_relation_constraints(root, childRTE->relid,
 													outer_rel, false);
+	List			*replacements = NIL;
+
 	ListCell		*lc;
-	substitution_node_context	context;
+	Bitmapset 		*chkattnos = NULL;
+	Bitmapset 		*joinattnos = NULL;
 
 	if (list_length(check_constr) <= 0)
 	{
@@ -1615,26 +1626,28 @@ create_rinfo_from_check_constr(PlannerInfo *root, List *joininfo,
 		return NIL;
 	}
 
-	context.joininfo = joininfo;
-	context.is_substituted = true;
+	pull_varattnos((Node *)check_constr, outer_rel->relid, &chkattnos);
+	replacements =
+		extract_replacements(joininfo, outer_rel->relid, &joinattnos);
 
 	/*
-	 * Try to convert CHECK() constraints to filter expressions.
+	 * exit if the join clauses cannot replace all vars in the check
+	 * constraint
+	 */
+	if (!bms_is_subset(chkattnos, joinattnos))
+		return NULL;
+
+	/*
+	 * Generate filter condition for the inner relation from check constraints
+	 * on the outer relation by substituting outer vars with inner equivalents
+	 * derived from the join condition.
 	 */
 	foreach(lc, check_constr)
 	{
-		Node *substituted =
-				expression_tree_mutator((Node *) lfirst(lc),
-										substitute_node_with_join_cond,
-										(void *) &context);
-
-		if (!context.is_substituted)
-		{
-			*succeed = false;
-			list_free_deep(check_constr);
-			return NIL;
-		}
-		result = lappend(result, substituted);
+		result = lappend(result,
+						 expression_tree_mutator((Node *) lfirst(lc),
+												 substitute_nodes,
+												 (void *) replacements));
 	}
 
 	Assert(list_length(check_constr) == list_length(result));
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index 11e7d4d..07f3b8d 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -35,6 +35,10 @@ extern void estimate_rel_size(Relation rel, int32 *attr_widths,
 
 extern int32 get_relation_data_width(Oid relid, int32 *attr_widths);
 
+extern List *get_relation_constraints(PlannerInfo *root,
+									  Oid relationObjectId, RelOptInfo *rel,
+									  bool include_notnull);
+
 extern bool relation_excluded_by_constraints(PlannerInfo *root,
 								 RelOptInfo *rel, RangeTblEntry *rte);
 
-- 
1.8.3.1

