1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-12 21:01:52 +03:00

Postpone creation of pathkeys lists to fix bug #8049.

This patch gets rid of the concept of, and infrastructure for,
non-canonical PathKeys; we now only ever create canonical pathkey lists.

The need for non-canonical pathkeys came from the desire to have
grouping_planner initialize query_pathkeys and related pathkey lists before
calling query_planner.  However, since query_planner didn't actually *do*
anything with those lists before they'd been made canonical, we can get rid
of the whole mess by just not creating the lists at all until the point
where we formerly canonicalized them.

There are several ways in which we could implement that without making
query_planner itself deal with grouping/sorting features (which are
supposed to be the province of grouping_planner).  I chose to add a
callback function to query_planner's API; other alternatives would have
required adding more fields to PlannerInfo, which while not bad in itself
would create an ABI break for planner-related plugins in the 9.2 release
series.  This still breaks ABI for anything that calls query_planner
directly, but it seems somewhat unlikely that there are any such plugins.

I had originally conceived of this change as merely a step on the way to
fixing bug #8049 from Teun Hoogendoorn; but it turns out that this fixes
that bug all by itself, as per the added regression test.  The reason is
that now get_eclass_for_sort_expr is adding the ORDER BY expression at the
end of EquivalenceClass creation not the start, and so anything that is in
a multi-member EquivalenceClass has already been created with correct
em_nullable_relids.  I am suspicious that there are related scenarios in
which we still need to teach get_eclass_for_sort_expr to compute correct
nullable_relids, but am not eager to risk destabilizing either 9.2 or 9.3
to fix bugs that are only hypothetical.  So for the moment, do this and
stop here.

Back-patch to 9.2 but not to earlier branches, since they don't exhibit
this bug for lack of join-clause-movement logic that depends on
em_nullable_relids being correct.  (We might have to revisit that choice
if any related bugs turn up.)  In 9.2, don't change the signature of
make_pathkeys_for_sortclauses nor remove canonicalize_pathkeys, so as
not to risk more plugin breakage than we have to.
This commit is contained in:
Tom Lane
2013-04-29 14:49:01 -04:00
parent 5fc893760f
commit db9f0e1d9a
12 changed files with 246 additions and 314 deletions

View File

