From fb2e5be3de7e1917bce3b1fdd76058aeac13a41f Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Fri, 10 Feb 2017 14:08:16 +0530
Subject: [PATCH 14/14] Multi-level partition-wise join support.

Annotate RelOptInfos of partitioned table by partitioning scheme at all levels
of a partitioning hierarchy.

Also, while creating Merge/Append plans from PartitionJoinPaths flattern
Merge/Append subplans arising from multi-level partitioning hierarchy.

The path to be reparameterized by a child refers to the parameters from the
topmost parent. This parent can be multiple levels away from the given child.
Hence use multi-level expression adjustment routines in
reparameterize_path_by_child().
---
 src/backend/optimizer/plan/createplan.c |   27 ++++-
 src/backend/optimizer/prep/prepunion.c  |   36 +++++--
 src/backend/optimizer/util/pathnode.c   |  175 +++++++++++++++++++++++++++----
 src/backend/optimizer/util/plancat.c    |   16 ---
 src/include/nodes/relation.h            |    6 ++
 5 files changed, 217 insertions(+), 43 deletions(-)

diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 3f1f2c1..f756be6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -30,6 +30,7 @@
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/paths.h"
+#include "optimizer/pathnode.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/plancat.h"
 #include "optimizer/planmain.h"
@@ -4115,6 +4116,18 @@ create_partition_join_plan(PlannerInfo *root, PartitionJoinPath *best_path)
 													  best_path->path.pathkeys,
 														NULL,
 														TOTAL_COST);
+		else if (best_path->reparamterized_by)
+		{
+			child_path = get_cheapest_parameterized_child_path(root,
+															   child_join,
+													best_path->old_param);
+
+			if (!child_path)
+				elog(ERROR, "Could not find a child-join path with required pathkeys or parameterization.");
+
+			child_path = reparameterize_path_by_child(root, child_path,
+												 best_path->reparamterized_by);
+		}
 		else
 			child_path = get_cheapest_parameterized_child_path(root,
 															   child_join,
@@ -4173,7 +4186,19 @@ create_partition_join_plan(PlannerInfo *root, PartitionJoinPath *best_path)
 			}
 		}
 
-		child_plans = lappend(child_plans, child_plan);
+		/* Flatten Merge/Append plans. */
+		if (IsA(child_plan, Append))
+		{
+			Append	   *append = (Append *) child_plan;
+			child_plans = list_concat(child_plans, append->appendplans);
+		}
+		else if (IsA(child_plan, MergeAppend))
+		{
+			MergeAppend	   *mappend = (MergeAppend *) child_plan;
+			child_plans = list_concat(child_plans, mappend->mergeplans);
+		}
+		else
+			child_plans = lappend(child_plans, child_plan);
 
 		/*
 		 * Reset the child_join memory context to reclaim the memory consumed
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 63b45d6..9399050 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -2183,23 +2183,47 @@ adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
  * adjust_appendrel_attrs_multilevel
  *	  Apply Var translations from a toplevel appendrel parent down to a child.
  *
- * In some cases we need to translate expressions referencing a baserel
+ * In some cases we need to translate expressions referencing a parent relation
  * to reference an appendrel child that's multiple levels removed from it.
  */
 Node *
 adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
 								  RelOptInfo *child_rel)
 {
-	AppendRelInfo *appinfo = find_childrel_appendrelinfo(root, child_rel);
-	RelOptInfo *parent_rel = find_base_rel(root, appinfo->parent_relid);
+	List	   *appinfos = find_appinfos_by_relids(root, child_rel->relids);
+	RelOptInfo *parent_rel;
+
+	if (child_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
+	{
+		AppendRelInfo *appinfo = linitial(appinfos);
+		parent_rel = find_base_rel(root, appinfo->parent_relid);
+	}
+	else
+	{
+		Relids		parent_relids = NULL;
+		ListCell   *lc;
+
+		/* Construct relids set for the immediate parent of the given child. */
+		foreach (lc, appinfos)
+		{
+			AppendRelInfo   *appinfo = lfirst(lc);
+
+			parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
+		}
+
+		parent_rel = find_join_rel(root, parent_relids);
+		bms_free(parent_relids);
+	}
 
 	/* If parent is also a child, first recurse to apply its translations */
-	if (parent_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
+	if (IS_OTHER_REL(parent_rel))
 		node = adjust_appendrel_attrs_multilevel(root, node, parent_rel);
 	else
-		Assert(parent_rel->reloptkind == RELOPT_BASEREL);
+		Assert(parent_rel->reloptkind == RELOPT_BASEREL ||
+			   parent_rel->reloptkind == RELOPT_JOINREL);
+
 	/* Now translate for this child */
-	return adjust_appendrel_attrs(root, node, list_make1(appinfo));
+	return adjust_appendrel_attrs(root, node, appinfos);
 }
 
 /*
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index d1ee237..a32ef1a 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -48,6 +48,11 @@ typedef enum
 #define STD_FUZZ_FACTOR 1.01
 
 static List *translate_sub_tlist(List *tlist, int relid);
+static Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
+											 Relids child_relids,
+											 Relids top_parent_relids);
+static List *build_child_clauses_multilevel(PlannerInfo *root, List *clauses,
+								Relids child_relids, Relids top_parent_relids);
 
 
 /*****************************************************************************
@@ -3410,7 +3415,6 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 	Path	   *new_path;
 	ParamPathInfo   *new_ppi;
 	ParamPathInfo   *old_ppi;
-	List	   *child_aris;
 	Relids		required_outer;
 
 	/*
@@ -3453,6 +3457,11 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 			memcpy(new_path, path, sizeof(AppendPath));
 			break;
 
+		case T_PartitionJoinPath:
+			new_path = (Path *) makeNode(PartitionJoinPath);
+			memcpy(new_path, path, sizeof(PartitionJoinPath));
+			break;
+
 		/*
 		 * TODO:
 		 * If this method of translation is fine add more path types here.
@@ -3464,15 +3473,15 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 	}
 
 	/*
-	 * 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.
+	 * Adjust the parameterization information, which refers to the topmost
+	 * parent. The topmost parent can be multiple levels away from the given
+	 * child, hence use multi-level expression adjustment routines.
 	 */
-	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);
+	required_outer = adjust_child_relids_multilevel(root,
+													old_ppi->ppi_req_outer,
+													child_rel->relids,
+												 child_rel->top_parent_relids);
 
 	/* If we already have a PPI for this parameterization, just return it */
 	new_ppi = find_param_path_info(new_path->parent, required_outer);
@@ -3483,8 +3492,10 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 		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_ppi->ppi_clauses = build_child_clauses_multilevel(root,
+														  old_ppi->ppi_clauses,
+															 child_rel->relids,
+												 child_rel->top_parent_relids);
 		new_path->parent->ppilist = lappend(new_path->parent->ppilist, new_ppi);
 	}
 	else
