diff options
Diffstat (limited to 'src/backend/optimizer/plan/planner.c')
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 165 |
1 files changed, 131 insertions, 34 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 0c9397a36c3..e8ea78c0c97 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -73,6 +73,12 @@ bool enable_distinct_reordering = true; /* Hook for plugins to get control in planner() */ planner_hook_type planner_hook = NULL; +/* Hook for plugins to get control after PlannerGlobal is initialized */ +planner_setup_hook_type planner_setup_hook = NULL; + +/* Hook for plugins to get control before PlannerGlobal is discarded */ +planner_shutdown_hook_type planner_shutdown_hook = NULL; + /* Hook for plugins to get control when grouping_planner() plans upper rels */ create_upper_paths_hook_type create_upper_paths_hook = NULL; @@ -232,7 +238,6 @@ static void add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *partially_grouped_rel, const AggClauseCosts *agg_costs, grouping_sets_data *gd, - double dNumGroups, GroupPathExtraData *extra); static RelOptInfo *create_partial_grouping_paths(PlannerInfo *root, RelOptInfo *grouped_rel, @@ -280,6 +285,23 @@ static void create_partial_unique_paths(PlannerInfo *root, RelOptInfo *input_rel * * Query optimizer entry point * + * Inputs: + * parse: an analyzed-and-rewritten query tree for an optimizable statement + * query_string: source text for the query tree (used for error reports) + * cursorOptions: bitmask of CURSOR_OPT_XXX flags, see parsenodes.h + * boundParams: passed-in parameter values, or NULL if none + * es: ExplainState if being called from EXPLAIN, else NULL + * + * The result is a PlannedStmt tree. + * + * PARAM_EXTERN Param nodes within the parse tree can be replaced by Consts + * using values from boundParams, if those values are marked PARAM_FLAG_CONST. + * Parameter values not so marked are still relied on for estimation purposes. + * + * The ExplainState pointer is not currently used by the core planner, but it + * is passed through to some planner hooks so that they can report information + * back to EXPLAIN extension hooks. + * * To support loadable plugins that monitor or modify planner behavior, * we provide a hook variable that lets a plugin get control before and * after the standard planning process. The plugin would normally call @@ -291,14 +313,16 @@ static void create_partial_unique_paths(PlannerInfo *root, RelOptInfo *input_rel *****************************************************************************/ PlannedStmt * planner(Query *parse, const char *query_string, int cursorOptions, - ParamListInfo boundParams) + ParamListInfo boundParams, ExplainState *es) { PlannedStmt *result; if (planner_hook) - result = (*planner_hook) (parse, query_string, cursorOptions, boundParams); + result = (*planner_hook) (parse, query_string, cursorOptions, + boundParams, es); else - result = standard_planner(parse, query_string, cursorOptions, boundParams); + result = standard_planner(parse, query_string, cursorOptions, + boundParams, es); pgstat_report_plan_id(result->planId, false); @@ -307,7 +331,7 @@ planner(Query *parse, const char *query_string, int cursorOptions, PlannedStmt * standard_planner(Query *parse, const char *query_string, int cursorOptions, - ParamListInfo boundParams) + ParamListInfo boundParams, ExplainState *es) { PlannedStmt *result; PlannerGlobal *glob; @@ -438,6 +462,10 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, tuple_fraction = 0.0; } + /* Allow plugins to take control after we've initialized "glob" */ + if (planner_setup_hook) + (*planner_setup_hook) (glob, parse, query_string, &tuple_fraction, es); + /* primary planning entry point (may recurse for subqueries) */ root = subquery_planner(glob, parse, NULL, NULL, false, tuple_fraction, NULL); @@ -617,6 +645,10 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->jitFlags |= PGJIT_DEFORM; } + /* Allow plugins to take control before we discard "glob" */ + if (planner_shutdown_hook) + (*planner_shutdown_hook) (glob, parse, query_string, result); + if (glob->partition_directory != NULL) DestroyPartitionDirectory(glob->partition_directory); @@ -4014,9 +4046,7 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, GroupPathExtraData *extra, RelOptInfo **partially_grouped_rel_p) { - Path *cheapest_path = input_rel->cheapest_total_path; RelOptInfo *partially_grouped_rel = NULL; - double dNumGroups; PartitionwiseAggregateType patype = PARTITIONWISE_AGGREGATE_NONE; /* @@ -4098,23 +4128,16 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, /* Gather any partially grouped partial paths. */ if (partially_grouped_rel && partially_grouped_rel->partial_pathlist) - { gather_grouping_paths(root, partially_grouped_rel); - set_cheapest(partially_grouped_rel); - } - /* - * Estimate number of groups. - */ - dNumGroups = get_number_of_groups(root, - cheapest_path->rows, - gd, - extra->targetList); + /* Now choose the best path(s) for partially_grouped_rel. */ + if (partially_grouped_rel && partially_grouped_rel->pathlist) + set_cheapest(partially_grouped_rel); /* Build final grouping paths */ add_paths_to_grouping_rel(root, input_rel, grouped_rel, partially_grouped_rel, agg_costs, gd, - dNumGroups, extra); + extra); /* Give a helpful error if we failed to find any implementation */ if (grouped_rel->pathlist == NIL) @@ -7059,16 +7082,42 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, RelOptInfo *partially_grouped_rel, const AggClauseCosts *agg_costs, - grouping_sets_data *gd, double dNumGroups, + grouping_sets_data *gd, GroupPathExtraData *extra) { Query *parse = root->parse; Path *cheapest_path = input_rel->cheapest_total_path; + Path *cheapest_partially_grouped_path = NULL; ListCell *lc; bool can_hash = (extra->flags & GROUPING_CAN_USE_HASH) != 0; bool can_sort = (extra->flags & GROUPING_CAN_USE_SORT) != 0; List *havingQual = (List *) extra->havingQual; AggClauseCosts *agg_final_costs = &extra->agg_final_costs; + double dNumGroups = 0; + double dNumFinalGroups = 0; + + /* + * Estimate number of groups for non-split aggregation. + */ + dNumGroups = get_number_of_groups(root, + cheapest_path->rows, + gd, + extra->targetList); + + if (partially_grouped_rel && partially_grouped_rel->pathlist) + { + cheapest_partially_grouped_path = + partially_grouped_rel->cheapest_total_path; + + /* + * Estimate number of groups for final phase of partial aggregation. + */ + dNumFinalGroups = + get_number_of_groups(root, + cheapest_partially_grouped_path->rows, + gd, + extra->targetList); + } if (can_sort) { @@ -7181,7 +7230,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel, path = make_ordered_path(root, grouped_rel, path, - partially_grouped_rel->cheapest_total_path, + cheapest_partially_grouped_path, info->pathkeys, -1.0); @@ -7199,7 +7248,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel, info->clauses, havingQual, agg_final_costs, - dNumGroups)); + dNumFinalGroups)); else add_path(grouped_rel, (Path *) create_group_path(root, @@ -7207,7 +7256,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel, path, info->clauses, havingQual, - dNumGroups)); + dNumFinalGroups)); } } @@ -7249,19 +7298,17 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel, */ if (partially_grouped_rel && partially_grouped_rel->pathlist) { - Path *path = partially_grouped_rel->cheapest_total_path; - add_path(grouped_rel, (Path *) create_agg_path(root, grouped_rel, - path, + cheapest_partially_grouped_path, grouped_rel->reltarget, AGG_HASHED, AGGSPLIT_FINAL_DESERIAL, root->processed_groupClause, havingQual, agg_final_costs, - dNumGroups)); + dNumFinalGroups)); } } @@ -7301,6 +7348,7 @@ create_partial_grouping_paths(PlannerInfo *root, { Query *parse = root->parse; RelOptInfo *partially_grouped_rel; + RelOptInfo *eager_agg_rel = NULL; AggClauseCosts *agg_partial_costs = &extra->agg_partial_costs; AggClauseCosts *agg_final_costs = &extra->agg_final_costs; Path *cheapest_partial_path = NULL; @@ -7312,6 +7360,15 @@ create_partial_grouping_paths(PlannerInfo *root, bool can_sort = (extra->flags & GROUPING_CAN_USE_SORT) != 0; /* + * Check whether any partially aggregated paths have been generated + * through eager aggregation. + */ + if (input_rel->grouped_rel && + !IS_DUMMY_REL(input_rel->grouped_rel) && + input_rel->grouped_rel->pathlist != NIL) + eager_agg_rel = input_rel->grouped_rel; + + /* * Consider whether we should generate partially aggregated non-partial * paths. We can only do this if we have a non-partial path, and only if * the parent of the input rel is performing partial partitionwise @@ -7332,11 +7389,13 @@ create_partial_grouping_paths(PlannerInfo *root, /* * If we can't partially aggregate partial paths, and we can't partially - * aggregate non-partial paths, then don't bother creating the new + * aggregate non-partial paths, and no partially aggregated paths were + * generated by eager aggregation, then don't bother creating the new * RelOptInfo at all, unless the caller specified force_rel_creation. */ if (cheapest_total_path == NULL && cheapest_partial_path == NULL && + eager_agg_rel == NULL && !force_rel_creation) return NULL; @@ -7562,6 +7621,51 @@ create_partial_grouping_paths(PlannerInfo *root, } /* + * Add any partially aggregated paths generated by eager aggregation to + * the new upper relation after applying projection steps as needed. + */ + if (eager_agg_rel) + { + /* Add the paths */ + foreach(lc, eager_agg_rel->pathlist) + { + Path *path = (Path *) lfirst(lc); + + /* Shouldn't have any parameterized paths anymore */ + Assert(path->param_info == NULL); + + path = (Path *) create_projection_path(root, + partially_grouped_rel, + path, + partially_grouped_rel->reltarget); + + add_path(partially_grouped_rel, path); + } + + /* + * Likewise add the partial paths, but only if parallelism is possible + * for partially_grouped_rel. + */ + if (partially_grouped_rel->consider_parallel) + { + foreach(lc, eager_agg_rel->partial_pathlist) + { + Path *path = (Path *) lfirst(lc); + + /* Shouldn't have any parameterized paths anymore */ + Assert(path->param_info == NULL); + + path = (Path *) create_projection_path(root, + partially_grouped_rel, + path, + partially_grouped_rel->reltarget); + + add_partial_path(partially_grouped_rel, path); + } + } + } + + /* * If there is an FDW that's responsible for all baserels of the query, * let it consider adding partially grouped ForeignPaths. */ @@ -8124,13 +8228,6 @@ create_partitionwise_grouping_paths(PlannerInfo *root, add_paths_to_append_rel(root, partially_grouped_rel, partially_grouped_live_children); - - /* - * We need call set_cheapest, since the finalization step will use the - * cheapest path from the rel. - */ - if (partially_grouped_rel->pathlist) - set_cheapest(partially_grouped_rel); } /* If possible, create append paths for fully grouped children. */ |