v26-0001-Build-other-rels-of-appendrel-baserels-in-a-sepa.patch

text/plain

Filename: v26-0001-Build-other-rels-of-appendrel-baserels-in-a-sepa.patch
Type: text/plain
Part: 0
Message: Re: speeding up planning with partitions

Patch

Format: format-patch
Series: patch v26-0001
Subject: Build "other rels" of appendrel baserels in a separate step
File+
src/backend/optimizer/path/allpaths.c 1 1
src/backend/optimizer/plan/initsplan.c 49 4
src/backend/optimizer/plan/planmain.c 11 4
src/backend/optimizer/util/relnode.c 76 42
src/include/optimizer/pathnode.h 2 0
src/include/optimizer/planmain.h 1 0
From 1d83debe07040a93721f17785d075d26dab6f088 Mon Sep 17 00:00:00 2001
From: Amit <amitlangote09@gmail.com>
Date: Sat, 2 Mar 2019 14:13:13 +0900
Subject: [PATCH v26 1/6] Build "other rels" of appendrel baserels in a
 separate step

add_other_rels_to_query() runs after query_planner has distributed
restrict 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 partiitons.
---
 src/backend/optimizer/path/allpaths.c  |   2 +-
 src/backend/optimizer/plan/initsplan.c |  53 +++++++++++++--
 src/backend/optimizer/plan/planmain.c  |  15 +++--
 src/backend/optimizer/util/relnode.c   | 118 +++++++++++++++++++++------------
 src/include/optimizer/pathnode.h       |   2 +
 src/include/optimizer/planmain.h       |   1 +
 6 files changed, 140 insertions(+), 51 deletions(-)

diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0debac75c6..8d8a8f17d5 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -1028,7 +1028,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..54fc4dda97 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 added built later when
+ * query_planner calls add_other_rels_to_query().)
  */
 void
 add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
@@ -133,6 +136,48 @@ 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);
+
+		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
@@ -204,6 +202,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
 	 * other pathkeys fields in PlannerInfo.
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 4130514952..a6c3eedc56 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,80 @@ 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;
+	Relation		relation = NULL;
+	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);
+		rel->part_rels = (RelOptInfo **)
+				palloc0(sizeof(RelOptInfo *) * rel->nparts);
+		relation = table_open(rte->relid, NoLock);
 	}
 
-	return rel;
+	/*
+	 * We don't need to use expand_planner_arrays in this case, because
+	 * no new child RTEs are created.  setup_simple_rel_arrays() and
+	 * setup_append_rel_array would've considered these child RTEs when
+	 * allocating space for various arrays.
+	 */
+	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(rel->nparts > 0 && 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);
+	}
+
+	if (relation)
+		table_close(relation, NoLock);
 }
 
 /*
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.11.0