@@ -3500,6 +3511,7 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 	if (bms_overlap(path->parent->lateral_relids, child_rel->top_parent_relids))
 	{
 		MemoryContext	old_context;
+		List	   *exprs;
 
 		/*
 		 * Allocate the target in planner's context, since they are copied as
@@ -3507,9 +3519,12 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 		 */
 		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);
+		exprs = new_path->pathtarget->exprs;
+		exprs = (List *) adjust_appendrel_attrs_multilevel(root,
+														   (Node *) exprs,
+														   child_rel);
+		new_path->pathtarget->exprs = exprs;
+
 		MemoryContextSwitchTo(old_context);
 	}
 
@@ -3531,9 +3546,10 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 				jpath->innerjoinpath = reparameterize_path_by_child(root,
 														 jpath->innerjoinpath,
 														 child_rel);
-				jpath->joinrestrictinfo = build_child_clauses(root,
-															  jpath->joinrestrictinfo,
-															  child_aris);
+				jpath->joinrestrictinfo = build_child_clauses_multilevel(root,
+													  jpath->joinrestrictinfo,
+															child_rel->relids,
+												child_rel->top_parent_relids);
 			}
 			break;
 
@@ -3556,10 +3572,30 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 			{
 				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);
+				ipath->indexclauses = build_child_clauses_multilevel(root,
+														   ipath->indexclauses,
+															 child_rel->relids,
+												 child_rel->top_parent_relids);
+
+				ipath->indexquals = build_child_clauses_multilevel(root,
+															 ipath->indexquals,
+															 child_rel->relids,
+												 child_rel->top_parent_relids);
+			}
+			break;
+
+		case T_PartitionJoinPath:
+			{
+				PartitionJoinPath *pjpath = (PartitionJoinPath *)new_path;
+
+				/*
+				 * The paths for the child-joins covered by PartitionJoinPath will
+				 * be created later. If this PartitionJoinPath was reparameterized
+				 * by child of another relations, we will need that relation to
+				 * reparamterize the child-join paths.
+				 */
+				pjpath->old_param = PATH_REQ_OUTER(path); 
+				pjpath->reparamterized_by = child_rel;
 			}
 			break;
 
@@ -3570,3 +3606,102 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 
 	return new_path;
 }
