mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-04 00:02:52 -05:00 
			
		
		
		
	Split create_grouping_paths into degenerate and non-degenerate cases.
There's no functional change here, or at least I hope there isn't, just code rearrangement. The rearrangement is motivated by partition-wise aggregate, which doesn't need to consider the degenerate case but wants to reuse the logic for the ordinary case. Based loosely on a patch from Ashutosh Bapat and Jeevan Chalke, but I whacked it around pretty heavily. The larger patch series of which this patch is a part was also reviewed and tested by Antonin Houska, Rajkumar Raghuwanshi, David Rowley, Dilip Kumar, Konstantin Knizhnik, Pascal Legrand, Rafia Sabih, and me. Discussion: http://postgr.es/m/CAFjFpRewpqCmVkwvq6qrRjmbMDpN0CZvRRzjd8UvncczA3Oz1Q@mail.gmail.com
This commit is contained in:
		
							parent
							
								
									a446a1c70f
								
							
						
					
					
						commit
						1466bcfa4a
					
				@ -141,6 +141,16 @@ static RelOptInfo *create_grouping_paths(PlannerInfo *root,
 | 
				
			|||||||
					  bool target_parallel_safe,
 | 
										  bool target_parallel_safe,
 | 
				
			||||||
					  const AggClauseCosts *agg_costs,
 | 
										  const AggClauseCosts *agg_costs,
 | 
				
			||||||
					  grouping_sets_data *gd);
 | 
										  grouping_sets_data *gd);
 | 
				
			||||||
 | 
					static bool is_degenerate_grouping(PlannerInfo *root);
 | 
				
			||||||
 | 
					static void create_degenerate_grouping_paths(PlannerInfo *root,
 | 
				
			||||||
 | 
													 RelOptInfo *input_rel,
 | 
				
			||||||
 | 
													 PathTarget *target, RelOptInfo *grouped_rel);
 | 
				
			||||||
 | 
					static void create_ordinary_grouping_paths(PlannerInfo *root,
 | 
				
			||||||
 | 
												   RelOptInfo *input_rel,
 | 
				
			||||||
 | 
												   PathTarget *target, RelOptInfo *grouped_rel,
 | 
				
			||||||
 | 
												   RelOptInfo *partially_grouped_rel,
 | 
				
			||||||
 | 
												   const AggClauseCosts *agg_costs,
 | 
				
			||||||
 | 
												   grouping_sets_data *gd);
 | 
				
			||||||
