From 54a8a61abb3c629196e6e9a778f8527103df26c2 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Tue, 20 Mar 2018 14:12:58 -0400
Subject: [PATCH 1/3] Remove loop from 0 to 1.

---
 src/backend/optimizer/plan/planner.c | 289 +++++++++++++++++++++--------------
 1 file changed, 171 insertions(+), 118 deletions(-)

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 4c11d84fd6..c05182d479 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6378,14 +6378,32 @@ create_partial_grouping_paths(PlannerInfo *root,
 	RelOptInfo *partially_grouped_rel;
 	AggClauseCosts *agg_partial_costs = &extra->agg_partial_costs;
 	AggClauseCosts *agg_final_costs = &extra->agg_final_costs;
-	Path	   *cheapest_path;
-	Size		hashaggtablesize;
+	Path	   *cheapest_partial_path = NULL;
+	Path	   *cheapest_total_path = NULL;
 	double		dNumPartialGroups = 0;
+	double		dNumPartialPartialGroups = 0;
 	ListCell   *lc;
 	bool		can_hash = (extra->flags & GROUPING_CAN_USE_HASH) != 0;
 	bool		can_sort = (extra->flags & GROUPING_CAN_USE_SORT) != 0;
-	bool		use_partial_pathlist;
-	int			i;
+
+	/*
+	 * Consider whether we should generate partially aggregated non-partial
+	 * paths.  We can only do this if we have a non-partial path, and in
+	 * addition the caller must have requested it by setting
+	 * extra->is_partial_aggregation or
+	 * extra->perform_partial_partitionwise_aggregation.
+	 */
+	if (input_rel->pathlist != NIL && (extra->is_partial_aggregation ||
+		extra->perform_partial_partitionwise_aggregation))
+		cheapest_total_path = input_rel->cheapest_total_path;
+
+	/*
+	 * If parallelism is possible for grouped_rel, then we should consider
+	 * generating partially-grouped partial paths.  However, if the input rel
+	 * has no partial paths, then we can't.
+	 */
+	if (grouped_rel->consider_parallel && input_rel->partial_pathlist != NIL)
+		cheapest_partial_path = linitial(input_rel->partial_pathlist);
 
 	/*
 	 * Build a new upper relation to represent the result of partially
@@ -6443,137 +6461,172 @@ create_partial_grouping_paths(PlannerInfo *root,
 		extra->partial_costs_set = true;
 	}
 
-	/*
-	 * We loop twice, one to generate paths for partial_pathlist when parallel
-	 * paths are possible and second time for generating paths in pathlist when
-	 * we need partially aggregated results for the partitionwise grouping
-	 * and/or aggregation.
-	 */
-	use_partial_pathlist = true;
-	for (i = 0; i < 2; i++)
+	/* Estimate number of partial groups. */
+	if (cheapest_total_path != NULL)
+		dNumPartialGroups =
+			get_number_of_groups(root,
+								 cheapest_total_path->rows,
+								 gd,
+								 extra->targetList);
+	if (cheapest_partial_path != NULL)
+		dNumPartialPartialGroups =
+			get_number_of_groups(root,
+								 cheapest_partial_path->rows,
+								 gd,
+								 extra->targetList);
+
+	if (can_sort && cheapest_total_path != NULL)
 	{
-		if (use_partial_pathlist && !(grouped_rel->consider_parallel &&
-									  input_rel->partial_pathlist != NIL &&
-									  (extra->flags & GROUPING_CAN_PARTIAL_AGG) != 0))
-		{
-			/* No parallel paths possible. */
-			use_partial_pathlist = false;
-			continue;
-		}
+		/* This should have been checked previously */
+		Assert(parse->hasAggs || parse->groupClause);
 
-		if (!use_partial_pathlist && !extra->is_partial_aggregation)
+		/*
+		 * Use any available suitably-sorted path as input, and also consider
+		 * sorting the cheapest partial path.
+		 */
+		foreach(lc, input_rel->pathlist)
 		{
-			/* No partial partitiowise aggregation possible. */
-			continue;
-		}
+			Path	   *path = (Path *) lfirst(lc);
+			bool		is_sorted;
 
-		/* Get either total or partial cheapest path */
-		cheapest_path = use_partial_pathlist ? linitial(input_rel->partial_pathlist) :
-			input_rel->cheapest_total_path;
+			is_sorted = pathkeys_contained_in(root->group_pathkeys,
+											  path->pathkeys);
+			if (path == cheapest_total_path || is_sorted)
+			{
+				/* Sort the cheapest partial path, if it isn't already */
+				if (!is_sorted)
+					path = (Path *) create_sort_path(root,
+													 partially_grouped_rel,
+													 path,
+													 root->group_pathkeys,
+													 -1.0);
 
-		/* Estimate number of partial groups. */
-		dNumPartialGroups = get_number_of_groups(root,
-												 cheapest_path->rows,
-												 gd,
-												 extra->targetList);
+				if (parse->hasAggs)
+					add_path(partially_grouped_rel, (Path *)
+							 create_agg_path(root,
+											 partially_grouped_rel,
+											 path,
+											 partially_grouped_rel->reltarget,
+											 parse->groupClause ? AGG_SORTED : AGG_PLAIN,
+											 AGGSPLIT_INITIAL_SERIAL,
+											 parse->groupClause,
+											 NIL,
+											 agg_partial_costs,
+											 dNumPartialGroups));
+				else
+					add_path(partially_grouped_rel, (Path *)
+							 create_group_path(root,
+											   partially_grouped_rel,
+											   path,
+											   parse->groupClause,
+											   NIL,
+											   dNumPartialGroups));
+			}
+		}
+	}
 