+
+/*
+ * Replace any relid present in top_parent_relids with its child in
+ * child_relids. Members of child_relids can be multiple levels below top
+ * parent in the partition hierarchy.
+ */
+Relids
+adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
+							   Relids child_relids, Relids top_parent_relids)
+{
+	List	   *appinfos;
+	Relids		parent_relids = NULL;
+	ListCell   *lc;
+	Relids		result;
+	Relids		tmp_result = NULL;
+
+	/*
+	 * If the given relids set doesn't contain any of the top parent relids,
+	 * it will remain unchanged.
+	 */
+	if (!bms_overlap(relids, top_parent_relids))
+		return relids;
+
+	appinfos = find_appinfos_by_relids(root, child_relids);
+
+	/* Construct relids set for the immediate parent of the given child. */
+	foreach (lc, appinfos)
+	{
+		AppendRelInfo   *appinfo = lfirst(lc);
+
+		parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
+	}
+
+	/* Recurse if immediate parent is not the top parent. */
+	if (!bms_equal(parent_relids, top_parent_relids))
+	{
+		tmp_result = adjust_child_relids_multilevel(root, relids,
+													parent_relids,
+													top_parent_relids);
+		relids = tmp_result;
+	}
+
+	result = adjust_child_relids(relids, appinfos);
+
+	/* Free memory consumed by any immediate result. */
+	if (tmp_result)
+		bms_free(tmp_result);
+
+	bms_free(parent_relids);
+	list_free(appinfos);
+	return result;
+}
+
+/*
+ * build_child_clauses_multilevel
+ *		Similar to build_child_clauses but used when the child relation
+ *		represented by child_relids is
+ *		deeper down in the partition hierarchy reprepresented by parent
+ *		relation with relids top_parent_relids.
+ */
+List *
+build_child_clauses_multilevel(PlannerInfo *root, List *clauses,
+							   Relids child_relids, Relids top_parent_relids)
+{
+	List	   *appinfos;
+	Relids		parent_relids = NULL;
+	ListCell   *lc;
+	List	   *result;
+	List	   *tmp_result = NIL;
+
+	appinfos = find_appinfos_by_relids(root, child_relids);
+
+	/* Construct relids set for the immediate parent of the given child. */
+	foreach (lc, appinfos)
+	{
+		AppendRelInfo   *appinfo = lfirst(lc);
+
+		parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
+	}
+
+	/* Recurse if immediate parent is not the top parent. */
+	if (!bms_equal(parent_relids, top_parent_relids))
+	{
+		clauses = build_child_clauses_multilevel(root, clauses, parent_relids,
+												  top_parent_relids);
+		tmp_result = clauses;
+	}
+
+	result = build_child_clauses(root, clauses, appinfos);
+
+	/* Free memory consumed by any itermediate list. */
+	if (tmp_result)
+		list_free(tmp_result);
+
+	bms_free(parent_relids);
+	list_free(appinfos);
+
+	return result;
+}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 01ba885..c5e0ebf 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1787,7 +1787,6 @@ find_partition_scheme(PlannerInfo *root, Relation relation)
 	int		nparts;
 	int		partnatts;
 	int		cnt_pks;
-	int		cnt_parts;
 	PartitionScheme	part_scheme = NULL;
 
 	/* No partition scheme for an unpartitioned relation. */
@@ -1797,21 +1796,6 @@ find_partition_scheme(PlannerInfo *root, Relation relation)
 	nparts = part_desc->nparts;
 	partnatts = part_key->partnatts;
 
-	/*
-	 * For a multi-level partitioned table, we do not retain the partitioning
-	 * hierarchy while expanding RTE for the topmost parent. Thus the number of
-	 * children as per root->append_rel_list does not match the number of
-	 * partitions specified in the partition descriptor and hence the
-	 * partitioning scheme of a multi-partitioned table does not reflect the
-	 * true picture. So for now, treat a multi-partitioned table as not
-	 * partitioned.
-	 */
-	for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
-	{
-		if (has_subclass(part_desc->oids[cnt_parts]))
-			return NULL;
-	}
-
 	/* Search for a matching partition scheme and return if found one. */
 	foreach (lc, root->part_schemes)
 	{
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 146d53b..f1ae325 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1569,6 +1569,12 @@ typedef struct LimitPath
 typedef struct PartitionJoinPath
 {
 	Path		path;
+
+	/* If this path is result of reparameterization by child. */
+	Relids		old_param;			/* Older parameterization. */
+	RelOptInfo *reparamterized_by;	/* Child relation by which this paths was
+									 * reparamterized.
+									 */
 } PartitionJoinPath;
 
 /*
-- 
1.7.9.5