static void consider_groupingsets_paths(PlannerInfo *root,
 | 
					static void consider_groupingsets_paths(PlannerInfo *root,
 | 
				
			||||||
							RelOptInfo *grouped_rel,
 | 
												RelOptInfo *grouped_rel,
 | 
				
			||||||
							Path *path,
 | 
												Path *path,
 | 
				
			||||||
@ -3667,11 +3677,6 @@ estimate_hashagg_tablesize(Path *path, const AggClauseCosts *agg_costs,
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * Note: all Paths in input_rel are expected to return the target computed
 | 
					 * Note: all Paths in input_rel are expected to return the target computed
 | 
				
			||||||
 * by make_group_input_target.
 | 
					 * by make_group_input_target.
 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * We need to consider sorted and hashed aggregation in the same function,
 | 
					 | 
				
			||||||
 * because otherwise (1) it would be harder to throw an appropriate error
 | 
					 | 
				
			||||||
 * message if neither way works, and (2) we should not allow hashtable size
 | 
					 | 
				
			||||||
 * considerations to dissuade us from using hashing if sorting is not possible.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static RelOptInfo *
 | 
					static RelOptInfo *
 | 
				
			||||||
create_grouping_paths(PlannerInfo *root,
 | 
					create_grouping_paths(PlannerInfo *root,
 | 
				
			||||||
@ -3682,15 +3687,8 @@ create_grouping_paths(PlannerInfo *root,
 | 
				
			|||||||
					  grouping_sets_data *gd)
 | 
										  grouping_sets_data *gd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Query	   *parse = root->parse;
 | 
						Query	   *parse = root->parse;
 | 
				
			||||||
	Path	   *cheapest_path = input_rel->cheapest_total_path;
 | 
					 | 
				
			||||||
	RelOptInfo *grouped_rel;
 | 
						RelOptInfo *grouped_rel;
 | 
				
			||||||
	RelOptInfo *partially_grouped_rel;
 | 
						RelOptInfo *partially_grouped_rel;
 | 
				
			||||||
	AggClauseCosts agg_partial_costs;	/* parallel only */
 | 
					 | 
				
			||||||
	AggClauseCosts agg_final_costs; /* parallel only */
 | 
					 | 
				
			||||||
	double		dNumGroups;
 | 
					 | 
				
			||||||
	bool		can_hash;
 | 
					 | 
				
			||||||
	bool		can_sort;
 | 
					 | 
				
			||||||
	bool		try_parallel_aggregation;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * For now, all aggregated paths are added to the (GROUP_AGG, NULL)
 | 
						 * For now, all aggregated paths are added to the (GROUP_AGG, NULL)
 | 
				
			||||||
@ -3728,73 +3726,123 @@ create_grouping_paths(PlannerInfo *root,
 | 
				
			|||||||
	partially_grouped_rel->fdwroutine = input_rel->fdwroutine;
 | 
						partially_grouped_rel->fdwroutine = input_rel->fdwroutine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Check for degenerate grouping.
 | 
						 * Create either paths for a degenerate grouping or paths for ordinary
 | 
				
			||||||
 | 
						 * grouping, as appropriate.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if ((root->hasHavingQual || parse->groupingSets) &&
 | 
						if (is_degenerate_grouping(root))
 | 
				
			||||||
		!parse->hasAggs && parse->groupClause == NIL)
 | 
							create_degenerate_grouping_paths(root, input_rel, target, grouped_rel);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							create_ordinary_grouping_paths(root, input_rel, target, grouped_rel,
 | 
				
			||||||
 | 
														   partially_grouped_rel, agg_costs, gd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_cheapest(grouped_rel);
 | 
				
			||||||
 | 
						return grouped_rel;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * is_degenerate_grouping
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * A degenerate grouping is one in which the query has a HAVING qual and/or
 | 
				
			||||||
 | 
					 * grouping sets, but no aggregates and no GROUP BY (which implies that the
 | 
				
			||||||
 | 
					 * grouping sets are all empty).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					is_degenerate_grouping(PlannerInfo *root)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Query	   *parse = root->parse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (root->hasHavingQual || parse->groupingSets) &&
 | 
				
			||||||
 | 
							!parse->hasAggs && parse->groupClause == NIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * create_degenerate_grouping_paths
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * When the grouping is degenerate (see is_degenerate_grouping), we are
 | 
				
			||||||
 | 
					 * supposed to emit either zero or one row for each grouping set depending on
 | 
				
			||||||
 | 
					 * whether HAVING succeeds.  Furthermore, there cannot be any variables in
 | 
				
			||||||
 | 
					 * either HAVING or the targetlist, so we actually do not need the FROM table
 | 
				
			||||||
 | 
					 * at all! We can just throw away the plan-so-far and generate a Result node.
 | 
				
			||||||
 | 
					 * This is a sufficiently unusual corner case that it's not worth contorting
 | 
				
			||||||
 | 
					 * the structure of this module to avoid having to generate the earlier paths
 | 
				
			||||||
 | 
					 * in the first place.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					create_degenerate_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
 | 
				
			||||||
 | 
													 PathTarget *target, RelOptInfo *grouped_rel)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Query	   *parse = root->parse;
 | 
				
			||||||
 | 
						int			nrows;
 | 
				
			||||||
 | 
						Path	   *path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nrows = list_length(parse->groupingSets);
 | 
				
			||||||
 | 
						if (nrows > 1)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * We have a HAVING qual and/or grouping sets, but no aggregates and
 | 
							 * Doesn't seem worthwhile writing code to cons up a generate_series
 | 
				
			||||||
		 * no GROUP BY (which implies that the grouping sets are all empty).
 | 
							 * or a values scan to emit multiple rows. Instead just make N clones
 | 
				
			||||||
		 *
 | 
							 * and append them.  (With a volatile HAVING clause, this means you
 | 
				
			||||||
		 * This is a degenerate case in which we are supposed to emit either
 | 
							 * might get between 0 and N output rows. Offhand I think that's
 | 
				
			||||||
		 * zero or one row for each grouping set depending on whether HAVING
 | 
							 * desired.)
 | 
				
			||||||
		 * succeeds.  Furthermore, there cannot be any variables in either
 | 
					 | 
				
			||||||
		 * HAVING or the targetlist, so we actually do not need the FROM table
 | 
					 | 
				
			||||||
		 * at all!	We can just throw away the plan-so-far and generate a
 | 
					 | 
				
			||||||
		 * Result node.  This is a sufficiently unusual corner case that it's
 | 
					 | 
				
			||||||
		 * not worth contorting the structure of this module to avoid having
 | 
					 | 
				
			||||||
		 * to generate the earlier paths in the first place.
 | 
					 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		int			nrows = list_length(parse->groupingSets);
 | 
							List	   *paths = NIL;
 | 
				
			||||||
		Path	   *path;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (nrows > 1)
 | 
							while (--nrows >= 0)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			/*
 | 
					 | 
				
			||||||
			 * Doesn't seem worthwhile writing code to cons up a
 | 
					 | 
				
			||||||
			 * generate_series or a values scan to emit multiple rows. Instead
 | 
					 | 
				
			||||||
			 * just make N clones and append them.  (With a volatile HAVING
 | 
					 | 
				
			||||||
			 * clause, this means you might get between 0 and N output rows.
 | 
					 | 
				
			||||||
			 * Offhand I think that's desired.)
 | 
					 | 
				
			||||||
			 */
 | 
					 | 
				
			||||||
			List	   *paths = NIL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			while (--nrows >= 0)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				path = (Path *)
 | 
					 | 
				
			||||||
					create_result_path(root, grouped_rel,
 | 
					 | 
				
			||||||
									   target,
 | 
					 | 
				
			||||||
									   (List *) parse->havingQual);
 | 
					 | 
				
			||||||
				paths = lappend(paths, path);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			path = (Path *)
 | 
					 | 
				
			||||||
				create_append_path(grouped_rel,
 | 
					 | 
				
			||||||
								   paths,
 | 
					 | 
				
			||||||
								   NIL,
 | 
					 | 
				
			||||||
								   NULL,
 | 
					 | 
				
			||||||
								   0,
 | 
					 | 
				
			||||||
								   false,
 | 
					 | 
				
			||||||
								   NIL,
 | 
					 | 
				
			||||||
								   -1);
 | 
					 | 
				
			||||||
			path->pathtarget = target;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			/* No grouping sets, or just one, so one output row */
 | 
					 | 
				
			||||||
			path = (Path *)
 | 
								path = (Path *)
 | 
				
			||||||
				create_result_path(root, grouped_rel,
 | 
									create_result_path(root, grouped_rel,
 | 
				
			||||||
								   target,
 | 
													   target,
 | 
				
			||||||
								   (List *) parse->havingQual);
 | 
													   (List *) parse->havingQual);
 | 
				
			||||||
 | 
								paths = lappend(paths, path);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							path = (Path *)
 | 
				
			||||||
		add_path(grouped_rel, path);
 | 
								create_append_path(grouped_rel,
 | 
				
			||||||
 | 
												   paths,
 | 
				
			||||||
		/* No need to consider any other alternatives. */
 | 
												   NIL,
 | 
				
			||||||
		set_cheapest(grouped_rel);
 | 
												   NULL,
 | 
				
			||||||
 | 
												   0,
 | 
				
			||||||
		return grouped_rel;
 | 
												   false,
 | 
				
			||||||
 | 
												   NIL,
 | 
				
			||||||
 | 
												   -1);
 | 
				
			||||||
 | 
							path->pathtarget = target;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* No grouping sets, or just one, so one output row */
 | 
				
			||||||
 | 
							path = (Path *)
 | 
				
			||||||
 | 
								create_result_path(root, grouped_rel,
 | 
				
			||||||
 | 
												   target,
 | 
				
			||||||
 | 
												   (List *) parse->havingQual);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						add_path(grouped_rel, path);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * create_ordinary_grouping_paths
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Create grouping paths for the ordinary (that is, non-degenerate) case.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * We need to consider sorted and hashed aggregation in the same function,
 | 
				
			||||||
 | 
					 * because otherwise (1) it would be harder to throw an appropriate error
 | 
				
			||||||
 | 
					 * message if neither way works, and (2) we should not allow hashtable size
 | 
				
			||||||
 | 
					 * considerations to dissuade us from using hashing if sorting is not possible.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
 | 
				
			||||||
 | 
												   PathTarget *target, RelOptInfo *grouped_rel,
 | 
				
			||||||
 | 
												   RelOptInfo *partially_grouped_rel,
 | 
				
			||||||
 | 
												   const AggClauseCosts *agg_costs,
 | 
				
			||||||
 | 
												   grouping_sets_data *gd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Query	   *parse = root->parse;
 | 
				
			||||||
 | 
						Path	   *cheapest_path = input_rel->cheapest_total_path;
 | 
				
			||||||
 | 
						AggClauseCosts agg_partial_costs;	/* parallel only */
 | 
				
			||||||
 | 
						AggClauseCosts agg_final_costs; /* parallel only */
 | 
				
			||||||
 | 
						double		dNumGroups;
 | 
				
			||||||
 | 
						bool		can_hash;
 | 
				
			||||||
 | 
						bool		can_sort;
 | 
				
			||||||
 | 
						bool		try_parallel_aggregation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Estimate number of groups.
 | 
						 * Estimate number of groups.
 | 
				
			||||||
@ -3922,14 +3970,8 @@ create_grouping_paths(PlannerInfo *root,
 | 
				
			|||||||
	if (create_upper_paths_hook)
 | 
						if (create_upper_paths_hook)
 | 
				
			||||||
		(*create_upper_paths_hook) (root, UPPERREL_GROUP_AGG,
 | 
							(*create_upper_paths_hook) (root, UPPERREL_GROUP_AGG,
 | 
				
			||||||
									input_rel, grouped_rel);
 | 
														input_rel, grouped_rel);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Now choose the best path(s) */
 | 
					 | 
				
			||||||
	set_cheapest(grouped_rel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return grouped_rel;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * For a given input path, consider the possible ways of doing grouping sets on
 | 
					 * For a given input path, consider the possible ways of doing grouping sets on
 | 
				
			||||||
 * it, by combinations of hashing and sorting.  This can be called multiple
 | 
					 * it, by combinations of hashing and sorting.  This can be called multiple
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user