From 49635d7b3e86c0088eadd80db1563a210bc89efd Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 11 Mar 2016 10:24:33 -0500 Subject: [PATCH] Minor additional refactoring of planner.c's PathTarget handling. Teach make_group_input_target() and make_window_input_target() to work entirely with the PathTarget representation of tlists, rather than constructing a tlist and immediately deconstructing it into PathTarget format. In itself this only saves a few palloc's; the bigger picture is that it opens the door for sharing cost_qual_eval work across all of planner.c's constructions of PathTargets. I'll come back to that later. In support of this, flesh out tlist.c's infrastructure for PathTargets a bit more. --- src/backend/optimizer/plan/planner.c | 126 +++++++++++++-------------- src/backend/optimizer/util/tlist.c | 46 ++++++++++ src/include/optimizer/tlist.h | 3 + 3 files changed, 111 insertions(+), 64 deletions(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index a2cd6deb61..9d5f9ca62e 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -128,11 +128,13 @@ static RelOptInfo *create_distinct_paths(PlannerInfo *root, static RelOptInfo *create_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, double limit_tuples); -static PathTarget *make_group_input_target(PlannerInfo *root, List *tlist); +static PathTarget *make_group_input_target(PlannerInfo *root, + PathTarget *final_target); static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist); static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists); static PathTarget *make_window_input_target(PlannerInfo *root, - List *tlist, List *activeWindows); + PathTarget *final_target, + List *activeWindows); static List *make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc, List *tlist); @@ -1664,7 +1666,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, */ if (activeWindows) grouping_target = make_window_input_target(root, - tlist, + final_target, activeWindows); else grouping_target = final_target; @@ -1678,7 +1680,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, have_grouping = (parse->groupClause || parse->groupingSets || parse->hasAggs || root->hasHavingQual); if (have_grouping) - scanjoin_target = make_group_input_target(root, tlist); + scanjoin_target = make_group_input_target(root, final_target); else scanjoin_target = grouping_target; @@ -3758,10 +3760,10 @@ create_ordered_paths(PlannerInfo *root, * * If there is grouping or aggregation, the scan/join subplan cannot emit * the query's final targetlist; for example, it certainly can't emit any - * aggregate function calls. This routine generates the correct target list + * aggregate function calls. This routine generates the correct target * for the scan/join subplan. * - * The initial target list passed from the parser already contains entries + * The query target list passed from the parser already contains entries * for all ORDER BY and GROUP BY expressions, but it will not have entries * for variables used only in HAVING clauses; so we need to add those * variables to the subplan target list. Also, we flatten all expressions @@ -3774,56 +3776,52 @@ create_ordered_paths(PlannerInfo *root, * where the a+b target will be used by the Sort/Group steps, and the * other targets will be used for computing the final results. * - * 'tlist' is the query's final target list. + * 'final_target' is the query's final target list (in PathTarget form) * * The result is the PathTarget to be computed by the Paths returned from * query_planner(). */ static PathTarget * -make_group_input_target(PlannerInfo *root, List *tlist) +make_group_input_target(PlannerInfo *root, PathTarget *final_target) { Query *parse = root->parse; - List *sub_tlist; + PathTarget *input_target; List *non_group_cols; List *non_group_vars; - ListCell *tl; + int i; + ListCell *lc; /* - * We must build a tlist containing all grouping columns, plus any other - * Vars mentioned in the targetlist and HAVING qual. + * We must build a target containing all grouping columns, plus any other + * Vars mentioned in the query's targetlist and HAVING qual. */ - sub_tlist = NIL; + input_target = create_empty_pathtarget(); non_group_cols = NIL; - foreach(tl, tlist) + i = 0; + foreach(lc, final_target->exprs) { - TargetEntry *tle = (TargetEntry *) lfirst(tl); + Expr *expr = (Expr *) lfirst(lc); + Index sgref = final_target->sortgrouprefs[i]; - if (tle->ressortgroupref && parse->groupClause && - get_sortgroupref_clause_noerr(tle->ressortgroupref, - parse->groupClause) != NULL) + if (sgref && parse->groupClause && + get_sortgroupref_clause_noerr(sgref, parse->groupClause) != NULL) { /* - * It's a grouping column, so add it to the result tlist as-is. + * It's a grouping column, so add it to the input target as-is. */ - TargetEntry *newtle; - - newtle = makeTargetEntry(tle->expr, - list_length(sub_tlist) + 1, - NULL, - false); - newtle->ressortgroupref = tle->ressortgroupref; - sub_tlist = lappend(sub_tlist, newtle); + add_column_to_pathtarget(input_target, expr, sgref); } else { /* * Non-grouping column, so just remember the expression for later - * call to pull_var_clause. There's no need for pull_var_clause - * to examine the TargetEntry node itself. + * call to pull_var_clause. */ - non_group_cols = lappend(non_group_cols, tle->expr); + non_group_cols = lappend(non_group_cols, expr); } + + i++; } /* @@ -3834,7 +3832,7 @@ make_group_input_target(PlannerInfo *root, List *tlist) /* * Pull out all the Vars mentioned in non-group cols (plus HAVING), and - * add them to the result tlist if not already present. (A Var used + * add them to the input target if not already present. (A Var used * directly as a GROUP BY item will be present already.) Note this * includes Vars used in resjunk items, so we are covering the needs of * ORDER BY and window specifications. Vars used within Aggrefs and @@ -3844,13 +3842,14 @@ make_group_input_target(PlannerInfo *root, List *tlist) PVC_RECURSE_AGGREGATES | PVC_RECURSE_WINDOWFUNCS | PVC_INCLUDE_PLACEHOLDERS); - sub_tlist = add_to_flat_tlist(sub_tlist, non_group_vars); + add_new_columns_to_pathtarget(input_target, non_group_vars); /* clean up cruft */ list_free(non_group_vars); list_free(non_group_cols); - return create_pathtarget(root, sub_tlist); + /* XXX this causes some redundant cost calculation ... */ + return set_pathtarget_cost_width(root, input_target); } /* @@ -3964,13 +3963,13 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists) * make_window_input_target * Generate appropriate PathTarget for initial input to WindowAgg nodes. * - * When the query has window functions, this function computes the initial - * target list to be computed by the node just below the first WindowAgg. + * When the query has window functions, this function computes the desired + * target to be computed by the node just below the first WindowAgg. * This tlist must contain all values needed to evaluate the window functions, * compute the final target list, and perform any required final sort step. * If multiple WindowAggs are needed, each intermediate one adds its window - * function results onto this tlist; only the topmost WindowAgg computes the - * actual desired target list. + * function results onto this base tlist; only the topmost WindowAgg computes + * the actual desired target list. * * This function is much like make_group_input_target, though not quite enough * like it to share code. As in that function, we flatten most expressions @@ -3986,7 +3985,7 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists) * flatten Aggref expressions, since those are to be computed below the * window functions and just referenced like Vars above that. * - * 'tlist' is the query's final target list. + * 'final_target' is the query's final target list (in PathTarget form) * 'activeWindows' is the list of active windows previously identified by * select_active_windows. * @@ -3995,14 +3994,15 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists) */ static PathTarget * make_window_input_target(PlannerInfo *root, - List *tlist, + PathTarget *final_target, List *activeWindows) { Query *parse = root->parse; + PathTarget *input_target; Bitmapset *sgrefs; - List *new_tlist; List *flattenable_cols; List *flattenable_vars; + int i; ListCell *lc; Assert(parse->hasWindowFuncs); @@ -4040,52 +4040,49 @@ make_window_input_target(PlannerInfo *root, } /* - * Construct a tlist containing all the non-flattenable tlist items, and - * save aside the others for a moment. + * Construct a target containing all the non-flattenable targetlist items, + * and save aside the others for a moment. */ - new_tlist = NIL; + input_target = create_empty_pathtarget(); flattenable_cols = NIL; - foreach(lc, tlist) + i = 0; + foreach(lc, final_target->exprs) { - TargetEntry *tle = (TargetEntry *) lfirst(lc); + Expr *expr = (Expr *) lfirst(lc); + Index sgref = final_target->sortgrouprefs[i]; /* * Don't want to deconstruct window clauses or GROUP BY items. (Note * that such items can't contain window functions, so it's okay to * compute them below the WindowAgg nodes.) */ - if (tle->ressortgroupref != 0 && - bms_is_member(tle->ressortgroupref, sgrefs)) + if (sgref != 0 && bms_is_member(sgref, sgrefs)) { - /* Don't want to deconstruct this value, so add to new_tlist */ - TargetEntry *newtle; - - newtle = makeTargetEntry(tle->expr, - list_length(new_tlist) + 1, - NULL, - false); - /* Preserve its sortgroupref marking, in case it's volatile */ - newtle->ressortgroupref = tle->ressortgroupref; - new_tlist = lappend(new_tlist, newtle); + /* + * Don't want to deconstruct this value, so add it to the input + * target as-is. + */ + add_column_to_pathtarget(input_target, expr, sgref); } else { /* * Column is to be flattened, so just remember the expression for - * later call to pull_var_clause. There's no need for - * pull_var_clause to examine the TargetEntry node itself. + * later call to pull_var_clause. */ - flattenable_cols = lappend(flattenable_cols, tle->expr); + flattenable_cols = lappend(flattenable_cols, expr); } + + i++; } /* * Pull out all the Vars and Aggrefs mentioned in flattenable columns, and - * add them to the result tlist if not already present. (Some might be + * add them to the input target if not already present. (Some might be * there already because they're used directly as window/group clauses.) * - * Note: it's essential to use PVC_INCLUDE_AGGREGATES here, so that the + * Note: it's essential to use PVC_INCLUDE_AGGREGATES here, so that any * Aggrefs are placed in the Agg node's tlist and not left to be computed * at higher levels. On the other hand, we should recurse into * WindowFuncs to make sure their input expressions are available. @@ -4094,13 +4091,14 @@ make_window_input_target(PlannerInfo *root, PVC_INCLUDE_AGGREGATES | PVC_RECURSE_WINDOWFUNCS | PVC_INCLUDE_PLACEHOLDERS); - new_tlist = add_to_flat_tlist(new_tlist, flattenable_vars); + add_new_columns_to_pathtarget(input_target, flattenable_vars); /* clean up cruft */ list_free(flattenable_vars); list_free(flattenable_cols); - return create_pathtarget(root, new_tlist); + /* XXX this causes some redundant cost calculation ... */ + return set_pathtarget_cost_width(root, input_target); } /* diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index cbc8c2b9fe..9f85dee387 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -623,6 +623,17 @@ copy_pathtarget(PathTarget *src) return dst; } +/* + * create_empty_pathtarget + * Create an empty (zero columns, zero cost) PathTarget. + */ +PathTarget * +create_empty_pathtarget(void) +{ + /* This is easy, but we don't want callers to hard-wire this ... */ + return (PathTarget *) palloc0(sizeof(PathTarget)); +} + /* * add_column_to_pathtarget * Append a target column to the PathTarget. @@ -655,6 +666,41 @@ add_column_to_pathtarget(PathTarget *target, Expr *expr, Index sortgroupref) } } +/* + * add_new_column_to_pathtarget + * Append a target column to the PathTarget, but only if it's not + * equal() to any pre-existing target expression. + * + * The caller cannot specify a sortgroupref, since it would be unclear how + * to merge that with a pre-existing column. + * + * As with make_pathtarget_from_tlist, we leave it to the caller to update + * the cost and width fields. + */ +void +add_new_column_to_pathtarget(PathTarget *target, Expr *expr) +{ + if (!list_member(target->exprs, expr)) + add_column_to_pathtarget(target, expr, 0); +} + +/* + * add_new_columns_to_pathtarget + * Apply add_new_column_to_pathtarget() for each element of the list. + */ +void +add_new_columns_to_pathtarget(PathTarget *target, List *exprs) +{ + ListCell *lc; + + foreach(lc, exprs) + { + Expr *expr = (Expr *) lfirst(lc); + + add_new_column_to_pathtarget(target, expr); + } +} + /* * apply_pathtarget_labeling_to_tlist * Apply any sortgrouprefs in the PathTarget to matching tlist entries diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h index 2c79a80391..0d745a0891 100644 --- a/src/include/optimizer/tlist.h +++ b/src/include/optimizer/tlist.h @@ -55,8 +55,11 @@ extern bool grouping_is_hashable(List *groupClause); extern PathTarget *make_pathtarget_from_tlist(List *tlist); extern List *make_tlist_from_pathtarget(PathTarget *target); extern PathTarget *copy_pathtarget(PathTarget *src); +extern PathTarget *create_empty_pathtarget(void); extern void add_column_to_pathtarget(PathTarget *target, Expr *expr, Index sortgroupref); +extern void add_new_column_to_pathtarget(PathTarget *target, Expr *expr); +extern void add_new_columns_to_pathtarget(PathTarget *target, List *exprs); extern void apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target); /* Convenience macro to get a PathTarget with valid cost/width fields */