From 633152ec443469a5a6611f7005db2b52474a60f7 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Tue, 7 Feb 2017 17:25:25 +0530
Subject: [PATCH 10/14] Parameterized path fixes.

We do not create merge or hash join paths when the inner path is parameterized
by the outer and vice-versa. Parameterization information in path refers to the
top-most parent relation. Current tests (PATH_PARAM_BY_REL) to avoid joining
such paths fail while joining child relations; the paths from either child may
be paramterized by other's parent. Modify the tests to consider paths
parameterized by parent as parameterized by any of its child.

If the inner path is parameterized by outer path, we can create a nested loop
join using those two paths with inner relation parameterized by the outer
relation. For LATERAL JOINs this is the only legal way to plan a join. In case
of partitioned joins, the lateral references refer to the topmost parent and
hence inner paths are parameterized by the topmost parent. In such cases, it's
possible to translate the inner path to be parameterized by
the child and create nested loop join. When presented with a pair of child
relation paths, where the inner paths is parameterized by the parent of outer
child, this patch translates the path to be parameterized by the outer child
and creates a nested loop join path.

The function reparameterize_path_by_child() needs to call adjust_relid_set() to
substitute parent relids by child relids in Path::param_info::ppi_req_outer.
Hence "extern"alized that function. Since there is already another
static adjust_relid_set() in rewriteManip.c, renamed this one to
adjust_child_relids().

Also "extern"alized find_param_path_info() required by
reparameterize_path_by_child().
---
 src/backend/optimizer/path/joinpath.c  |   33 +++++-
 src/backend/optimizer/prep/prepunion.c |   42 ++++----
 src/backend/optimizer/util/pathnode.c  |  182 ++++++++++++++++++++++++++++++++
 src/backend/optimizer/util/relnode.c   |    2 -
 src/include/optimizer/pathnode.h       |    4 +
 src/include/optimizer/prep.h           |    1 +
 6 files changed, 239 insertions(+), 25 deletions(-)

diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index f80fb25..4d4a183 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -25,9 +25,19 @@
 /* Hook for plugins to get control in add_paths_to_joinrel() */
 set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
 
-#define PATH_PARAM_BY_REL(path, rel)  \
+/*
+ * Paths parameterized by the parent can be considered to be parameterized by
+ * any of its child.
+ */
+#define PATH_PARAM_BY_PARENT(path, rel)	\
+	((path)->param_info && bms_overlap(PATH_REQ_OUTER(path),	\
+									   (rel)->top_parent_relids))
+#define PATH_PARAM_BY_REL_SELF(path, rel)  \
 	((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))
 
+#define PATH_PARAM_BY_REL(path, rel)	\
+	(PATH_PARAM_BY_REL_SELF(path, rel) || PATH_PARAM_BY_PARENT(path, rel))
+
 static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
 					 RelOptInfo *outerrel, RelOptInfo *innerrel,
 					 JoinType jointype, JoinPathExtraData *extra);
@@ -301,6 +311,27 @@ try_nestloop_path(PlannerInfo *root,
 	JoinCostWorkspace workspace;
 
 	/*
+	 * Since result produced by a child is part of the result produced by its
+	 * topmost parent and has same properties, the parameters representing that
+	 * parent may be substituted by values from a child. Hence expressions and
+	 * hence paths using those expressions, parameterized by a parent can be
+	 * said to be parameterized by any of its child.  For a join between child
+	 * relations, if the inner path is parameterized by the parent of the outer
+	 * relation,  create a nestloop join path with inner relation parameterized
+	 * by the outer relation by translating the inner path to be parameterized
+	 * by the outer child relation.
+	 */
+	if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent))
+	{
+		inner_path = reparameterize_path_by_child(root, inner_path,
+												   outer_path->parent);
+
+		/* If we could not translate the path, don't produce nest loop path. */
+		if (!inner_path)
+			return;
+	}
+
+	/*
 	 * Check to see if proposed path is still parameterized, and reject if the
 	 * parameterization wouldn't be sensible --- unless allow_star_schema_join
 	 * says to allow it anyway.  Also, we must reject if have_dangerous_phv
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 676204f..d459e95 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -109,7 +109,6 @@ static Node *adjust_appendrel_attrs_mutator(Node *node,
 							   adjust_appendrel_attrs_context *context);
 static List *adjust_inherited_tlist(List *tlist,
 					   AppendRelInfo *context);
-static Relids adjust_relid_set(Relids relids, List *append_rel_infos);
 
 
 /*
@@ -1951,7 +1950,7 @@ adjust_appendrel_attrs_mutator(Node *node,
 														 (void *) context);
 		/* now fix PlaceHolderVar's relid sets */
 		if (phv->phlevelsup == 0)
