From edd69ad781c2ec8ef96b4b2b1e46a6be354cdfd4 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Mon, 12 Mar 2018 16:45:15 -0400
Subject: [PATCH 4/7] Postpone generate_gather_paths for topmost scan/join rel.

Don't call generate_gather_paths for the topmost scan/join relation
when it is initially populated with paths.  If the scan/join target
is parallel-safe, we actually skip this for the topmost scan/join rel
altogether and instead do it for the tlist_rel, so that the
projection is done in the worker and costs are computed accordingly.

Amit Kapila and Robert Haas
---
 src/backend/optimizer/geqo/geqo_eval.c | 21 ++++++++++++++-------
 src/backend/optimizer/path/allpaths.c  | 26 +++++++++++++++++++-------
 src/backend/optimizer/plan/planner.c   | 24 +++++++++++++++++++++++-
 3 files changed, 56 insertions(+), 15 deletions(-)

diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c
index 0be2a73e05..3ef7d7d8aa 100644
--- a/src/backend/optimizer/geqo/geqo_eval.c
+++ b/src/backend/optimizer/geqo/geqo_eval.c
@@ -40,7 +40,7 @@ typedef struct
 } Clump;
 
 static List *merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump,
-			bool force);
+			int num_gene, bool force);
 static bool desirable_join(PlannerInfo *root,
 			   RelOptInfo *outer_rel, RelOptInfo *inner_rel);
 
@@ -196,7 +196,7 @@ gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
 		cur_clump->size = 1;
 
 		/* Merge it into the clumps list, using only desirable joins */
-		clumps = merge_clump(root, clumps, cur_clump, false);
+		clumps = merge_clump(root, clumps, cur_clump, num_gene, false);
 	}
 
 	if (list_length(clumps) > 1)
@@ -210,7 +210,7 @@ gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
 		{
 			Clump	   *clump = (Clump *) lfirst(lc);
 
-			fclumps = merge_clump(root, fclumps, clump, true);
+			fclumps = merge_clump(root, fclumps, clump, num_gene, true);
 		}
 		clumps = fclumps;
 	}
@@ -235,7 +235,8 @@ gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
  * "desirable" joins.
  */
 static List *
-merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, bool force)
+merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, int num_gene,
+			bool force)
 {
 	ListCell   *prev;
 	ListCell   *lc;
@@ -267,8 +268,14 @@ merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, bool force)
 				/* Create paths for partitionwise joins. */
 				generate_partitionwise_join_paths(root, joinrel);
 
-				/* Create GatherPaths for any useful partial paths for rel */
-				generate_gather_paths(root, joinrel, false);
+				/*
+				 * Except for the topmost scan/join rel, consider gathering
+				 * partial paths.  We'll do the same for the topmost scan/join
+				 * rel once we know the final targetlist (see
+				 * grouping_planner).
+				 */
+				if (old_clump->size + new_clump->size < num_gene)
+					generate_gather_paths(root, joinrel, false);
 
 				/* Find and save the cheapest paths for this joinrel */
 				set_cheapest(joinrel);
@@ -286,7 +293,7 @@ merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, bool force)
 				 * others.  When no further merge is possible, we'll reinsert
 				 * it into the list.
 				 */
-				return merge_clump(root, clumps, old_clump, force);
+				return merge_clump(root, clumps, old_clump, num_gene, force);
 			}
 		}
 		prev = lc;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 43f4e75748..c4e4db15a6 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -479,13 +479,20 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	}
 
 	/*
-	 * If this is a baserel, consider gathering any partial paths we may have
-	 * created for it.  (If we tried to gather inheritance children, we could
+	 * If this is a baserel, we should normally consider gathering any partial
+	 * paths we may have created for it.
+	 *
+	 * However, if this is an inheritance child, skip it.  Otherwise, we could
 	 * end up with a very large number of gather nodes, each trying to grab
-	 * its own pool of workers, so don't do this for otherrels.  Instead,
-	 * we'll consider gathering partial paths for the parent appendrel.)
+	 * its own pool of workers.  Instead, we'll consider gathering partial
+	 * paths for the parent appendrel.
+	 *
+	 * Also, if this is the topmost scan/join rel (that is, the only baserel),
+	 * we postpone this until the final scan/join targelist is available (see
+	 * grouping_planner).
 	 */
-	if (rel->reloptkind == RELOPT_BASEREL)
+	if (rel->reloptkind == RELOPT_BASEREL &&
+		bms_membership(root->all_baserels) != BMS_SINGLETON)
 		generate_gather_paths(root, rel, false);
 
 	/*
@@ -2699,8 +2706,13 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
 			/* Create paths for partitionwise joins. */
 			generate_partitionwise_join_paths(root, rel);
 
-			/* Create GatherPaths for any useful partial paths for rel */
-			generate_gather_paths(root, rel, false);
+			/*
+			 * Except for the topmost scan/join rel, consider gathering
+			 * partial paths.  We'll do the same for the topmost scan/join rel
+			 * once we know the final targetlist (see grouping_planner).
+			 */
+			if (lev < levels_needed)
+				generate_gather_paths(root, rel, false);
 
 			/* Find and save the cheapest paths for this rel */
 			set_cheapest(rel);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 1e1b363402..c48e98643a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1970,6 +1970,17 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 			scanjoin_targets_contain_srfs = NIL;
 		}
 
+		/*
+		 * If the final scan/join target is not parallel-safe, we must
+		 * generate Gather paths now, since no partial paths will be generated
+		 * for the tlist rel.  Otherwise, the Gather or Gather Merge paths
+		 * generated for the tlist rel will be superior to these in that
+		 * projection will be done in by each participant rather than only in
+		 * the leader, so skip generating them here.
+		 */
+		if (!scanjoin_target_parallel_safe)
+			generate_gather_paths(root, current_rel, false);
+
 		/*
 		 * Apply SRF-free scan/join target to all Paths for the scanjoin rel
 		 * to produce paths for the tlist rel.
@@ -6791,6 +6802,7 @@ create_tlist_paths(PlannerInfo *root,
 {
 	ListCell   *lc;
 	RelOptInfo *tlist_rel;
+	bool		is_other_rel = IS_OTHER_REL(input_rel);
 
 	check_stack_depth();
 
@@ -6799,7 +6811,7 @@ create_tlist_paths(PlannerInfo *root,
 	 * projection.
 	 */
 	tlist_rel = fetch_upper_rel(root, UPPERREL_TLIST, input_rel->relids);
-	if (IS_OTHER_REL(input_rel))
+	if (is_other_rel)
 		tlist_rel->reloptkind = RELOPT_OTHER_UPPER_REL;
 	tlist_rel->rows = input_rel->rows;
 	tlist_rel->reltarget = llast_node(PathTarget, scanjoin_targets);
@@ -6948,6 +6960,16 @@ create_tlist_paths(PlannerInfo *root,
 								  scanjoin_targets_contain_srfs);
 	}
 
+	/*
+	 * Consider generating Gather or Gather Merge paths.  We must only do this
+	 * if the relation is parallel safe, and we don't do it for child rels to
+	 * avoid creating multiple Gather nodes within the same plan. Also, we
+	 * must do it before calling set_cheapest, since one of the generated
+	 * paths may turn out to be the cheapest one.
+	 */
+	if (tlist_rel->consider_parallel && !is_other_rel)
+		generate_gather_paths(root, tlist_rel, false);
+
 	/* Determine cheapest paths, for the benefit of future planning steps. */
 	set_cheapest(tlist_rel);
 
-- 
2.14.3 (Apple Git-98)

