From ef834653b34bd69d8f3f8dd07abd4f5d1e09d8c0 Mon Sep 17 00:00:00 2001
From: Amit <amitlangote09@gmail.com>
Date: Sat, 2 Mar 2019 14:13:13 +0900
Subject: [PATCH v31 1/8] Build "other rels" of appendrel baserels in a
 separate step

Currently they're built in a stanza in build_simple_rel() which
builds the child rels in the same invocation of build_simple_rel
that's used to build the parent relation.  Since query hasn't
been processed to distribute restriction clauses to individual
baserels at this point, we cannot perform partition pruning before
adding the children.

Newly added add_other_rels_to_query() runs *after* query_planner has
distributed restriction clauses to base relations.  This will allow
us to use the clauses applied a given partitioned baserel to perform
partition pruning, and add other rels for only the unpruned
partitions.  Later patches will do that.
---
 src/backend/optimizer/path/allpaths.c  |   2 +-
 src/backend/optimizer/plan/initsplan.c |  60 ++++++++++++-
 src/backend/optimizer/plan/planmain.c  |  15 +++-
 src/backend/optimizer/util/relnode.c   | 114 ++++++++++++++++---------
 src/include/optimizer/pathnode.h       |   2 +
 src/include/optimizer/planmain.h       |   1 +
 6 files changed, 143 insertions(+), 51 deletions(-)

diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index d8ba7add13..892032dbe4 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -1029,7 +1029,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 
 		/*
 		 * The child rel's RelOptInfo was already created during
-		 * add_base_rels_to_query.
+		 * add_other_rels_to_query.
 		 */
 		childrel = find_base_rel(root, childRTindex);
 		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 2afc3f1dfe..077d3203ba 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -20,6 +20,7 @@
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/inherit.h"
 #include "optimizer/joininfo.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/pathnode.h"
@@ -30,6 +31,7 @@
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "parser/analyze.h"
+#include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
@@ -97,10 +99,11 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
  * jtnode.  Internally, the function recurses through the jointree.
  *
  * At the end of this process, there should be one baserel RelOptInfo for
- * every non-join RTE that is used in the query.  Therefore, this routine
- * is the only place that should call build_simple_rel with reloptkind
- * RELOPT_BASEREL.  (Note: build_simple_rel recurses internally to build
- * "other rel" RelOptInfos for the members of any appendrels we find here.)
+ * every non-join RTE that is specified in the query.  Therefore, this
+ * routine is the only place that should call build_simple_rel with
+ * reloptkind RELOPT_BASEREL.  (Note:  "other rel" RelOptInfos for the
+ * members of any appendrels we find here are built later when query_planner
+ * calls add_other_rels_to_query().)
  */
 void
 add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
@@ -133,6 +136,55 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
 			 (int) nodeTag(jtnode));
 }
 
+/*
+ * add_other_rels_to_query
+ *
+ *	  Scan the query's jointree and for each base rels that is an appendrel,
+ *	  create otherrel RelOptInfos of its children
+ *
+ * At the end of this process, there should be RelOptInfos for all relations
+ * that will be scanned by the query.
+ */
+void
+add_other_rels_to_query(PlannerInfo *root, Node *jtnode)
+{
+	if (jtnode == NULL)
+		return;
+	if (IsA(jtnode, RangeTblRef))
+	{
+		int			varno = ((RangeTblRef *) jtnode)->rtindex;
+		RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable);
+
+		/*
+		 * Only the parent subquery of a flattened UNION ALL and an inherited
+		 * table can have children.
+		 */
+		if (rte->rtekind != RTE_SUBQUERY && rte->rtekind != RTE_RELATION)
+			return;
+
+		if (rte->inh)
+			(void) add_appendrel_other_rels(root, rte, varno);
+	}
+	else if (IsA(jtnode, FromExpr))
+	{
+		FromExpr   *f = (FromExpr *) jtnode;
+		ListCell   *l;
+
+		foreach(l, f->fromlist)
+			add_other_rels_to_query(root, lfirst(l));
+	}
+	else if (IsA(jtnode, JoinExpr))
+	{
+		JoinExpr   *j = (JoinExpr *) jtnode;
+
+		add_other_rels_to_query(root, j->larg);
+		add_other_rels_to_query(root, j->rarg);
+	}
+	else
+		elog(ERROR, "unrecognized node type: %d",
+			 (int) nodeTag(jtnode));
+}
+
 
 /*****************************************************************************
  *
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 3cedd01c98..03c81772a3 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -159,10 +159,8 @@ query_planner(PlannerInfo *root, List *tlist,
 	setup_append_rel_array(root);
 
 	/*
-	 * Construct RelOptInfo nodes for all base relations in query, and
-	 * indirectly for all appendrel member relations ("other rels").  This
-	 * will give us a RelOptInfo for every "simple" (non-join) rel involved in
-	 * the query.
+	 * Construct RelOptInfo nodes for all base relations directly mentioned
+	 * in query, but not any appendrel member relations ("other rels") yet.
 	 *
 	 * Note: the reason we find the rels by searching the jointree and
 	 * appendrel list, rather than just scanning the rangetable, is that the
@@ -203,6 +201,15 @@ query_planner(PlannerInfo *root, List *tlist,
 	 */
 	generate_base_implied_equalities(root);
 