-		if (can_sort)
+	if (can_sort && cheapest_partial_path != NULL)
+	{
+		/* Similar to above logic, but for partial paths. */
+		foreach(lc, input_rel->partial_pathlist)
 		{
-			List	   *pathlist;
-
-			/* This should have been checked previously */
-			Assert(parse->hasAggs || parse->groupClause);
-
-			/* Get appropriate pathlist */
-			pathlist = use_partial_pathlist ? input_rel->partial_pathlist :
-				input_rel->pathlist;
+			Path	   *path = (Path *) lfirst(lc);
+			bool		is_sorted;
 
-			/*
-			 * Use any available suitably-sorted path as input, and also
-			 * consider sorting the cheapest path.
-			 */
-			foreach(lc, pathlist)
+			is_sorted = pathkeys_contained_in(root->group_pathkeys,
+											  path->pathkeys);
+			if (path == cheapest_partial_path || is_sorted)
 			{
-				Path	   *path = (Path *) lfirst(lc);
-				bool		is_sorted;
-				Path	   *gpath;
+				/* Sort the cheapest partial path, if it isn't already */
+				if (!is_sorted)
+					path = (Path *) create_sort_path(root,
+													 partially_grouped_rel,
+													 path,
+													 root->group_pathkeys,
+													 -1.0);
 
-				is_sorted = pathkeys_contained_in(root->group_pathkeys,
-												  path->pathkeys);
-				if (path == cheapest_path || is_sorted)
-				{
-					/* Sort the cheapest path, if it isn't already */
-					if (!is_sorted)
-						path = (Path *) create_sort_path(root,
-														 partially_grouped_rel,
-														 path,
-														 root->group_pathkeys,
-														 -1.0);
-
-					if (parse->hasAggs)
-						gpath = (Path *) create_agg_path(root,
-														 partially_grouped_rel,
-														 path,
-														 partially_grouped_rel->reltarget,
-														 parse->groupClause ? AGG_SORTED : AGG_PLAIN,
-														 AGGSPLIT_INITIAL_SERIAL,
-														 parse->groupClause,
-														 NIL,
-														 agg_partial_costs,
-														 dNumPartialGroups);
-					else
-						gpath = (Path *) create_group_path(root,
-														   partially_grouped_rel,
-														   path,
-														   parse->groupClause,
-														   NIL,
-														   dNumPartialGroups);
-
-					if (use_partial_pathlist)
-						add_partial_path(partially_grouped_rel, gpath);
-					else
-						add_path(partially_grouped_rel, gpath);
-				}
+				if (parse->hasAggs)
+					add_partial_path(partially_grouped_rel, (Path *)
+									 create_agg_path(root,
+													 partially_grouped_rel,
+													 path,
+													 partially_grouped_rel->reltarget,
+													 parse->groupClause ? AGG_SORTED : AGG_PLAIN,
+													 AGGSPLIT_INITIAL_SERIAL,
+													 parse->groupClause,
+													 NIL,
+													 agg_partial_costs,
+													 dNumPartialPartialGroups));
+				else
+					add_partial_path(partially_grouped_rel, (Path *)
+									 create_group_path(root,
+													   partially_grouped_rel,
+													   path,
+													   parse->groupClause,
+													   NIL,
+													   dNumPartialPartialGroups));
 			}
 		}
+	}
 
