From 4a20fd84ec67fd485d8d413ca71cdb157b2e93a0 Mon Sep 17 00:00:00 2001
From: "dgrowley@gmail.com" <dgrowley@gmail.com>
Date: Wed, 6 Jun 2018 17:53:09 +1200
Subject: [PATCH v2] Allow direct lookups of AppendRelInfo by child relid

find_appinfos_by_relids had quite a large overhead when the number of items in
the append_rel_list was high.  Here we're just looking for a single
AppendRelInfo, so let's make an array so that can be looked up directly.

A test case with 10k RANGE partitions show that planning of an UPDATE to the
partitioned table is about twice as fast with this change.
---
 src/backend/optimizer/plan/planner.c   |  4 ++++
 src/backend/optimizer/prep/prepunion.c | 25 +++++++++----------------
 src/backend/optimizer/util/relnode.c   | 22 ++++++++++++++++++++++
 src/include/nodes/relation.h           |  5 +++++
 4 files changed, 40 insertions(+), 16 deletions(-)

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 67a2c7a581..da112ddfd8 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1163,6 +1163,7 @@ inheritance_planner(PlannerInfo *root)
 	List	   *final_rtable = NIL;
 	int			save_rel_array_size = 0;
 	RelOptInfo **save_rel_array = NULL;
+	AppendRelInfo **save_append_rel_array = NULL;
 	List	   *subpaths = NIL;
 	List	   *subroots = NIL;
 	List	   *resultRelations = NIL;
@@ -1529,6 +1530,7 @@ inheritance_planner(PlannerInfo *root)
 		}
 		save_rel_array_size = subroot->simple_rel_array_size;
 		save_rel_array = subroot->simple_rel_array;
+		save_append_rel_array = subroot->append_rel_array;
 
 		/* Make sure any initplans from this rel get into the outer list */
 		root->init_plans = subroot->init_plans;
@@ -1579,6 +1581,8 @@ inheritance_planner(PlannerInfo *root)
 	parse->rtable = final_rtable;
 	root->simple_rel_array_size = save_rel_array_size;
 	root->simple_rel_array = save_rel_array;
+	root->append_rel_array = save_append_rel_array;
+
 	/* Must reconstruct master's simple_rte_array, too */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *));
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0ab4014be6..6047a33a44 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -2617,29 +2617,22 @@ build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
 AppendRelInfo **
 find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
 {
-	ListCell   *lc;
 	AppendRelInfo **appinfos;
 	int			cnt = 0;
-
+	int			i;
 	*nappinfos = bms_num_members(relids);
 	appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);
 
-	foreach(lc, root->append_rel_list)
+	Assert(root->append_rel_array);
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		AppendRelInfo *appinfo = lfirst(lc);
+		AppendRelInfo *appinfo = root->append_rel_array[i];
 
-		if (bms_is_member(appinfo->child_relid, relids))
-		{
-			appinfos[cnt] = appinfo;
-			cnt++;
+		if (!appinfo)
+			elog(ERROR, "child rel missing from append_rel_array");
 
-			/* Stop when we have gathered all the AppendRelInfos. */
-			if (cnt == *nappinfos)
-				return appinfos;
-		}
+		appinfos[cnt++] = appinfo;
 	}
-
-	/* Should have found the entries ... */
-	elog(ERROR, "did not find all requested child rels in append_rel_list");
-	return NULL;				/* not reached */
+	return appinfos;
 }
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 82b78420e7..c27cd52f62 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -86,6 +86,28 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->simple_rte_array[rti++] = rte;
 	}
+
+	if (root->append_rel_list != NIL)
+	{
+		root->append_rel_array = (AppendRelInfo **)
+			palloc0(root->simple_rel_array_size * sizeof(AppendRelInfo *));
+
+		foreach(lc, root->append_rel_list)
+		{
+			AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
+			int		child_relid = appinfo->child_relid;
+
+			/* Sanity check */
+			Assert(child_relid < root->simple_rel_array_size);
+
+			if (root->append_rel_array[child_relid])
+				elog(ERROR, "child relation already exists");
+
+			root->append_rel_array[child_relid] = appinfo;
+		}
+	}
+	else
+		root->append_rel_array = NULL;
 }
 
 /*
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 3b28d1994f..9045a90f97 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -151,6 +151,7 @@ typedef struct PlannerGlobal
 #define planner_subplan_get_plan(root, subplan) \
 	((Plan *) list_nth((root)->glob->subplans, (subplan)->plan_id - 1))
 
+struct AppendRelInfo;
 
 /*----------
  * PlannerInfo
@@ -201,6 +202,10 @@ typedef struct PlannerInfo
 	 */
 	RangeTblEntry **simple_rte_array;	/* rangetable as an array */
 
+	struct AppendRelInfo **append_rel_array;	/* append_rel_list indexed by
+												 * child_relid, or NULL if
+												 * append_rel_list is empty */
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not "other"
 	 * relids) in the query; that is, the Relids identifier of the final join
-- 
2.16.2.windows.1