-			phv->phrels = adjust_relid_set(phv->phrels, context->appinfos);
+			phv->phrels = adjust_child_relids(phv->phrels, context->appinfos);
 		return (Node *) phv;
 	}
 	/* Shouldn't need to handle planner auxiliary nodes here */
@@ -1982,17 +1981,17 @@ adjust_appendrel_attrs_mutator(Node *node,
 			adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
 
 		/* adjust relid sets too */
-		newinfo->clause_relids = adjust_relid_set(oldinfo->clause_relids,
+		newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,
 													 context->appinfos);
-		newinfo->required_relids = adjust_relid_set(oldinfo->required_relids,
+		newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,
 													 context->appinfos);
-		newinfo->outer_relids = adjust_relid_set(oldinfo->outer_relids,
+		newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,
 													 context->appinfos);
-		newinfo->nullable_relids = adjust_relid_set(oldinfo->nullable_relids,
+		newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,
 													   context->appinfos);
-		newinfo->left_relids = adjust_relid_set(oldinfo->left_relids,
+		newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,
 												   context->appinfos);
-		newinfo->right_relids = adjust_relid_set(oldinfo->right_relids,
+		newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,
 													context->appinfos);
 
 		/*
@@ -2026,15 +2025,18 @@ adjust_appendrel_attrs_mutator(Node *node,
 
 /*
  * Replace parent relids by child relids in the copy of given relid set
- * according to the given list of AppendRelInfos. The given relid set is
- * returned as is if it contains no parent in the given list, otherwise, the
- * given relid set is not changed.
+ * according to the given list of AppendRelInfos.
  */
 Relids
-adjust_relid_set(Relids relids, List *append_rel_infos)
+adjust_child_relids(Relids relids, List *append_rel_infos)
 {
 	ListCell   *lc;
-	Bitmapset  *result = NULL;
+
+	/*
+	 * The new relids set may be expected to be in a memory context different
+	 * from the given one. Make a copy here.
+	 */
+	Bitmapset  *result = bms_copy(relids);
 
 	foreach (lc, append_rel_infos)
 	{
@@ -2043,10 +2045,6 @@ adjust_relid_set(Relids relids, List *append_rel_infos)
 		/* Remove parent, add child */
 		if (bms_is_member(appinfo->parent_relid, relids))
 		{
-			/* Make a copy if we are changing the set. */
-			if (!result)
-				result = bms_copy(relids);
-
 			result = bms_del_member(result, appinfo->parent_relid);
 			result = bms_add_member(result, appinfo->child_relid);
 		}
@@ -2202,7 +2200,7 @@ build_child_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo,
 	RestrictInfo   *child_rinfo;
 	MemoryContext	old_context;
 
-	child_required_relids = adjust_relid_set(rinfo->required_relids,
+	child_required_relids = adjust_child_relids(rinfo->required_relids,
 												append_rel_infos);
 
 
@@ -2313,13 +2311,13 @@ build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
 
 	memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));
 
-	sjinfo->min_lefthand = adjust_relid_set(sjinfo->min_lefthand,
+	sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,
 											   left_appinfos);
-	sjinfo->min_righthand = adjust_relid_set(sjinfo->min_righthand,
+	sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand,
 												right_appinfos);
-	sjinfo->syn_lefthand = adjust_relid_set(sjinfo->syn_lefthand,
+	sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,
 											   left_appinfos);
-	sjinfo->syn_righthand = adjust_relid_set(sjinfo->syn_righthand,
+	sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand,
 												right_appinfos);
 
 	/*
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index d861a49..d1ee237 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -3388,3 +3388,185 @@ reparameterize_path(PlannerInfo *root, Path *path,
 	}
 	return NULL;
 }
+
+/*
+ * reparameterize_path_by_child
+ * 		Given a path parameterized by the parent of the given relation,
+ * 		translate the path to be parameterized by the given child relation.
+ *
+ * The function creates a new path of the same type as the given path, but
+ * parameterized by the given child relation. If it can not reparameterize the
+ * path as required, it returns NULL.
+ *
+ * The cost, number of rows, width and parallel path properties depend upon
+ * path->parent, which does not change during the translation. Hence those
+ * members are copied as they are.
+ */
+
+Path *
+reparameterize_path_by_child(PlannerInfo *root, Path *path,
+							  RelOptInfo *child_rel)
+{
+	Path	   *new_path;
+	ParamPathInfo   *new_ppi;
+	ParamPathInfo   *old_ppi;
+	List	   *child_aris;
+	Relids		required_outer;
+
+	/*
+	 * If the path is not parameterized by parent of the given relation or it it
+	 * doesn't need reparameterization.
+	 */
+	if (!path->param_info ||
+		!bms_overlap(PATH_REQ_OUTER(path), child_rel->top_parent_relids))
+	return path;
+
+	switch (nodeTag(path))
+	{
+		case T_Path:
+			new_path = makeNode(Path);
+			memcpy(new_path, path, sizeof(Path));
+			break;
+
+		case T_HashPath:
+			new_path = (Path *) makeNode(HashPath);
+			memcpy(new_path, path, sizeof(HashPath));
+			break;
+
+		case T_MergePath:
+			new_path = (Path *) makeNode(MergePath);
+			memcpy(new_path, path, sizeof(MergePath));
+			break;
+
+		case T_NestPath:
+			new_path = (Path *) makeNode(NestPath);
+			memcpy(new_path, path, sizeof(NestPath));
+			break;
+
+		case T_IndexPath:
+			new_path = (Path *) makeNode(IndexPath);
+			memcpy(new_path, path, sizeof(IndexPath));
+			break;
+
+		case T_AppendPath:
+			new_path = (Path *) makeNode(AppendPath);
+			memcpy(new_path, path, sizeof(AppendPath));
+			break;
+
+		/*
+		 * TODO:
+		 * If this method of translation is fine add more path types here.
+		 */
+
+		default:
+			/* Path type unsupported by this function. */
+			return NULL;
+	}
+
+	/*
+	 * Gather AppendRelInfos of the base partition relations in the outer child
+	 * relation. We need those for translating parent path to that of child by
+	 * substituting parent Var nodes and relids with those of children.
+	 */
+	child_aris = find_appinfos_by_relids(root, child_rel->relids);
+
+	/* Adjust the parameterization information. */
+	old_ppi = new_path->param_info;
+	required_outer = adjust_child_relids(old_ppi->ppi_req_outer, child_aris);
+
+	/* If we already have a PPI for this parameterization, just return it */
+	new_ppi = find_param_path_info(new_path->parent, required_outer);
+
+	/* If not build a new one and link it to the list of PPIs. */
+	if (!new_ppi)
+	{
+		new_ppi = makeNode(ParamPathInfo);
+		new_ppi->ppi_req_outer = required_outer;
+		new_ppi->ppi_rows = old_ppi->ppi_rows;
+		new_ppi->ppi_clauses = build_child_clauses(root, old_ppi->ppi_clauses,
+												   child_aris);
+		new_path->parent->ppilist = lappend(new_path->parent->ppilist, new_ppi);
+	}
+	else
+		bms_free(required_outer);
+
+	new_path->param_info = new_ppi;
+
+	/*
+	 * Adjust the path target if the parent of the outer relation is referenced
+	 * in the targetlist. This can happen when only the parent of outer relation is
+	 * laterally referenced in this relation.
+	 */
+	if (bms_overlap(path->parent->lateral_relids, child_rel->top_parent_relids))
+	{
+		MemoryContext	old_context;
+
+		/*
+		 * Allocate the target in planner's context, since they are copied as
+		 * is from path while creating plans.
+		 */
+		old_context = MemoryContextSwitchTo(root->planner_cxt);
+		new_path->pathtarget = copy_pathtarget(new_path->pathtarget);
+		new_path->pathtarget->exprs = (List *) adjust_appendrel_attrs(root,
+											(Node *) new_path->pathtarget->exprs,
+																	child_aris);
+		MemoryContextSwitchTo(old_context);
+	}
+
+	/*
+	 * Change parameterization of subpaths recursively. Also carry out any
+	 * pathtype specific adjustments.
+	 */
+	switch (nodeTag(path))
+	{
+		case T_HashPath:
+		case T_MergePath:
+		case T_NestPath:
+			{
+				JoinPath *jpath = (JoinPath *)new_path;
+
+				jpath->outerjoinpath = reparameterize_path_by_child(root,
+														 jpath->outerjoinpath,
+														 child_rel);
+				jpath->innerjoinpath = reparameterize_path_by_child(root,
+														 jpath->innerjoinpath,
+														 child_rel);
+				jpath->joinrestrictinfo = build_child_clauses(root,
+															  jpath->joinrestrictinfo,
+															  child_aris);
+			}
+			break;
+
+		case T_AppendPath:
+			{
+				AppendPath	*apath = (AppendPath *)new_path;
+				List		*subpaths = NIL;
+				ListCell	*lc;
+
+				foreach (lc, apath->subpaths)
+					subpaths = lappend(subpaths,
+									   reparameterize_path_by_child(root,
+																	lfirst(lc),
+																	child_rel));
+				apath->subpaths = subpaths;
+			}
+			break;
+
+		case T_IndexPath:
+			{
+				IndexPath *ipath = (IndexPath *)new_path;
+
+				ipath->indexclauses = build_child_clauses(root, ipath->indexclauses,
+														  child_aris);
+				ipath->indexquals = build_child_clauses(root, ipath->indexquals,
+														child_aris);
+			}
+			break;
+
+		default:
+			/* Nothing to do. */
+			break;
+	}
+
+	return new_path;
+}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 1eed987..46eea02 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -53,8 +53,6 @@ static List *subbuild_joinrel_joinlist(RelOptInfo *joinrel,
 static void set_foreign_rel_properties(RelOptInfo *joinrel,
 						   RelOptInfo *outer_rel, RelOptInfo *inner_rel);
 static void add_join_rel(PlannerInfo *root, RelOptInfo *joinrel);
-extern ParamPathInfo *find_param_path_info(RelOptInfo *rel,
-									  Relids required_outer);
 static void build_joinrel_partition_info(RelOptInfo *joinrel,
 						  RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 						  JoinType jointype);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 81d637a..b9f5b11 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -236,6 +236,8 @@ extern PartitionJoinPath *create_partition_join_path(PlannerInfo *root,
 extern Path *reparameterize_path(PlannerInfo *root, Path *path,
 					Relids required_outer,
 					double loop_count);
+extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path,
+					RelOptInfo *child_rel);
 
 /*
  * prototypes for relnode.c
@@ -277,5 +279,7 @@ extern ParamPathInfo *get_appendrel_parampathinfo(RelOptInfo *appendrel,
 extern RelOptInfo *build_child_join_rel(PlannerInfo *root,
 						 RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 						 RelOptInfo *parent_joinrel, JoinType jointype);
+extern ParamPathInfo *find_param_path_info(RelOptInfo *rel,
+									  Relids required_outer);
 
 #endif   /* PATHNODE_H */
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index 5832130..0347b37 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -65,5 +65,6 @@ extern List *find_appinfos_by_relids(PlannerInfo *root, Relids relids);
 extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
 									SpecialJoinInfo *parent_sjinfo,
 									Relids left_relids, Relids right_relids);
+extern Relids adjust_child_relids(Relids relids, List *append_rel_infos);
 
 #endif   /* PREP_H */
-- 
1.7.9.5

