diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c index 98fc2b44b50..170d9bdad84 100644 --- a/src/backend/nodes/list.c +++ b/src/backend/nodes/list.c @@ -1513,6 +1513,52 @@ list_deduplicate_oid(List *list) check_list_invariants(list); } +/* + * list_uniquify + * Remove duplicate items from a pointer list according to the item + * values. + * + * Unlike the list_deduplicate* functions, we have no means to sort pointer + * lists by their values. Because we don't require a sorted list, this + * function does not follow the naming standard of those functions. + */ +void +list_uniquify(List *list) +{ + int len; + + Assert(IsPointerList(list)); + len = list_length(list); + if (len > 1) + { + ListCell *elements = list->elements; + + for (int i = 0; i < len; i++) + { + for (int j = i + 1; j < len;) + { + if (equal(elements[i].ptr_value, elements[j].ptr_value)) + { + /* + * Move all elements beyond the j'th element up 1 place. + * XXX this won't be very efficient for long lists with + * many duplicates. + */ + memmove(&list->elements[j], + &list->elements[j + 1], + (len - j) * sizeof(ListCell)); + len--; + } + else + j++; + } + } + + list->length = len; + } + check_list_invariants(list); +} + /* * Free all storage in a list, and optionally the pointed-to elements */ diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 7a6b8b749f2..8b8fae695ec 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -5964,6 +5964,32 @@ optimize_window_clauses(PlannerInfo *root, WindowFuncLists *wflists) } } } + + /* + * XXX remove any duplicate WindowFuncs from each WindowClause. This has + * been done only in the back branches. Previously, the deduplication was + * done in find_window_functions(), but that caused issues with the code + * above when moving a WindowFunc to another WindowClause as any duplicate + * WindowFuncs won't receive the adjusted winref when merging + * WindowClauses. The deduplication below has been done only so that we + * maintain the same cost calculations. As it turns out, the previous + * deduplication code thought it was saving effort during execution by + * getting rid of duplicates, but that was not true as the expression + * evaluation code will evaluate each WindowFunc mentioned in the + * targetlist. + */ + foreach(lc, windowClause) + { + WindowClause *wc = lfirst_node(WindowClause, lc); + List *lst = wflists->windowFuncs[wc->winref]; + + if (lst == NIL) + continue; + + wflists->numWindowFuncs -= list_length(lst); + list_uniquify(lst); + wflists->numWindowFuncs += list_length(lst); + } } /* diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 39d35827c35..32204776c45 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -261,13 +261,10 @@ find_window_functions_walker(Node *node, WindowFuncLists *lists) if (wfunc->winref > lists->maxWinRef) elog(ERROR, "WindowFunc contains out-of-range winref %u", wfunc->winref); - /* 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++; - } + + 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/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index ae80975548f..ad65945177b 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -668,6 +668,7 @@ pg_nodiscard extern List *list_concat_unique_int(List *list1, const List *list2) pg_nodiscard extern List *list_concat_unique_oid(List *list1, const List *list2); extern void list_deduplicate_oid(List *list); +extern void list_uniquify(List *list); extern void list_free(List *list); extern void list_free_deep(List *list);