diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 1e4dd27dba..deb22e9404 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -5999,6 +5999,9 @@ make_window_input_target(PlannerInfo *root, * Create a pathkeys list describing the required input ordering * for the given WindowClause. * + * Modifies wc's partitionClause to remove any clauses which are deemed + * redundant by the pathkey logic. + * * The required ordering is first the PARTITION keys, then the ORDER keys. * In the future we might try to implement windowing using hashing, in which * case the ordering could be relaxed, but for now we always sort. @@ -6007,8 +6010,7 @@ static List * make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc, List *tlist) { - List *window_pathkeys; - List *window_sortclauses; + List *window_pathkeys = NIL; /* Throw error if can't sort */ if (!grouping_is_sortable(wc->partitionClause)) @@ -6022,12 +6024,43 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc, errmsg("could not implement window ORDER BY"), errdetail("Window ordering columns must be of sortable datatypes."))); - /* Okay, make the combined pathkeys */ - window_sortclauses = list_concat_copy(wc->partitionClause, wc->orderClause); - window_pathkeys = make_pathkeys_for_sortclauses(root, - window_sortclauses, - tlist); - list_free(window_sortclauses); + /* + * First fetch the pathkeys for the PARTITION BY clause. We can safely + * remove any clauses from the wc->partitionClause for redundant pathkeys. + */ + if (wc->partitionClause != NIL) + { + bool sortable; + + window_pathkeys = make_pathkeys_for_sortclauses_extended(root, + &wc->partitionClause, + tlist, + true, + &sortable); + + Assert(sortable); + } + + /* + * And fetch the pathkeys for the ORDER BY clause. We must keep any + * redundant pathkeys. Removing these would change the semantics of peer + * rows during execution. + */ + if (wc->orderClause != NIL) + { + List *orderby_pathkeys; + + orderby_pathkeys = make_pathkeys_for_sortclauses(root, + wc->orderClause, + tlist); + + /* Okay, make the combined pathkeys */ + if (window_pathkeys != NIL) + window_pathkeys = append_pathkeys(window_pathkeys, orderby_pathkeys); + else + window_pathkeys = orderby_pathkeys; + } + return window_pathkeys; } diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 0ca298f5a1..94520103a1 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1473,6 +1473,8 @@ typedef struct GroupingSet * if the clause originally came from WINDOW, and is NULL if it originally * was an OVER clause (but note that we collapse out duplicate OVERs). * partitionClause and orderClause are lists of SortGroupClause structs. + * partitionClause is sanitized by the query planner to remove any columns or + * expressions belonging to redundant PathKeys. * If we have RANGE with offset PRECEDING/FOLLOWING, the semantics of that are * specified by startInRangeFunc/inRangeColl/inRangeAsc/inRangeNullsFirst * for the start offset, or endInRangeFunc/inRange* for the end offset.