Improve EXPLAIN's display of window functions.

8b1b342544b69b281ffd3aafe594aec629ec4d3c
pull/8010/head
Mehmet Yilmaz 2025-05-30 09:36:22 +00:00
parent 26fd672333
commit 485feb6acc
1 changed files with 81 additions and 40 deletions

View File

@ -390,6 +390,9 @@ static void get_rule_orderby(List *orderList, List *targetList,
static void get_rule_windowclause(Query *query, deparse_context *context); static void get_rule_windowclause(Query *query, deparse_context *context);
static void get_rule_windowspec(WindowClause *wc, List *targetList, static void get_rule_windowspec(WindowClause *wc, List *targetList,
deparse_context *context); deparse_context *context);
static void get_window_frame_options(int frameOptions,
Node *startOffset, Node *endOffset,
deparse_context *context);
static char *get_variable(Var *var, int levelsup, bool istoplevel, static char *get_variable(Var *var, int levelsup, bool istoplevel,
deparse_context *context); deparse_context *context);
static void get_special_variable(Node *node, deparse_context *context, static void get_special_variable(Node *node, deparse_context *context,
@ -3249,45 +3252,64 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
{ {
if (needspace) if (needspace)
appendStringInfoChar(buf, ' '); appendStringInfoChar(buf, ' ');
if (wc->frameOptions & FRAMEOPTION_RANGE) get_window_frame_options(wc->frameOptions,
wc->startOffset, wc->endOffset,
context);
}
appendStringInfoChar(buf, ')');
}
/*
* Append the description of a window's framing options to context->buf
*/
static void
get_window_frame_options(int frameOptions,
Node *startOffset, Node *endOffset,
deparse_context *context)
{
StringInfo buf = context->buf;
if (frameOptions & FRAMEOPTION_NONDEFAULT)
{
if (frameOptions & FRAMEOPTION_RANGE)
appendStringInfoString(buf, "RANGE "); appendStringInfoString(buf, "RANGE ");
else if (wc->frameOptions & FRAMEOPTION_ROWS) else if (frameOptions & FRAMEOPTION_ROWS)
appendStringInfoString(buf, "ROWS "); appendStringInfoString(buf, "ROWS ");
else if (wc->frameOptions & FRAMEOPTION_GROUPS) else if (frameOptions & FRAMEOPTION_GROUPS)
appendStringInfoString(buf, "GROUPS "); appendStringInfoString(buf, "GROUPS ");
else else
Assert(false); Assert(false);
if (wc->frameOptions & FRAMEOPTION_BETWEEN) if (frameOptions & FRAMEOPTION_BETWEEN)
appendStringInfoString(buf, "BETWEEN "); appendStringInfoString(buf, "BETWEEN ");
if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
appendStringInfoString(buf, "UNBOUNDED PRECEDING "); appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW) else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
appendStringInfoString(buf, "CURRENT ROW "); appendStringInfoString(buf, "CURRENT ROW ");
else if (wc->frameOptions & FRAMEOPTION_START_OFFSET) else if (frameOptions & FRAMEOPTION_START_OFFSET)
{ {
get_rule_expr(wc->startOffset, context, false); get_rule_expr(startOffset, context, false);
if (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING) if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
appendStringInfoString(buf, " PRECEDING "); appendStringInfoString(buf, " PRECEDING ");
else if (wc->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING) else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
appendStringInfoString(buf, " FOLLOWING "); appendStringInfoString(buf, " FOLLOWING ");
else else
Assert(false); Assert(false);
} }
else else
Assert(false); Assert(false);
if (wc->frameOptions & FRAMEOPTION_BETWEEN) if (frameOptions & FRAMEOPTION_BETWEEN)
{ {
appendStringInfoString(buf, "AND "); appendStringInfoString(buf, "AND ");
if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
appendStringInfoString(buf, "UNBOUNDED FOLLOWING "); appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW) else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
appendStringInfoString(buf, "CURRENT ROW "); appendStringInfoString(buf, "CURRENT ROW ");
else if (wc->frameOptions & FRAMEOPTION_END_OFFSET) else if (frameOptions & FRAMEOPTION_END_OFFSET)
{ {
get_rule_expr(wc->endOffset, context, false); get_rule_expr(endOffset, context, false);
if (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING) if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
appendStringInfoString(buf, " PRECEDING "); appendStringInfoString(buf, " PRECEDING ");
else if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING) else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
appendStringInfoString(buf, " FOLLOWING "); appendStringInfoString(buf, " FOLLOWING ");
else else
Assert(false); Assert(false);
@ -3295,16 +3317,15 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
else else
Assert(false); Assert(false);
} }
if (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
appendStringInfoString(buf, "EXCLUDE CURRENT ROW "); appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP) else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
appendStringInfoString(buf, "EXCLUDE GROUP "); appendStringInfoString(buf, "EXCLUDE GROUP ");
else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES) else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
appendStringInfoString(buf, "EXCLUDE TIES "); appendStringInfoString(buf, "EXCLUDE TIES ");
/* we will now have a trailing space; remove it */ /* we will now have a trailing space; remove it */
buf->len--; buf->data[--(buf->len)] = '\0';
} }
appendStringInfoChar(buf, ')');
} }
/* ---------- /* ----------
@ -6683,7 +6704,7 @@ get_rule_expr(Node *node, deparse_context *context,
else else
appendStringInfoString(buf, " NO INDENT"); appendStringInfoString(buf, " NO INDENT");
} }
if (xexpr->op == IS_DOCUMENT) if (xexpr->op == IS_DOCUMENT)
appendStringInfoString(buf, " IS DOCUMENT"); appendStringInfoString(buf, " IS DOCUMENT");
else else
@ -7612,30 +7633,50 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
appendStringInfoString(buf, ") OVER "); appendStringInfoString(buf, ") OVER ");
foreach(l, context->windowClause) if (context->windowClause)
{ {
WindowClause *wc = (WindowClause *) lfirst(l); /* Query-decompilation case: search the windowClause list */
foreach(l, context->windowClause)
if (wc->winref == wfunc->winref)
{ {
if (wc->name) WindowClause *wc = (WindowClause *) lfirst(l);
appendStringInfoString(buf, quote_identifier(wc->name));
else if (wc->winref == wfunc->winref)
get_rule_windowspec(wc, context->targetList, context); {
break; if (wc->name)
appendStringInfoString(buf, quote_identifier(wc->name));
else
get_rule_windowspec(wc, context->targetList, context);
break;
}
} }
} if (l == NULL)
if (l == NULL)
{
if (context->windowClause)
elog(ERROR, "could not find window clause for winref %u", elog(ERROR, "could not find window clause for winref %u",
wfunc->winref); wfunc->winref);
}
else
{
/* /*
* In EXPLAIN, we don't have window context information available, so * In EXPLAIN, search the namespace stack for a matching WindowAgg
* we have to settle for this: * node (probably it's always the first entry), and print winname.
*/ */
appendStringInfoString(buf, "(?)"); foreach(l, context->namespaces)
{
deparse_namespace *dpns = (deparse_namespace *) lfirst(l);
if (dpns->plan && IsA(dpns->plan, WindowAgg))
{
WindowAgg *wagg = (WindowAgg *) dpns->plan;
if (wagg->winref == wfunc->winref)
{
appendStringInfoString(buf, quote_identifier(wagg->winname));
break;
}
}
}
if (l == NULL)
elog(ERROR, "could not find window clause for winref %u",
wfunc->winref);
} }
} }