1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-18 02:02:55 +03:00

Improve EXPLAIN's display of window functions.

Up to now we just punted on showing the window definitions used
in a plan, with window function calls represented as "OVER (?)".
To improve that, show the window definition implemented by each
WindowAgg plan node, and reference their window names in OVER.
For nameless window clauses generated by "OVER (...)", assign
unique names w1, w2, etc.

In passing, re-order the properties shown for a WindowAgg node
so that the Run Condition (if any) appears after the Window
property and before the Filter (if any).  This seems more
sensible since the Run Condition is associated with the Window
and acts before the Filter.

Thanks to David G. Johnston and Álvaro Herrera for design
suggestions.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/144530.1741469955@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2025-03-11 11:19:54 -04:00
parent 426ea61117
commit 8b1b342544
18 changed files with 598 additions and 253 deletions

View File

@@ -285,12 +285,9 @@ static Memoize *make_memoize(Plan *lefttree, Oid *hashoperators,
Oid *collations, List *param_exprs,
bool singlerow, bool binary_mode,
uint32 est_entries, Bitmapset *keyparamids);
static WindowAgg *make_windowagg(List *tlist, Index winref,
static WindowAgg *make_windowagg(List *tlist, WindowClause *wc,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
int frameOptions, Node *startOffset, Node *endOffset,
Oid startInRangeFunc, Oid endInRangeFunc,
Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst,
List *runCondition, List *qual, bool topWindow,
Plan *lefttree);
static Group *make_group(List *tlist, List *qual, int numGroupCols,
@@ -2683,7 +2680,7 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
/* And finally we can make the WindowAgg node */
plan = make_windowagg(tlist,
wc->winref,
wc,
partNumCols,
partColIdx,
partOperators,
@@ -2692,14 +2689,6 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
ordColIdx,
ordOperators,
ordCollations,
wc->frameOptions,
wc->startOffset,
wc->endOffset,
wc->startInRangeFunc,
wc->endInRangeFunc,
wc->inRangeColl,
wc->inRangeAsc,
wc->inRangeNullsFirst,
best_path->runCondition,
best_path->qual,
best_path->topwindow,
@@ -6704,18 +6693,16 @@ make_agg(List *tlist, List *qual,
}
static WindowAgg *
make_windowagg(List *tlist, Index winref,
make_windowagg(List *tlist, WindowClause *wc,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
int frameOptions, Node *startOffset, Node *endOffset,
Oid startInRangeFunc, Oid endInRangeFunc,
Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst,
List *runCondition, List *qual, bool topWindow, Plan *lefttree)
{
WindowAgg *node = makeNode(WindowAgg);
Plan *plan = &node->plan;
node->winref = winref;
node->winname = wc->name;
node->winref = wc->winref;
node->partNumCols = partNumCols;
node->partColIdx = partColIdx;
node->partOperators = partOperators;
@@ -6724,17 +6711,17 @@ make_windowagg(List *tlist, Index winref,
node->ordColIdx = ordColIdx;
node->ordOperators = ordOperators;
node->ordCollations = ordCollations;
node->frameOptions = frameOptions;
node->startOffset = startOffset;
node->endOffset = endOffset;
node->frameOptions = wc->frameOptions;
node->startOffset = wc->startOffset;
node->endOffset = wc->endOffset;
node->runCondition = runCondition;
/* a duplicate of the above for EXPLAIN */
node->runConditionOrig = runCondition;
node->startInRangeFunc = startInRangeFunc;
node->endInRangeFunc = endInRangeFunc;
node->inRangeColl = inRangeColl;
node->inRangeAsc = inRangeAsc;
node->inRangeNullsFirst = inRangeNullsFirst;
node->startInRangeFunc = wc->startInRangeFunc;
node->endInRangeFunc = wc->endInRangeFunc;
node->inRangeColl = wc->inRangeColl;
node->inRangeAsc = wc->inRangeAsc;
node->inRangeNullsFirst = wc->inRangeNullsFirst;
node->topWindow = topWindow;
plan->targetlist = tlist;

View File

@@ -214,6 +214,7 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
static void optimize_window_clauses(PlannerInfo *root,
WindowFuncLists *wflists);
static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
static void name_active_windows(List *activeWindows);
static PathTarget *make_window_input_target(PlannerInfo *root,
PathTarget *final_target,
List *activeWindows);
@@ -1539,7 +1540,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction,
*/
optimize_window_clauses(root, wflists);
/* Extract the list of windows actually in use. */
activeWindows = select_active_windows(root, wflists);
/* Make sure they all have names, for EXPLAIN's use. */
name_active_windows(activeWindows);
}
else
parse->hasWindowFuncs = false;
@@ -5914,6 +5919,52 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
return result;
}
/*
* name_active_windows
* Ensure all active windows have unique names.
*
* The parser will have checked that user-assigned window names are unique
* within the Query. Here we assign made-up names to any unnamed
* WindowClauses for the benefit of EXPLAIN. (We don't want to do this
* at parse time, because it'd mess up decompilation of views.)
*
* activeWindows: result of select_active_windows
*/
static void
name_active_windows(List *activeWindows)
{
int next_n = 1;
char newname[16];
ListCell *lc;
foreach(lc, activeWindows)
{
WindowClause *wc = lfirst_node(WindowClause, lc);
/* Nothing to do if it has a name already. */
if (wc->name)
continue;
/* Select a name not currently present in the list. */
for (;;)
{
ListCell *lc2;
snprintf(newname, sizeof(newname), "w%d", next_n++);
foreach(lc2, activeWindows)
{
WindowClause *wc2 = lfirst_node(WindowClause, lc2);
if (wc2->name && strcmp(wc2->name, newname) == 0)
break; /* matched */
}
if (lc2 == NULL)
break; /* reached the end with no match */
}
wc->name = pstrdup(newname);
}
}
/*
* common_prefix_cmp
* QSort comparison function for WindowClauseSortData