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.
This commit is contained in:
Tom Lane 2016-03-11 10:24:33 -05:00
parent 92f03fe76f
commit 49635d7b3e
3 changed files with 111 additions and 64 deletions

View File

@ -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);
}
/*

View File

@ -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

View File

@ -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 */