-		if (can_hash)
-		{
-			/* Checked above */
-			Assert(parse->hasAggs || parse->groupClause);
+	if (can_hash && cheapest_total_path != NULL)
+	{
+		Size		hashaggtablesize;
 
-			hashaggtablesize =
-				estimate_hashagg_tablesize(cheapest_path,
-										   agg_partial_costs,
-										   dNumPartialGroups);
+		/* Checked above */
+		Assert(parse->hasAggs || parse->groupClause);
 
-			/*
-			 * Tentatively produce a partial HashAgg Path, depending on if it
-			 * looks as if the hash table will fit in work_mem.
-			 */
-			if (hashaggtablesize < work_mem * 1024L)
-			{
-				Path	   *gpath;
-
-				gpath = (Path *) create_agg_path(root,
-												 partially_grouped_rel,
-												 cheapest_path,
-												 partially_grouped_rel->reltarget,
-												 AGG_HASHED,
-												 AGGSPLIT_INITIAL_SERIAL,
-												 parse->groupClause,
-												 NIL,
-												 agg_partial_costs,
-												 dNumPartialGroups);
-
-				if (use_partial_pathlist)
-					add_partial_path(partially_grouped_rel, gpath);
-				else
-					add_path(partially_grouped_rel, gpath);
-			}
+		hashaggtablesize =
+			estimate_hashagg_tablesize(cheapest_total_path,
+									   agg_partial_costs,
+									   dNumPartialGroups);
+
+		/*
+		 * Tentatively produce a partial HashAgg Path, depending on if it
+		 * looks as if the hash table will fit in work_mem.
+		 */
+		if (hashaggtablesize < work_mem * 1024L &&
+			cheapest_total_path != NULL)
+		{
+			add_path(partially_grouped_rel, (Path *)
+					 create_agg_path(root,
+									 partially_grouped_rel,
+									 cheapest_total_path,
+									 partially_grouped_rel->reltarget,
+									 AGG_HASHED,
+									 AGGSPLIT_INITIAL_SERIAL,
+									 parse->groupClause,
+									 NIL,
+									 agg_partial_costs,
+									 dNumPartialGroups));
 		}
+	}
+
+	if (can_hash && cheapest_partial_path != NULL)
+	{
+		Size		hashaggtablesize;
+
+		hashaggtablesize =
+			estimate_hashagg_tablesize(cheapest_partial_path,
+									   agg_partial_costs,
+									   dNumPartialPartialGroups);
 
-		use_partial_pathlist = false;
+		/* Do the same for partial paths. */
+		if (hashaggtablesize < work_mem * 1024L &&
+			cheapest_partial_path != NULL)
+		{
+			add_partial_path(partially_grouped_rel, (Path *)
+							 create_agg_path(root,
+											 partially_grouped_rel,
+											 cheapest_partial_path,
+											 partially_grouped_rel->reltarget,
+											 AGG_HASHED,
+											 AGGSPLIT_INITIAL_SERIAL,
+											 parse->groupClause,
+											 NIL,
+											 agg_partial_costs,
+											 dNumPartialPartialGroups));
+		}
 	}
 
 	/*
-- 
2.14.3 (Apple Git-98)