+	/*
+	 * Now that we have restrict clauses figured out and assigned to proper
+	 * base rels, we can proceed to add otherrels, that is, UNION ALL child
+	 * tables, inheritance child tables.  Having restrict clauses ready helps
+	 * to exclude any children that wouldn't be necessary to scan, based on
+	 * constraint exclusion and partition pruning.
+	 */
+	add_other_rels_to_query(root, (Node *) root->parse->jointree);
+
 	/*
 	 * We have completed merging equivalence sets, so it's now possible to
 	 * generate pathkeys in canonical form; so compute query_pathkeys and
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 4130514952..8a59819469 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -16,19 +16,26 @@
 
 #include <limits.h>
 
+#include "access/table.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "optimizer/appendinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/inherit.h"
+#include "optimizer/optimizer.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/plancat.h"
+#include "optimizer/planmain.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
 #include "partitioning/partbounds.h"
+#include "partitioning/partdesc.h"
 #include "utils/hsearch.h"
+#include "utils/rel.h"
 
 
 typedef struct JoinHashEntry
@@ -273,53 +280,76 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 		root->qual_security_level = Max(root->qual_security_level,
 										list_length(rte->securityQuals));
 
+	return rel;
+}
+
+/*
+ * add_appendrel_other_rels
+ *		This adds the "other rel" RelOptInfos a given "appendrel" baserel
+ *
+ * Parent relation in this case is the parent subquery in the flattened UNION
+ * ALL case or an inheritance parent table.
+ */
+void
+add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, Index rti)
+{
+	ListCell	   *l;
+	RelOptInfo	   *rel;
+	int				i;
+
+	rel = find_base_rel(root, rti);
+
 	/*
-	 * If this rel is an appendrel parent, recurse to build "other rel"
-	 * RelOptInfos for its children.  They are "other rels" because they are
-	 * not in the main join tree, but we will need RelOptInfos to plan access
-	 * to them.
+	 * For partitioned tables, we need to store the child RelOptInfos in the
+	 * rel->part_rels array too.
 	 */
-	if (rte->inh)
+	if (rel->part_scheme)
 	{
-		ListCell   *l;
-		int			nparts = rel->nparts;
-		int			cnt_parts = 0;
-
-		if (nparts > 0)
-			rel->part_rels = (RelOptInfo **)
-				palloc(sizeof(RelOptInfo *) * nparts);
-
-		foreach(l, root->append_rel_list)
-		{
-			AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
-			RelOptInfo *childrel;
-
-			/* append_rel_list contains all append rels; ignore others */
-			if (appinfo->parent_relid != relid)
-				continue;
-
-			childrel = build_simple_rel(root, appinfo->child_relid,
-										rel);
-
-			/* Nothing more to do for an unpartitioned table. */
-			if (!rel->part_scheme)
-				continue;
-
-			/*
-			 * The order of partition OIDs in append_rel_list is the same as
-			 * the order in the PartitionDesc, so the order of part_rels will
-			 * also match the PartitionDesc.  See expand_partitioned_rtentry.
-			 */
-			Assert(cnt_parts < nparts);
-			rel->part_rels[cnt_parts] = childrel;
-			cnt_parts++;
-		}
-
-		/* We should have seen all the child partitions. */
-		Assert(cnt_parts == nparts);
+		Assert(rel->nparts > 0);
+		rel->part_rels = (RelOptInfo **)
+				palloc0(sizeof(RelOptInfo *) * rel->nparts);
 	}
 
-	return rel;
+	i  = 0;
+	foreach(l, root->append_rel_list)
+	{
+		AppendRelInfo  *appinfo = lfirst(l);
+		Index			childRTindex = appinfo->child_relid;
+		RangeTblEntry  *childrte;
+		RelOptInfo	   *childrel;
+
+		if (appinfo->parent_relid != rti)
+				continue;
+
+		Assert(childRTindex < root->simple_rel_array_size);
+		childrte = root->simple_rte_array[childRTindex];
+		Assert(childrte != NULL);
+		childrel = build_simple_rel(root, childRTindex, rel);
+
+		/*
+		 * For partitioned parents, we also need to add childrel to its
+		 * part_rels array.  The order in which child tables appear in
+		 * append_rel_list is same as the order in which they appear in the
+		 * parent's PartitionDesc, so assigning partitions like this works.
+		 */
+		if (rel->part_scheme != NULL)
+		{
+			Assert(i < rel->nparts);
+			rel->part_rels[i] = childrel;
+		}
+
+		i++;
+
+		/* Child may itself be an inherited relation. */
+		if (childrte->inh)
+			add_appendrel_other_rels(root, childrte, childRTindex);
+	}
+
+	/*
+	 * For a partitioned table with non-zero number of partitions, we must
+	 * have assigned all elements of its part_rels array.
+	 */
+	Assert(rel->nparts == 0 || i == rel->nparts);
 }
 
 /*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 574bb85b50..1a07963a7d 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -279,6 +279,8 @@ extern void setup_simple_rel_arrays(PlannerInfo *root);
 extern void setup_append_rel_array(PlannerInfo *root);
 extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
 				 RelOptInfo *parent);
+extern void add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte,
+				 Index rti);
 extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
 extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
 extern RelOptInfo *build_join_rel(PlannerInfo *root,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 3bbdb5e2f7..035caac500 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -65,6 +65,7 @@ extern int	from_collapse_limit;
 extern int	join_collapse_limit;
 
 extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode);
+extern void add_other_rels_to_query(PlannerInfo *root, Node *jtnode);
 extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
 extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
 					   Relids where_needed, bool create_new_ph);
-- 
2.17.2 (Apple Git-113)

