diff options
author | Richard Guo <rguo@postgresql.org> | 2024-09-10 12:36:48 +0900 |
---|---|---|
committer | Richard Guo <rguo@postgresql.org> | 2024-09-10 12:36:48 +0900 |
commit | f5050f795aea67dfc40bbc429c8934e9439e22e7 (patch) | |
tree | de5ea9e20be4c6563165346e8c00dd05892993b0 /src/backend/optimizer/plan/planner.c | |
parent | 247dea89f7616fdf06b7272b74abafc29e8e5860 (diff) | |
download | postgresql-f5050f795aea67dfc40bbc429c8934e9439e22e7.tar.gz postgresql-f5050f795aea67dfc40bbc429c8934e9439e22e7.zip |
Mark expressions nullable by grouping sets
When generating window_pathkeys, distinct_pathkeys, or sort_pathkeys,
we failed to realize that the grouping/ordering expressions might be
nullable by grouping sets. As a result, we may incorrectly deem that
the PathKeys are redundant by EquivalenceClass processing and thus
remove them from the pathkeys list. That would lead to wrong results
in some cases.
To fix this issue, we mark the grouping expressions nullable by
grouping sets if that is the case. If the grouping expression is a
Var or PlaceHolderVar or constructed from those, we can just add the
RT index of the RTE_GROUP RTE to the existing nullingrels field(s);
otherwise we have to add a PlaceHolderVar to carry on the nullingrel
bit.
However, we have to manually remove this nullingrel bit from
expressions in various cases where these expressions are logically
below the grouping step, such as when we generate groupClause pathkeys
for grouping sets, or when we generate PathTarget for initial input to
grouping nodes.
Furthermore, in set_upper_references, the targetlist and quals of an
Agg node should have nullingrels that include the effects of the
grouping step, ie they will have nullingrels equal to the input
Vars/PHVs' nullingrels plus the nullingrel bit that references the
grouping RTE. In order to perform exact nullingrels matches, we also
need to manually remove this nullingrel bit.
Bump catversion because this changes the querytree produced by the
parser.
Thanks to Tom Lane for the idea to invent a new kind of RTE.
Per reports from Geoff Winkless, Tobias Wendorff, Richard Guo from
various threads.
Author: Richard Guo
Reviewed-by: Ashutosh Bapat, Sutou Kouhei
Discussion: https://postgr.es/m/CAMbWs4_dp7e7oTwaiZeBX8+P1rXw4ThkZxh1QG81rhu9Z47VsQ@mail.gmail.com
Diffstat (limited to 'src/backend/optimizer/plan/planner.c')
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 49 |
1 files changed, 46 insertions, 3 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index bd4b652f7a3..df35d1ff9c4 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -58,6 +58,7 @@ #include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" +#include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/selfuncs.h" @@ -3506,9 +3507,23 @@ standard_qp_callback(PlannerInfo *root, void *extra) if (grouping_is_sortable(groupClause)) { - root->group_pathkeys = make_pathkeys_for_sortclauses(root, - groupClause, - tlist); + bool sortable; + + /* + * The groupClause is logically below the grouping step. So if + * there is an RTE entry for the grouping step, we need to remove + * its RT index from the sort expressions before we make PathKeys + * for them. + */ + root->group_pathkeys = + make_pathkeys_for_sortclauses_extended(root, + &groupClause, + tlist, + false, + parse->hasGroupRTE, + &sortable, + false); + Assert(sortable); root->num_groupby_pathkeys = list_length(root->group_pathkeys); } else @@ -3538,6 +3553,7 @@ standard_qp_callback(PlannerInfo *root, void *extra) &root->processed_groupClause, tlist, true, + false, &sortable, true); if (!sortable) @@ -3589,6 +3605,7 @@ standard_qp_callback(PlannerInfo *root, void *extra) &root->processed_distinctClause, tlist, true, + false, &sortable, false); if (!sortable) @@ -3616,6 +3633,7 @@ standard_qp_callback(PlannerInfo *root, void *extra) &groupClauses, tlist, false, + false, &sortable, false); if (!sortable) @@ -5526,7 +5544,19 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target) { /* * It's a grouping column, so add it to the input target as-is. + * + * Note that the target is logically below the grouping step. So + * with grouping sets we need to remove the RT index of the + * grouping step if there is any from the target expression. */ + if (parse->hasGroupRTE && parse->groupingSets != NIL) + { + Assert(root->group_rtindex > 0); + expr = (Expr *) + remove_nulling_relids((Node *) expr, + bms_make_singleton(root->group_rtindex), + NULL); + } add_column_to_pathtarget(input_target, expr, sgref); } else @@ -5554,11 +5584,23 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target) * includes Vars used in resjunk items, so we are covering the needs of * ORDER BY and window specifications. Vars used within Aggrefs and * WindowFuncs will be pulled out here, too. + * + * Note that the target is logically below the grouping step. So with + * grouping sets we need to remove the RT index of the grouping step if + * there is any from the non-group Vars. */ non_group_vars = pull_var_clause((Node *) non_group_cols, PVC_RECURSE_AGGREGATES | PVC_RECURSE_WINDOWFUNCS | PVC_INCLUDE_PLACEHOLDERS); + if (parse->hasGroupRTE && parse->groupingSets != NIL) + { + Assert(root->group_rtindex > 0); + non_group_vars = (List *) + remove_nulling_relids((Node *) non_group_vars, + bms_make_singleton(root->group_rtindex), + NULL); + } add_new_columns_to_pathtarget(input_target, non_group_vars); /* clean up cruft */ @@ -6207,6 +6249,7 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc, &wc->partitionClause, tlist, true, + false, &sortable, false); |