diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/explain.c | 117 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 39 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 51 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 150 |
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); } } |