summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/explain.c117
-rw-r--r--src/backend/optimizer/plan/createplan.c39
-rw-r--r--src/backend/optimizer/plan/planner.c51
-rw-r--r--src/backend/utils/adt/ruleutils.c150
4 files changed, 290 insertions, 67 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
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 816a2b2a576..75e2b0b9036 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -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;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 014e80c30e6..a4d523dcb0f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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;
@@ -5915,6 +5920,52 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
}
/*
+ * 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
*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index d11a8a20eea..9e90acedb91 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -441,6 +441,9 @@ static void get_rule_orderby(List *orderList, List *targetList,
static void get_rule_windowclause(Query *query, deparse_context *context);
static void get_rule_windowspec(WindowClause *wc, List *targetList,
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,
deparse_context *context);
static void get_special_variable(Node *node, deparse_context *context,
@@ -6811,45 +6814,64 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
{
if (needspace)
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 ");
- else if (wc->frameOptions & FRAMEOPTION_ROWS)
+ else if (frameOptions & FRAMEOPTION_ROWS)
appendStringInfoString(buf, "ROWS ");
- else if (wc->frameOptions & FRAMEOPTION_GROUPS)
+ else if (frameOptions & FRAMEOPTION_GROUPS)
appendStringInfoString(buf, "GROUPS ");
else
Assert(false);
- if (wc->frameOptions & FRAMEOPTION_BETWEEN)
+ if (frameOptions & FRAMEOPTION_BETWEEN)
appendStringInfoString(buf, "BETWEEN ");
- if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
+ if (frameOptions & FRAMEOPTION_START_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 ");
- else if (wc->frameOptions & FRAMEOPTION_START_OFFSET)
+ else if (frameOptions & FRAMEOPTION_START_OFFSET)
{
- get_rule_expr(wc->startOffset, context, false);
- if (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
+ get_rule_expr(startOffset, context, false);
+ if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
appendStringInfoString(buf, " PRECEDING ");
- else if (wc->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
+ else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
appendStringInfoString(buf, " FOLLOWING ");
else
Assert(false);
}
else
Assert(false);
- if (wc->frameOptions & FRAMEOPTION_BETWEEN)
+ if (frameOptions & FRAMEOPTION_BETWEEN)
{
appendStringInfoString(buf, "AND ");
- if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
+ if (frameOptions & FRAMEOPTION_END_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 ");
- else if (wc->frameOptions & FRAMEOPTION_END_OFFSET)
+ else if (frameOptions & FRAMEOPTION_END_OFFSET)
{
- get_rule_expr(wc->endOffset, context, false);
- if (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
+ get_rule_expr(endOffset, context, false);
+ if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
appendStringInfoString(buf, " PRECEDING ");
- else if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
+ else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
appendStringInfoString(buf, " FOLLOWING ");
else
Assert(false);
@@ -6857,16 +6879,46 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
else
Assert(false);
}
- if (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
+ if (frameOptions & FRAMEOPTION_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 ");
- else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES)
+ else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
appendStringInfoString(buf, "EXCLUDE TIES ");
/* we will now have a trailing space; remove it */
- buf->len--;
+ buf->data[--(buf->len)] = '\0';
}
- appendStringInfoChar(buf, ')');
+}
+
+/*
+ * Return the description of a window's framing options as a palloc'd string
+ */
+char *
+get_window_frame_options_for_explain(int frameOptions,
+ Node *startOffset, Node *endOffset,
+ List *dpcontext, bool forceprefix)
+{
+ StringInfoData buf;
+ deparse_context context;
+
+ initStringInfo(&buf);
+ context.buf = &buf;
+ context.namespaces = dpcontext;
+ context.resultDesc = NULL;
+ context.targetList = NIL;
+ context.windowClause = NIL;
+ context.varprefix = forceprefix;
+ context.prettyFlags = 0;
+ context.wrapColumn = WRAP_COLUMN_DEFAULT;
+ context.indentLevel = 0;
+ context.colNamesVisible = true;
+ context.inGroupBy = false;
+ context.varInOrderBy = false;
+ context.appendparents = NULL;
+
+ get_window_frame_options(frameOptions, startOffset, endOffset, &context);
+
+ return buf.data;
}
/* ----------
@@ -11030,30 +11082,50 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
appendStringInfoString(buf, ") OVER ");
- foreach(l, context->windowClause)
+ if (context->windowClause)
{
- WindowClause *wc = (WindowClause *) lfirst(l);
-
- if (wc->winref == wfunc->winref)
+ /* Query-decompilation case: search the windowClause list */
+ foreach(l, context->windowClause)
{
- if (wc->name)
- appendStringInfoString(buf, quote_identifier(wc->name));
- else
- get_rule_windowspec(wc, context->targetList, context);
- break;
+ WindowClause *wc = (WindowClause *) lfirst(l);
+
+ if (wc->winref == wfunc->winref)
+ {
+ if (wc->name)
+ appendStringInfoString(buf, quote_identifier(wc->name));
+ else
+ get_rule_windowspec(wc, context->targetList, context);
+ break;
+ }
}
- }
- if (l == NULL)
- {
- if (context->windowClause)
+ if (l == NULL)
elog(ERROR, "could not find window clause for winref %u",
wfunc->winref);
-
+ }
+ else
+ {
/*
- * In EXPLAIN, we don't have window context information available, so
- * we have to settle for this:
+ * In EXPLAIN, search the namespace stack for a matching WindowAgg
+ * 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);
}
}