diff options
author | Tom Lane | 2025-03-11 15:19:54 +0000 |
---|---|---|
committer | Tom Lane | 2025-03-11 15:19:54 +0000 |
commit | 8b1b342544b69b281ffd3aafe594aec629ec4d3c (patch) | |
tree | 6239ec69a949ffb5397fc4f7a5f50128d446d477 /src/backend/commands | |
parent | 426ea611171da4e60ab4f3863fa3cc3683ae9547 (diff) |
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 <[email protected]>
Reviewed-by: David Rowley <[email protected]>
Discussion: https://postgr.es/m/[email protected]
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/explain.c | 117 |
1 files changed, 115 insertions, 2 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index adefc5471a3..19ffcc2cacb 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -107,6 +107,11 @@ static void show_sort_group_keys(PlanState *planstate, const char *qlabel, List *ancestors, ExplainState *es); static void show_sortorder_options(StringInfo buf, Node *sortexpr, Oid sortOperator, Oid collation, bool nullsFirst); +static void show_window_def(WindowAggState *planstate, + List *ancestors, ExplainState *es); +static void show_window_keys(StringInfo buf, PlanState *planstate, + int nkeys, AttrNumber *keycols, + List *ancestors, ExplainState *es); static void show_storage_info(char *maxStorageType, int64 maxSpaceUsed, ExplainState *es); static void show_tablesample(TableSampleClause *tsc, PlanState *planstate, @@ -2333,12 +2338,13 @@ ExplainNode(PlanState *planstate, List *ancestors, planstate, es); break; case T_WindowAgg: + show_window_def(castNode(WindowAggState, planstate), ancestors, es); + show_upper_qual(((WindowAgg *) plan)->runConditionOrig, + "Run Condition", planstate, ancestors, es); show_upper_qual(plan->qual, "Filter", planstate, ancestors, es); if (plan->qual) show_instrumentation_count("Rows Removed by Filter", 1, planstate, es); - show_upper_qual(((WindowAgg *) plan)->runConditionOrig, - "Run Condition", planstate, ancestors, es); show_windowagg_info(castNode(WindowAggState, planstate), es); break; case T_Group: @@ -3008,6 +3014,113 @@ show_sortorder_options(StringInfo buf, Node *sortexpr, } /* + * Show the window definition for a WindowAgg node. + */ +static void +show_window_def(WindowAggState *planstate, List *ancestors, ExplainState *es) +{ + WindowAgg *wagg = (WindowAgg *) planstate->ss.ps.plan; + StringInfoData wbuf; + bool needspace = false; + + initStringInfo(&wbuf); + appendStringInfo(&wbuf, "%s AS (", quote_identifier(wagg->winname)); + + /* The key columns refer to the tlist of the child plan */ + ancestors = lcons(wagg, ancestors); + if (wagg->partNumCols > 0) + { + appendStringInfoString(&wbuf, "PARTITION BY "); + show_window_keys(&wbuf, outerPlanState(planstate), + wagg->partNumCols, wagg->partColIdx, + ancestors, es); + needspace = true; + } + if (wagg->ordNumCols > 0) + { + if (needspace) + appendStringInfoChar(&wbuf, ' '); + appendStringInfoString(&wbuf, "ORDER BY "); + show_window_keys(&wbuf, outerPlanState(planstate), + wagg->ordNumCols, wagg->ordColIdx, + ancestors, es); + needspace = true; + } + ancestors = list_delete_first(ancestors); + if (wagg->frameOptions & FRAMEOPTION_NONDEFAULT) + { + List *context; + bool useprefix; + char *framestr; + + /* Set up deparsing context for possible frame expressions */ + context = set_deparse_context_plan(es->deparse_cxt, + (Plan *) wagg, + ancestors); + useprefix = (es->rtable_size > 1 || es->verbose); + framestr = get_window_frame_options_for_explain(wagg->frameOptions, + wagg->startOffset, + wagg->endOffset, + context, + useprefix); + if (needspace) + appendStringInfoChar(&wbuf, ' '); + appendStringInfoString(&wbuf, framestr); + pfree(framestr); + } + appendStringInfoChar(&wbuf, ')'); + ExplainPropertyText("Window", wbuf.data, es); + pfree(wbuf.data); +} + +/* + * Append the keys of a window's PARTITION BY or ORDER BY clause to buf. + * We can't use show_sort_group_keys for this because that's too opinionated + * about how the result will be displayed. + * Note that the "planstate" node should be the WindowAgg's child. + */ +static void +show_window_keys(StringInfo buf, PlanState *planstate, + int nkeys, AttrNumber *keycols, + List *ancestors, ExplainState *es) +{ + Plan *plan = planstate->plan; + List *context; + bool useprefix; + + /* Set up deparsing context */ + context = set_deparse_context_plan(es->deparse_cxt, + plan, + ancestors); + useprefix = (es->rtable_size > 1 || es->verbose); + + for (int keyno = 0; keyno < nkeys; keyno++) + { + /* find key expression in tlist */ + AttrNumber keyresno = keycols[keyno]; + TargetEntry *target = get_tle_by_resno(plan->targetlist, + keyresno); + char *exprstr; + + if (!target) + elog(ERROR, "no tlist entry for key %d", keyresno); + /* Deparse the expression, showing any top-level cast */ + exprstr = deparse_expression((Node *) target->expr, context, + useprefix, true); + if (keyno > 0) + appendStringInfoString(buf, ", "); + appendStringInfoString(buf, exprstr); + pfree(exprstr); + + /* + * We don't attempt to provide sort order information because + * WindowAgg carries equality operators not comparison operators; + * compare show_agg_keys. + */ + } +} + +/* * Show information on storage method and maximum memory/disk space used. */ static void |