@ -59,7 +59,14 @@ planner_hook_type planner_hook = NULL;
#define EXPRKIND_APPINFO 7
#define EXPRKIND_PHV 8
/* Passthrough data for standard_qp_callback */
typedef struct
{
List *tlist; /* preprocessed query targetlist */
List *activeWindows; /* active windows, if any */
} standard_qp_extra;
/* Local functions */
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
static Plan *inheritance_planner(PlannerInfo *root);
@ -70,6 +77,7 @@ static double preprocess_limit(PlannerInfo *root,
int64 *offset_est, int64 *count_est);
static bool limit_needed(Query *parse);
static void preprocess_groupclause(PlannerInfo *root);
static void standard_qp_callback(PlannerInfo *root, void *extra);
static bool choose_hashed_grouping(PlannerInfo *root,
double tuple_fraction, double limit_tuples,
double path_rows, int path_width,
@ -94,7 +102,7 @@ static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
static List *make_windowInputTargetList(PlannerInfo *root,
List *tlist, List *activeWindows);
static List *make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
List *tlist, bool canonicalize);
List *tlist);
static void get_column_info_for_window(PlannerInfo *root, WindowClause *wc,
List *tlist,
int numSortCols, AttrNumber *sortColIdx,
@ -1052,8 +1060,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
*/
current_pathkeys = make_pathkeys_for_sortclauses(root,
set_sortclauses,
result_plan->targetlist,
true);
result_plan->targetlist);
/*
* We should not need to call preprocess_targetlist, since we must be
@ -1082,8 +1089,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
Assert(parse->distinctClause == NIL);
root->sort_pathkeys = make_pathkeys_for_sortclauses(root,
parse->sortClause,
tlist,
true);
tlist);
}
else
{
@ -1092,6 +1098,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
double sub_limit_tuples;
AttrNumber *groupColIdx = NULL;
bool need_tlist_eval = true;
standard_qp_extra qp_extra;
Path *cheapest_path;
Path *sorted_path;
Path *best_path;
@ -1167,82 +1174,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
preprocess_minmax_aggregates(root, tlist);
}
/*
* Calculate pathkeys that represent grouping/ordering requirements.
* Stash them in PlannerInfo so that query_planner can canonicalize
* them after EquivalenceClasses have been formed. The sortClause is
* certainly sort-able, but GROUP BY and DISTINCT might not be, in
* which case we just leave their pathkeys empty.
*/
if (parse->groupClause &&
grouping_is_sortable(parse->groupClause))
root->group_pathkeys =
make_pathkeys_for_sortclauses(root,
parse->groupClause,
tlist,
false);
else
root->group_pathkeys = NIL;
/* We consider only the first (bottom) window in pathkeys logic */
if (activeWindows != NIL)
{
WindowClause *wc = (WindowClause *) linitial(activeWindows);
root->window_pathkeys = make_pathkeys_for_window(root,
wc,
tlist,
false);
}
else
root->window_pathkeys = NIL;
if (parse->distinctClause &&
grouping_is_sortable(parse->distinctClause))
root->distinct_pathkeys =
make_pathkeys_for_sortclauses(root,
parse->distinctClause,
tlist,
false);
else
root->distinct_pathkeys = NIL;
root->sort_pathkeys =
make_pathkeys_for_sortclauses(root,
parse->sortClause,
tlist,
false);
/*
* Figure out whether we want a sorted result from query_planner.
*
* If we have a sortable GROUP BY clause, then we want a result sorted
* properly for grouping. Otherwise, if we have window functions to
* evaluate, we try to sort for the first window. Otherwise, if
* there's a sortable DISTINCT clause that's more rigorous than the
* ORDER BY clause, we try to produce output that's sufficiently well
* sorted for the DISTINCT. Otherwise, if there is an ORDER BY
* clause, we want to sort by the ORDER BY clause.
*
* Note: if we have both ORDER BY and GROUP BY, and ORDER BY is a
* superset of GROUP BY, it would be tempting to request sort by ORDER
* BY --- but that might just leave us failing to exploit an available
* sort order at all. Needs more thought. The choice for DISTINCT
* versus ORDER BY is much easier, since we know that the parser
* ensured that one is a superset of the other.
*/
if (root->group_pathkeys)
root->query_pathkeys = root->group_pathkeys;
else if (root->window_pathkeys)
root->query_pathkeys = root->window_pathkeys;
else if (list_length(root->distinct_pathkeys) >
list_length(root->sort_pathkeys))
root->query_pathkeys = root->distinct_pathkeys;
else if (root->sort_pathkeys)
root->query_pathkeys = root->sort_pathkeys;
else
root->query_pathkeys = NIL;
/*
* Figure out whether there's a hard limit on the number of rows that
* query_planner's result subplan needs to return. Even if we know a
@ -1258,13 +1189,19 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
else
sub_limit_tuples = limit_tuples;
/* Set up data needed by standard_qp_callback */
qp_extra.tlist = tlist;
qp_extra.activeWindows = activeWindows;
/*
* Generate the best unsorted and presorted paths for this Query (but
* note there may not be any presorted path). query_planner will also
* estimate the number of groups in the query, and canonicalize all
* the pathkeys.
* note there may not be any presorted path). We also generate (in
* standard_qp_callback) pathkey representations of the query's sort
* clause, distinct clause, etc. query_planner will also estimate the
* number of groups in the query.
*/
query_planner(root, sub_tlist, tuple_fraction, sub_limit_tuples,
standard_qp_callback, &qp_extra,
&cheapest_path, &sorted_path, &dNumGroups);
/*
@ -1597,8 +1534,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
window_pathkeys = make_pathkeys_for_window(root,
wc,
tlist,
true);
tlist);
/*
* This is a bit tricky: we build a sort node even if we don't
@ -2439,6 +2375,88 @@ preprocess_groupclause(PlannerInfo *root)
parse->groupClause = new_groupclause;
}
/*
* Compute query_pathkeys and other pathkeys during plan generation
*/
static void
standard_qp_callback(PlannerInfo *root, void *extra)
{
Query *parse = root->parse;
standard_qp_extra *qp_extra = (standard_qp_extra *) extra;
List *tlist = qp_extra->tlist;
List *activeWindows = qp_extra->activeWindows;
/*
* Calculate pathkeys that represent grouping/ordering requirements. The
* sortClause is certainly sort-able, but GROUP BY and DISTINCT might not
* be, in which case we just leave their pathkeys empty.
*/
if (parse->groupClause &&
grouping_is_sortable(parse->groupClause))
root->group_pathkeys =
make_pathkeys_for_sortclauses(root,
parse->groupClause,
tlist);
else
root->group_pathkeys = NIL;
/* We consider only the first (bottom) window in pathkeys logic */
if (activeWindows != NIL)
{
WindowClause *wc = (WindowClause *) linitial(activeWindows);
root->window_pathkeys = make_pathkeys_for_window(root,
wc,
tlist);
}
else
root->window_pathkeys = NIL;
if (parse->distinctClause &&
grouping_is_sortable(parse->distinctClause))
root->distinct_pathkeys =
make_pathkeys_for_sortclauses(root,
parse->distinctClause,
tlist);
else
root->distinct_pathkeys = NIL;
root->sort_pathkeys =
make_pathkeys_for_sortclauses(root,
parse->sortClause,
tlist);
/*
* Figure out whether we want a sorted result from query_planner.
*
* If we have a sortable GROUP BY clause, then we want a result sorted
* properly for grouping. Otherwise, if we have window functions to
* evaluate, we try to sort for the first window. Otherwise, if there's a
* sortable DISTINCT clause that's more rigorous than the ORDER BY clause,
* we try to produce output that's sufficiently well sorted for the
* DISTINCT. Otherwise, if there is an ORDER BY clause, we want to sort
* by the ORDER BY clause.
*
* Note: if we have both ORDER BY and GROUP BY, and ORDER BY is a superset
* of GROUP BY, it would be tempting to request sort by ORDER BY --- but
* that might just leave us failing to exploit an available sort order at
* all. Needs more thought. The choice for DISTINCT versus ORDER BY is
* much easier, since we know that the parser ensured that one is a
* superset of the other.
*/
if (root->group_pathkeys)
root->query_pathkeys = root->group_pathkeys;
else if (root->window_pathkeys)
root->query_pathkeys = root->window_pathkeys;
else if (list_length(root->distinct_pathkeys) >
list_length(root->sort_pathkeys))
root->query_pathkeys = root->distinct_pathkeys;
else if (root->sort_pathkeys)
root->query_pathkeys = root->sort_pathkeys;
else
root->query_pathkeys = NIL;
}
/*
* choose_hashed_grouping - should we use hashed grouping?
*
@ -3235,7 +3253,7 @@ make_windowInputTargetList(PlannerInfo *root,
*/
static List *
make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
List *tlist, bool canonicalize)
List *tlist)
{
List *window_pathkeys;
List *window_sortclauses;
@ -3257,8 +3275,7 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
list_copy(wc->orderClause));
window_pathkeys = make_pathkeys_for_sortclauses(root,
window_sortclauses,
tlist,
canonicalize);
tlist);
list_free(window_sortclauses);
return window_pathkeys;
}
@ -3336,8 +3353,7 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
sortclauses = lappend(sortclauses, sgc);
new_pathkeys = make_pathkeys_for_sortclauses(root,
sortclauses,
tlist,
true);
tlist);
if (list_length(new_pathkeys) > list_length(pathkeys))
{
/* this sort clause is actually significant */
@ -3355,8 +3371,7 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
sortclauses = lappend(sortclauses, sgc);
new_pathkeys = make_pathkeys_for_sortclauses(root,
sortclauses,
tlist,
true);
tlist);
if (list_length(new_pathkeys) > list_length(pathkeys))
{
/* this sort clause is actually significant */