aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/optimizer/plan/planner.c235
-rw-r--r--src/backend/optimizer/util/clauses.c10
-rw-r--r--src/backend/optimizer/util/tlist.c60
-rw-r--r--src/include/optimizer/tlist.h3
4 files changed, 190 insertions, 118 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 896f73d3e49..8937e717d06 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -50,6 +50,7 @@
#include "storage/dsm_impl.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -108,14 +109,16 @@ static RelOptInfo *create_grouping_paths(PlannerInfo *root,
List *rollup_groupclauses);
static RelOptInfo *create_window_paths(PlannerInfo *root,
RelOptInfo *input_rel,
- List *base_tlist,
+ PathTarget *input_target,
+ PathTarget *output_target,
List *tlist,
WindowFuncLists *wflists,
List *activeWindows);
static void create_one_window_path(PlannerInfo *root,
RelOptInfo *window_rel,
Path *path,
- List *base_tlist,
+ PathTarget *input_target,
+ PathTarget *output_target,
List *tlist,
WindowFuncLists *wflists,
List *activeWindows);
@@ -124,11 +127,11 @@ static RelOptInfo *create_distinct_paths(PlannerInfo *root,
static RelOptInfo *create_ordered_paths(PlannerInfo *root,
RelOptInfo *input_rel,
double limit_tuples);
-static PathTarget *make_scanjoin_target(PlannerInfo *root, List *tlist);
+static PathTarget *make_group_input_target(PlannerInfo *root, List *tlist);
static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
-static List *make_windowInputTargetList(PlannerInfo *root,
- List *tlist, List *activeWindows);
+static PathTarget *make_window_input_target(PlannerInfo *root,
+ List *tlist, List *activeWindows);
static List *make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
List *tlist);
@@ -1458,9 +1461,11 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
else
{
/* No set operations, do regular planning */
- PathTarget *sub_target;
+ PathTarget *final_target;
+ PathTarget *grouping_target;
+ PathTarget *scanjoin_target;
+ bool have_grouping;
double tlist_rows;
- List *grouping_tlist;
WindowFuncLists *wflists = NULL;
List *activeWindows = NIL;
List *rollup_lists = NIL;
@@ -1643,19 +1648,41 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
standard_qp_callback, &qp_extra);
/*
- * Now determine the tlist that we want the topmost scan/join plan
- * node to emit; this may be different from the final tlist if
- * grouping or aggregation is needed. This is also a convenient spot
- * for conversion of the tlist to PathTarget format.
+ * Convert the query's result tlist into PathTarget format.
*
* Note: it's desirable to not do this till after query_planner(),
* because the target width estimates can use per-Var width numbers
* that were obtained within query_planner().
*/
- sub_target = make_scanjoin_target(root, tlist);
+ final_target = create_pathtarget(root, tlist);
/*
- * Forcibly apply that tlist to all the Paths for the scan/join rel.
+ * If we have window functions to deal with, the output from any
+ * grouping step needs to be what the window functions want;
+ * otherwise, it should just be final_target.
+ */
+ if (activeWindows)
+ grouping_target = make_window_input_target(root,
+ tlist,
+ activeWindows);
+ else
+ grouping_target = final_target;
+
+ /*
+ * If we have grouping or aggregation to do, the topmost scan/join
+ * plan node must emit what the grouping step wants; otherwise, if
+ * there's window functions, it must emit what the window functions
+ * want; otherwise, it should emit final_target.
+ */
+ have_grouping = (parse->groupClause || parse->groupingSets ||
+ parse->hasAggs || root->hasHavingQual);
+ if (have_grouping)
+ scanjoin_target = make_group_input_target(root, tlist);
+ else
+ scanjoin_target = grouping_target;
+
+ /*
+ * Forcibly apply that target to all the Paths for the scan/join rel.
*
* In principle we should re-run set_cheapest() here to identify the
* cheapest path, but it seems unlikely that adding the same tlist
@@ -1671,7 +1698,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
Assert(subpath->param_info == NULL);
path = apply_projection_to_path(root, current_rel,
- subpath, sub_target);
+ subpath, scanjoin_target);
/* If we had to add a Result, path is different from subpath */
if (path != subpath)
{
@@ -1684,31 +1711,15 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
}
/*
- * Determine the tlist we need grouping paths to emit. While we could
- * skip this if we're not going to call create_grouping_paths, it's
- * trivial unless we've got window functions, and then we have to do
- * the work anyway. (XXX: refactor to work with PathTargets instead
- * of tlists)
- */
- if (activeWindows)
- grouping_tlist = make_windowInputTargetList(root,
- tlist,
- activeWindows);
- else
- grouping_tlist = tlist;
-
- /*
* If we have grouping and/or aggregation, consider ways to implement
* that. We build a new upperrel representing the output of this
* phase.
*/
- if (parse->groupClause || parse->groupingSets || parse->hasAggs ||
- root->hasHavingQual)
+ if (have_grouping)
{
current_rel = create_grouping_paths(root,
current_rel,
- create_pathtarget(root,
- grouping_tlist),
+ grouping_target,
rollup_lists,
rollup_groupclauses);
}
@@ -1721,7 +1732,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
{
current_rel = create_window_paths(root,
current_rel,
- grouping_tlist,
+ grouping_target,
+ final_target,
tlist,
wflists,
activeWindows);
@@ -3075,6 +3087,9 @@ get_number_of_groups(PlannerInfo *root,
* rollup_groupclauses: list of grouping clauses for grouping sets,
* or NIL if not doing grouping sets
*
+ * Note: all Paths in input_rel are expected to return the target computed
+ * 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 enable_hashagg or
@@ -3356,15 +3371,19 @@ create_grouping_paths(PlannerInfo *root,
* Build a new upperrel containing Paths for window-function evaluation.
*
* input_rel: contains the source-data Paths
- * base_tlist: result of make_windowInputTargetList
- * tlist: query's final target list (which is what output paths should emit)
+ * input_target: result of make_window_input_target
+ * output_target: what the topmost WindowAggPath should return
+ * tlist: query's target list (needed to look up pathkeys)
* wflists: result of find_window_functions
* activeWindows: result of select_active_windows
+ *
+ * Note: all Paths in input_rel are expected to return input_target.
*/
static RelOptInfo *
create_window_paths(PlannerInfo *root,
RelOptInfo *input_rel,
- List *base_tlist,
+ PathTarget *input_target,
+ PathTarget *output_target,
List *tlist,
WindowFuncLists *wflists,
List *activeWindows)
@@ -3389,7 +3408,8 @@ create_window_paths(PlannerInfo *root,
create_one_window_path(root,
window_rel,
path,
- base_tlist,
+ input_target,
+ output_target,
tlist,
wflists,
activeWindows);
@@ -3406,9 +3426,10 @@ create_window_paths(PlannerInfo *root,
* add the result to window_rel.
*
* window_rel: upperrel to contain result
- * path: input Path to use
- * base_tlist: result of make_windowInputTargetList
- * tlist: query's final target list (which is what output paths should emit)
+ * path: input Path to use (must return input_target)
+ * input_target: result of make_window_input_target
+ * output_target: what the topmost WindowAggPath should return
+ * tlist: query's target list (needed to look up pathkeys)
* wflists: result of find_window_functions
* activeWindows: result of select_active_windows
*/
@@ -3416,12 +3437,13 @@ static void
create_one_window_path(PlannerInfo *root,
RelOptInfo *window_rel,
Path *path,
- List *base_tlist,
+ PathTarget *input_target,
+ PathTarget *output_target,
List *tlist,
WindowFuncLists *wflists,
List *activeWindows)
{
- List *window_tlist;
+ PathTarget *window_target;
ListCell *l;
/*
@@ -3430,32 +3452,16 @@ create_one_window_path(PlannerInfo *root,
* needed. (We assume that select_active_windows chose a good order for
* executing the clauses in.)
*
- * The "base" targetlist for all steps of the windowing process is a flat
- * tlist of all Vars and Aggs needed in the result. (In some cases we
- * wouldn't need to propagate all of these all the way to the top, since
- * they might only be needed as inputs to WindowFuncs. It's probably not
- * worth trying to optimize that though.) We also add window partitioning
- * and sorting expressions to the base tlist, to ensure they're computed
- * only once at the bottom of the stack (that's critical for volatile
- * functions). As we climb up the stack, we'll add outputs for the
- * WindowFuncs computed at each level.
- */
- window_tlist = base_tlist;
-
- /*
- * Apply base_tlist to the given base path. If that path node is one that
- * cannot do expression evaluation, we must insert a Result node to
- * project the desired tlist. (In some cases this might not really be
- * required, but it's not worth trying to avoid it.) If the query has
- * both grouping and windowing, base_tlist was already applied to the
- * input path, but apply_projection_to_path is smart about that.
- *
- * The seemingly redundant create_pathtarget() steps here are important to
- * ensure that each path node has a separately modifiable tlist.
+ * input_target should contain all Vars and Aggs needed for the result.
+ * (In some cases we wouldn't need to propagate all of these all the way
+ * to the top, since they might only be needed as inputs to WindowFuncs.
+ * It's probably not worth trying to optimize that though.) It must also
+ * contain all window partitioning and sorting expressions, to ensure
+ * they're computed only once at the bottom of the stack (that's critical
+ * for volatile functions). As we climb up the stack, we'll add outputs
+ * for the WindowFuncs computed at each level.
*/
- path = apply_projection_to_path(root, window_rel,
- path,
- create_pathtarget(root, base_tlist));
+ window_target = input_target;
foreach(l, activeWindows)
{
@@ -3477,19 +3483,34 @@ create_one_window_path(PlannerInfo *root,
if (lnext(l))
{
- /* Add the current WindowFuncs to the running tlist */
- window_tlist = add_to_flat_tlist(window_tlist,
- wflists->windowFuncs[wc->winref]);
+ /*
+ * Add the current WindowFuncs to the output target for this
+ * intermediate WindowAggPath. We must copy window_target to
+ * avoid changing the previous path's target.
+ *
+ * Note: a WindowFunc adds nothing to the target's eval costs; but
+ * we do need to account for the increase in tlist width.
+ */
+ ListCell *lc2;
+
+ window_target = copy_pathtarget(window_target);
+ foreach(lc2, wflists->windowFuncs[wc->winref])
+ {
+ WindowFunc *wfunc = (WindowFunc *) lfirst(lc2);
+
+ Assert(IsA(wfunc, WindowFunc));
+ add_column_to_pathtarget(window_target, (Expr *) wfunc, 0);
+ window_target->width += get_typavgwidth(wfunc->wintype, -1);
+ }
}
else
{
- /* Install the final tlist in the topmost WindowAgg */
- window_tlist = tlist;
+ /* Install the goal target in the topmost WindowAgg */
+ window_target = output_target;
}
path = (Path *)
- create_windowagg_path(root, window_rel, path,
- create_pathtarget(root, window_tlist),
+ create_windowagg_path(root, window_rel, path, window_target,
wflists->windowFuncs[wc->winref],
wc,
window_pathkeys);
@@ -3731,14 +3752,13 @@ create_ordered_paths(PlannerInfo *root,
/*
- * make_scanjoin_target
- * Generate appropriate PathTarget for the result of scan/join steps.
+ * make_group_input_target
+ * Generate appropriate PathTarget for initial input to grouping nodes.
*
- * If there is grouping/aggregation or window functions, we typically want the
- * scan/join plan to emit a different target list than the upper plan nodes
- * will (in particular, it certainly can't include any aggregate or window
- * function calls). This routine generates the correct target list for the
- * scan/join subplan.
+ * 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
+ * for the scan/join subplan.
*
* The initial target list passed from the parser already contains entries
* for all ORDER BY and GROUP BY expressions, but it will not have entries
@@ -3753,17 +3773,13 @@ 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.
*
- * We also convert from targetlist format (List of TargetEntry nodes)
- * into PathTarget format, which is more compact and includes cost/width.
- *
- * 'tlist' is the query's target list.
+ * 'tlist' is the query's final target list.
*
- * The result is the PathTarget to be applied to the Paths returned from
+ * The result is the PathTarget to be computed by the Paths returned from
* query_planner().
*/
static PathTarget *
-make_scanjoin_target(PlannerInfo *root,
- List *tlist)
+make_group_input_target(PlannerInfo *root, List *tlist)
{
Query *parse = root->parse;
List *sub_tlist;
@@ -3772,16 +3788,8 @@ make_scanjoin_target(PlannerInfo *root,
ListCell *tl;
/*
- * If we're not grouping or aggregating or windowing, there's nothing to
- * do here except convert to PathTarget format.
- */
- if (!parse->hasAggs && !parse->groupClause && !parse->groupingSets &&
- !root->hasHavingQual && !parse->hasWindowFuncs)
- return create_pathtarget(root, tlist);
-
- /*
- * Otherwise, we must build a tlist containing all grouping columns, plus
- * any other Vars mentioned in the targetlist and HAVING qual.
+ * We must build a tlist containing all grouping columns, plus any other
+ * Vars mentioned in the targetlist and HAVING qual.
*/
sub_tlist = NIL;
non_group_cols = NIL;
@@ -3951,45 +3959,42 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
}
/*
- * make_windowInputTargetList
- * Generate appropriate target list for initial input to WindowAgg nodes.
+ * 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.
- * This list must contain all values needed to evaluate the window functions,
+ * 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.
*
- * This function is much like make_scanjoin_target, though not quite enough
+ * 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
* into their component variables. But we do not want to flatten window
* PARTITION BY/ORDER BY clauses, since that might result in multiple
* evaluations of them, which would be bad (possibly even resulting in
* inconsistent answers, if they contain volatile functions).
* Also, we must not flatten GROUP BY clauses that were left unflattened by
- * make_scanjoin_target, because we may no longer have access to the
+ * make_group_input_target, because we may no longer have access to the
* individual Vars in them.
*
- * Another key difference from make_scanjoin_target is that we don't flatten
- * Aggref expressions, since those are to be computed below the window
- * functions and just referenced like Vars above that.
- *
- * XXX another difference is that this produces targetlist format not a
- * PathTarget, but that should change sometime soon.
+ * Another key difference from make_group_input_target is that we don't
+ * 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.
* 'activeWindows' is the list of active windows previously identified by
* select_active_windows.
*
- * The result is the targetlist to be computed by the plan node immediately
+ * The result is the PathTarget to be computed by the plan node immediately
* below the first WindowAgg node.
*/
-static List *
-make_windowInputTargetList(PlannerInfo *root,
- List *tlist,
- List *activeWindows)
+static PathTarget *
+make_window_input_target(PlannerInfo *root,
+ List *tlist,
+ List *activeWindows)
{
Query *parse = root->parse;
Bitmapset *sgrefs;
@@ -4091,7 +4096,7 @@ make_windowInputTargetList(PlannerInfo *root,
list_free(flattenable_vars);
list_free(flattenable_cols);
- return new_tlist;
+ return create_pathtarget(root, new_tlist);
}
/*
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 6ac25dc6638..b692e18e3d4 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -672,9 +672,13 @@ find_window_functions_walker(Node *node, WindowFuncLists *lists)
if (wfunc->winref > lists->maxWinRef)
elog(ERROR, "WindowFunc contains out-of-range winref %u",
wfunc->winref);
- lists->windowFuncs[wfunc->winref] =
- lappend(lists->windowFuncs[wfunc->winref], wfunc);
- lists->numWindowFuncs++;
+ /* eliminate duplicates, so that we avoid repeated computation */
+ if (!list_member(lists->windowFuncs[wfunc->winref], wfunc))
+ {
+ lists->windowFuncs[wfunc->winref] =
+ lappend(lists->windowFuncs[wfunc->winref], wfunc);
+ lists->numWindowFuncs++;
+ }
/*
* We assume that the parser checked that there are no window
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index c51642fde5b..ccea3bf9d27 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -624,6 +624,66 @@ make_tlist_from_pathtarget(PathTarget *target)
}
/*
+ * copy_pathtarget
+ * Copy a PathTarget.
+ *
+ * The new PathTarget has its own List cells, but shares the underlying
+ * target expression trees with the old one. We duplicate the List cells
+ * so that items can be added to one target without damaging the other.
+ */
+PathTarget *
+copy_pathtarget(PathTarget *src)
+{
+ PathTarget *dst = (PathTarget *) palloc(sizeof(PathTarget));
+
+ /* Copy scalar fields */
+ memcpy(dst, src, sizeof(PathTarget));
+ /* Shallow-copy the expression list */
+ dst->exprs = list_copy(src->exprs);
+ /* Duplicate sortgrouprefs if any (if not, the memcpy handled this) */
+ if (src->sortgrouprefs)
+ {
+ Size nbytes = list_length(src->exprs) * sizeof(Index);
+
+ dst->sortgrouprefs = (Index *) palloc(nbytes);
+ memcpy(dst->sortgrouprefs, src->sortgrouprefs, nbytes);
+ }
+ return dst;
+}
+
+/*
+ * add_column_to_pathtarget
+ * Append a target column to the PathTarget.
+ *
+ * As with make_pathtarget_from_tlist, we leave it to the caller to update
+ * the cost and width fields.
+ */
+void
+add_column_to_pathtarget(PathTarget *target, Expr *expr, Index sortgroupref)
+{
+ /* Updating the exprs list is easy ... */
+ target->exprs = lappend(target->exprs, expr);
+ /* ... the sortgroupref data, a bit less so */
+ if (target->sortgrouprefs)
+ {
+ int nexprs = list_length(target->exprs);
+
+ /* This might look inefficient, but actually it's usually cheap */
+ target->sortgrouprefs = (Index *)
+ repalloc(target->sortgrouprefs, nexprs * sizeof(Index));
+ target->sortgrouprefs[nexprs - 1] = sortgroupref;
+ }
+ else if (sortgroupref)
+ {
+ /* Adding sortgroupref labeling to a previously unlabeled target */
+ int nexprs = list_length(target->exprs);
+
+ target->sortgrouprefs = (Index *) palloc0(nexprs * sizeof(Index));
+ target->sortgrouprefs[nexprs - 1] = sortgroupref;
+ }
+}
+
+/*
* 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 4493ff9e27b..a60e10278c7 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -56,6 +56,9 @@ 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 void add_column_to_pathtarget(PathTarget *target,
+ Expr *expr, Index sortgroupref);
extern void apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target);
/* Convenience macro to get a PathTarget with valid cost/width fields */