mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +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:
@ -3968,10 +3968,11 @@ select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 gr
|
|||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------
|
||||||
Sort
|
Sort
|
||||||
Output: c2, (sum(c2)), (count(c2) OVER (?)), ((c2 % 2))
|
Output: c2, (sum(c2)), (count(c2) OVER w1), ((c2 % 2))
|
||||||
Sort Key: ft2.c2
|
Sort Key: ft2.c2
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Output: c2, (sum(c2)), count(c2) OVER (?), ((c2 % 2))
|
Output: c2, (sum(c2)), count(c2) OVER w1, ((c2 % 2))
|
||||||
|
Window: w1 AS (PARTITION BY ((ft2.c2 % 2)))
|
||||||
-> Sort
|
-> Sort
|
||||||
Output: c2, ((c2 % 2)), (sum(c2))
|
Output: c2, ((c2 % 2)), (sum(c2))
|
||||||
Sort Key: ((ft2.c2 % 2))
|
Sort Key: ((ft2.c2 % 2))
|
||||||
@ -3979,7 +3980,7 @@ select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 gr
|
|||||||
Output: c2, ((c2 % 2)), (sum(c2))
|
Output: c2, ((c2 % 2)), (sum(c2))
|
||||||
Relations: Aggregate on (public.ft2)
|
Relations: Aggregate on (public.ft2)
|
||||||
Remote SQL: SELECT c2, (c2 % 2), sum(c2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY 1
|
Remote SQL: SELECT c2, (c2 % 2), sum(c2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY 1
|
||||||
(12 rows)
|
(13 rows)
|
||||||
|
|
||||||
select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 group by c2 order by 1;
|
select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 group by c2 order by 1;
|
||||||
c2 | sum | count
|
c2 | sum | count
|
||||||
@ -4001,10 +4002,11 @@ select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 wher
|
|||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
Sort
|
Sort
|
||||||
Output: c2, (array_agg(c2) OVER (?)), ((c2 % 2))
|
Output: c2, (array_agg(c2) OVER w1), ((c2 % 2))
|
||||||
Sort Key: ft1.c2
|
Sort Key: ft1.c2
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Output: c2, array_agg(c2) OVER (?), ((c2 % 2))
|
Output: c2, array_agg(c2) OVER w1, ((c2 % 2))
|
||||||
|
Window: w1 AS (PARTITION BY ((ft1.c2 % 2)) ORDER BY ft1.c2)
|
||||||
-> Sort
|
-> Sort
|
||||||
Output: c2, ((c2 % 2))
|
Output: c2, ((c2 % 2))
|
||||||
Sort Key: ((ft1.c2 % 2)), ft1.c2 DESC
|
Sort Key: ((ft1.c2 % 2)), ft1.c2 DESC
|
||||||
@ -4012,7 +4014,7 @@ select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 wher
|
|||||||
Output: c2, ((c2 % 2))
|
Output: c2, ((c2 % 2))
|
||||||
Relations: Aggregate on (public.ft1)
|
Relations: Aggregate on (public.ft1)
|
||||||
Remote SQL: SELECT c2, (c2 % 2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY 1
|
Remote SQL: SELECT c2, (c2 % 2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY 1
|
||||||
(12 rows)
|
(13 rows)
|
||||||
|
|
||||||
select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 where c2 < 10 group by c2 order by 1;
|
select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 where c2 < 10 group by c2 order by 1;
|
||||||
c2 | array_agg
|
c2 | array_agg
|
||||||
@ -4032,12 +4034,13 @@ select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 wher
|
|||||||
explain (verbose, costs off)
|
explain (verbose, costs off)
|
||||||
select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1;
|
select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------
|
||||||
Sort
|
Sort
|
||||||
Output: c2, (array_agg(c2) OVER (?)), ((c2 % 2))
|
Output: c2, (array_agg(c2) OVER w1), ((c2 % 2))
|
||||||
Sort Key: ft1.c2
|
Sort Key: ft1.c2
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Output: c2, array_agg(c2) OVER (?), ((c2 % 2))
|
Output: c2, array_agg(c2) OVER w1, ((c2 % 2))
|
||||||
|
Window: w1 AS (PARTITION BY ((ft1.c2 % 2)) ORDER BY ft1.c2 RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Output: c2, ((c2 % 2))
|
Output: c2, ((c2 % 2))
|
||||||
Sort Key: ((ft1.c2 % 2)), ft1.c2
|
Sort Key: ((ft1.c2 % 2)), ft1.c2
|
||||||
@ -4045,7 +4048,7 @@ select c2, array_agg(c2) over (partition by c2%2 order by c2 range between curre
|
|||||||
Output: c2, ((c2 % 2))
|
Output: c2, ((c2 % 2))
|
||||||
Relations: Aggregate on (public.ft1)
|
Relations: Aggregate on (public.ft1)
|
||||||
Remote SQL: SELECT c2, (c2 % 2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY 1
|
Remote SQL: SELECT c2, (c2 % 2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY 1
|
||||||
(12 rows)
|
(13 rows)
|
||||||
|
|
||||||
select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1;
|
select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1;
|
||||||
c2 | array_agg
|
c2 | array_agg
|
||||||
|
@ -107,6 +107,11 @@ static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
|
|||||||
List *ancestors, ExplainState *es);
|
List *ancestors, ExplainState *es);
|
||||||
static void show_sortorder_options(StringInfo buf, Node *sortexpr,
|
static void show_sortorder_options(StringInfo buf, Node *sortexpr,
|
||||||
Oid sortOperator, Oid collation, bool nullsFirst);
|
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,
|
static void show_storage_info(char *maxStorageType, int64 maxSpaceUsed,
|
||||||
ExplainState *es);
|
ExplainState *es);
|
||||||
static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
|
static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
|
||||||
@ -2333,12 +2338,13 @@ ExplainNode(PlanState *planstate, List *ancestors,
|
|||||||
planstate, es);
|
planstate, es);
|
||||||
break;
|
break;
|
||||||
case T_WindowAgg:
|
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);
|
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
|
||||||
if (plan->qual)
|
if (plan->qual)
|
||||||
show_instrumentation_count("Rows Removed by Filter", 1,
|
show_instrumentation_count("Rows Removed by Filter", 1,
|
||||||
planstate, es);
|
planstate, es);
|
||||||
show_upper_qual(((WindowAgg *) plan)->runConditionOrig,
|
|
||||||
"Run Condition", planstate, ancestors, es);
|
|
||||||
show_windowagg_info(castNode(WindowAggState, planstate), es);
|
show_windowagg_info(castNode(WindowAggState, planstate), es);
|
||||||
break;
|
break;
|
||||||
case T_Group:
|
case T_Group:
|
||||||
@ -3007,6 +3013,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.
|
* Show information on storage method and maximum memory/disk space used.
|
||||||
*/
|
*/
|
||||||
|
@ -285,12 +285,9 @@ static Memoize *make_memoize(Plan *lefttree, Oid *hashoperators,
|
|||||||
Oid *collations, List *param_exprs,
|
Oid *collations, List *param_exprs,
|
||||||
bool singlerow, bool binary_mode,
|
bool singlerow, bool binary_mode,
|
||||||
uint32 est_entries, Bitmapset *keyparamids);
|
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 partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
|
||||||
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
|
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,
|
List *runCondition, List *qual, bool topWindow,
|
||||||
Plan *lefttree);
|
Plan *lefttree);
|
||||||
static Group *make_group(List *tlist, List *qual, int numGroupCols,
|
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 */
|
/* And finally we can make the WindowAgg node */
|
||||||
plan = make_windowagg(tlist,
|
plan = make_windowagg(tlist,
|
||||||
wc->winref,
|
wc,
|
||||||
partNumCols,
|
partNumCols,
|
||||||
partColIdx,
|
partColIdx,
|
||||||
partOperators,
|
partOperators,
|
||||||
@ -2692,14 +2689,6 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
|
|||||||
ordColIdx,
|
ordColIdx,
|
||||||
ordOperators,
|
ordOperators,
|
||||||
ordCollations,
|
ordCollations,
|
||||||
wc->frameOptions,
|
|
||||||
wc->startOffset,
|
|
||||||
wc->endOffset,
|
|
||||||
wc->startInRangeFunc,
|
|
||||||
wc->endInRangeFunc,
|
|
||||||
wc->inRangeColl,
|
|
||||||
wc->inRangeAsc,
|
|
||||||
wc->inRangeNullsFirst,
|
|
||||||
best_path->runCondition,
|
best_path->runCondition,
|
||||||
best_path->qual,
|
best_path->qual,
|
||||||
best_path->topwindow,
|
best_path->topwindow,
|
||||||
@ -6704,18 +6693,16 @@ make_agg(List *tlist, List *qual,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static WindowAgg *
|
static WindowAgg *
|
||||||
make_windowagg(List *tlist, Index winref,
|
make_windowagg(List *tlist, WindowClause *wc,
|
||||||
int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
|
int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
|
||||||
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
|
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)
|
List *runCondition, List *qual, bool topWindow, Plan *lefttree)
|
||||||
{
|
{
|
||||||
WindowAgg *node = makeNode(WindowAgg);
|
WindowAgg *node = makeNode(WindowAgg);
|
||||||
Plan *plan = &node->plan;
|
Plan *plan = &node->plan;
|
||||||
|
|
||||||
node->winref = winref;
|
node->winname = wc->name;
|
||||||
|
node->winref = wc->winref;
|
||||||
node->partNumCols = partNumCols;
|
node->partNumCols = partNumCols;
|
||||||
node->partColIdx = partColIdx;
|
node->partColIdx = partColIdx;
|
||||||
node->partOperators = partOperators;
|
node->partOperators = partOperators;
|
||||||
@ -6724,17 +6711,17 @@ make_windowagg(List *tlist, Index winref,
|
|||||||
node->ordColIdx = ordColIdx;
|
node->ordColIdx = ordColIdx;
|
||||||
node->ordOperators = ordOperators;
|
node->ordOperators = ordOperators;
|
||||||
node->ordCollations = ordCollations;
|
node->ordCollations = ordCollations;
|
||||||
node->frameOptions = frameOptions;
|
node->frameOptions = wc->frameOptions;
|
||||||
node->startOffset = startOffset;
|
node->startOffset = wc->startOffset;
|
||||||
node->endOffset = endOffset;
|
node->endOffset = wc->endOffset;
|
||||||
node->runCondition = runCondition;
|
node->runCondition = runCondition;
|
||||||
/* a duplicate of the above for EXPLAIN */
|
/* a duplicate of the above for EXPLAIN */
|
||||||
node->runConditionOrig = runCondition;
|
node->runConditionOrig = runCondition;
|
||||||
node->startInRangeFunc = startInRangeFunc;
|
node->startInRangeFunc = wc->startInRangeFunc;
|
||||||
node->endInRangeFunc = endInRangeFunc;
|
node->endInRangeFunc = wc->endInRangeFunc;
|
||||||
node->inRangeColl = inRangeColl;
|
node->inRangeColl = wc->inRangeColl;
|
||||||
node->inRangeAsc = inRangeAsc;
|
node->inRangeAsc = wc->inRangeAsc;
|
||||||
node->inRangeNullsFirst = inRangeNullsFirst;
|
node->inRangeNullsFirst = wc->inRangeNullsFirst;
|
||||||
node->topWindow = topWindow;
|
node->topWindow = topWindow;
|
||||||
|
|
||||||
plan->targetlist = tlist;
|
plan->targetlist = tlist;
|
||||||
|
@ -214,6 +214,7 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
|
|||||||
static void optimize_window_clauses(PlannerInfo *root,
|
static void optimize_window_clauses(PlannerInfo *root,
|
||||||
WindowFuncLists *wflists);
|
WindowFuncLists *wflists);
|
||||||
static List *select_active_windows(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,
|
static PathTarget *make_window_input_target(PlannerInfo *root,
|
||||||
PathTarget *final_target,
|
PathTarget *final_target,
|
||||||
List *activeWindows);
|
List *activeWindows);
|
||||||
@ -1539,7 +1540,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction,
|
|||||||
*/
|
*/
|
||||||
optimize_window_clauses(root, wflists);
|
optimize_window_clauses(root, wflists);
|
||||||
|
|
||||||
|
/* Extract the list of windows actually in use. */
|
||||||
activeWindows = select_active_windows(root, wflists);
|
activeWindows = select_active_windows(root, wflists);
|
||||||
|
|
||||||
|
/* Make sure they all have names, for EXPLAIN's use. */
|
||||||
|
name_active_windows(activeWindows);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
parse->hasWindowFuncs = false;
|
parse->hasWindowFuncs = false;
|
||||||
@ -5914,6 +5919,52 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
|
|||||||
return result;
|
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
|
* common_prefix_cmp
|
||||||
* QSort comparison function for WindowClauseSortData
|
* QSort comparison function for WindowClauseSortData
|
||||||
|
@ -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_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,
|
||||||
@ -6811,45 +6814,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);
|
||||||
@ -6857,16 +6879,46 @@ 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, ')');
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,6 +11082,9 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
|
|||||||
|
|
||||||
appendStringInfoString(buf, ") OVER ");
|
appendStringInfoString(buf, ") OVER ");
|
||||||
|
|
||||||
|
if (context->windowClause)
|
||||||
|
{
|
||||||
|
/* Query-decompilation case: search the windowClause list */
|
||||||
foreach(l, context->windowClause)
|
foreach(l, context->windowClause)
|
||||||
{
|
{
|
||||||
WindowClause *wc = (WindowClause *) lfirst(l);
|
WindowClause *wc = (WindowClause *) lfirst(l);
|
||||||
@ -11044,16 +11099,33 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1171,6 +1171,9 @@ typedef struct WindowAgg
|
|||||||
{
|
{
|
||||||
Plan plan;
|
Plan plan;
|
||||||
|
|
||||||
|
/* name of WindowClause implemented by this node */
|
||||||
|
char *winname;
|
||||||
|
|
||||||
/* ID referenced by window functions */
|
/* ID referenced by window functions */
|
||||||
Index winref;
|
Index winref;
|
||||||
|
|
||||||
|
@ -43,6 +43,11 @@ extern List *set_deparse_context_plan(List *dpcontext,
|
|||||||
struct Plan *plan, List *ancestors);
|
struct Plan *plan, List *ancestors);
|
||||||
extern List *select_rtable_names_for_explain(List *rtable,
|
extern List *select_rtable_names_for_explain(List *rtable,
|
||||||
Bitmapset *rels_used);
|
Bitmapset *rels_used);
|
||||||
|
extern char *get_window_frame_options_for_explain(int frameOptions,
|
||||||
|
Node *startOffset,
|
||||||
|
Node *endOffset,
|
||||||
|
List *dpcontext,
|
||||||
|
bool forceprefix);
|
||||||
extern char *generate_collation_name(Oid collid);
|
extern char *generate_collation_name(Oid collid);
|
||||||
extern char *generate_opclass_name(Oid opclass);
|
extern char *generate_opclass_name(Oid opclass);
|
||||||
extern char *get_range_partbound_string(List *bound_datums);
|
extern char *get_range_partbound_string(List *bound_datums);
|
||||||
|
@ -595,11 +595,12 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id
|
SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id
|
||||||
FROM quad_box_tbl;
|
FROM quad_box_tbl;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (b <-> '(123,456)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Scan using quad_box_tbl_idx on quad_box_tbl
|
-> Index Scan using quad_box_tbl_idx on quad_box_tbl
|
||||||
Order By: (b <-> '(123,456)'::point)
|
Order By: (b <-> '(123,456)'::point)
|
||||||
(3 rows)
|
(4 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE quad_box_tbl_ord_idx1 AS
|
CREATE TEMP TABLE quad_box_tbl_ord_idx1 AS
|
||||||
SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id
|
SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id
|
||||||
@ -617,12 +618,13 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id
|
SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id
|
||||||
FROM quad_box_tbl WHERE b <@ box '((200,300),(500,600))';
|
FROM quad_box_tbl WHERE b <@ box '((200,300),(500,600))';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (b <-> '(123,456)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Scan using quad_box_tbl_idx on quad_box_tbl
|
-> Index Scan using quad_box_tbl_idx on quad_box_tbl
|
||||||
Index Cond: (b <@ '(500,600),(200,300)'::box)
|
Index Cond: (b <@ '(500,600),(200,300)'::box)
|
||||||
Order By: (b <-> '(123,456)'::point)
|
Order By: (b <-> '(123,456)'::point)
|
||||||
(4 rows)
|
(5 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE quad_box_tbl_ord_idx2 AS
|
CREATE TEMP TABLE quad_box_tbl_ord_idx2 AS
|
||||||
SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id
|
SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id
|
||||||
|
@ -330,11 +330,12 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
||||||
FROM quad_point_tbl;
|
FROM quad_point_tbl;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (p <-> '(0,0)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Order By: (p <-> '(0,0)'::point)
|
Order By: (p <-> '(0,0)'::point)
|
||||||
(3 rows)
|
(4 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE quad_point_tbl_ord_idx1 AS
|
CREATE TEMP TABLE quad_point_tbl_ord_idx1 AS
|
||||||
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
||||||
@ -350,12 +351,13 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
||||||
FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)';
|
FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (p <-> '(0,0)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
||||||
Order By: (p <-> '(0,0)'::point)
|
Order By: (p <-> '(0,0)'::point)
|
||||||
(4 rows)
|
(5 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE quad_point_tbl_ord_idx2 AS
|
CREATE TEMP TABLE quad_point_tbl_ord_idx2 AS
|
||||||
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
||||||
@ -371,12 +373,13 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p
|
||||||
FROM quad_point_tbl WHERE p IS NOT NULL;
|
FROM quad_point_tbl WHERE p IS NOT NULL;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (p <-> '(333,400)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: (p IS NOT NULL)
|
Index Cond: (p IS NOT NULL)
|
||||||
Order By: (p <-> '(333,400)'::point)
|
Order By: (p <-> '(333,400)'::point)
|
||||||
(4 rows)
|
(5 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE quad_point_tbl_ord_idx3 AS
|
CREATE TEMP TABLE quad_point_tbl_ord_idx3 AS
|
||||||
SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p
|
||||||
@ -497,11 +500,12 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
||||||
FROM kd_point_tbl;
|
FROM kd_point_tbl;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (p <-> '(0,0)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Order By: (p <-> '(0,0)'::point)
|
Order By: (p <-> '(0,0)'::point)
|
||||||
(3 rows)
|
(4 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE kd_point_tbl_ord_idx1 AS
|
CREATE TEMP TABLE kd_point_tbl_ord_idx1 AS
|
||||||
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
||||||
@ -517,12 +521,13 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
||||||
FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)';
|
FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (p <-> '(0,0)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
||||||
Order By: (p <-> '(0,0)'::point)
|
Order By: (p <-> '(0,0)'::point)
|
||||||
(4 rows)
|
(5 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE kd_point_tbl_ord_idx2 AS
|
CREATE TEMP TABLE kd_point_tbl_ord_idx2 AS
|
||||||
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p
|
||||||
@ -538,12 +543,13 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p
|
||||||
FROM kd_point_tbl WHERE p IS NOT NULL;
|
FROM kd_point_tbl WHERE p IS NOT NULL;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (p <-> '(333,400)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: (p IS NOT NULL)
|
Index Cond: (p IS NOT NULL)
|
||||||
Order By: (p <-> '(333,400)'::point)
|
Order By: (p <-> '(333,400)'::point)
|
||||||
(4 rows)
|
(5 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE kd_point_tbl_ord_idx3 AS
|
CREATE TEMP TABLE kd_point_tbl_ord_idx3 AS
|
||||||
SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p
|
SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p
|
||||||
|
@ -243,6 +243,42 @@ select explain_filter('explain (buffers, format json) select * from int8_tbl i8'
|
|||||||
]
|
]
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- Check expansion of window definitions
|
||||||
|
select explain_filter('explain verbose select sum(unique1) over w, sum(unique2) over (w order by hundred), sum(tenthous) over (w order by hundred) from tenk1 window w as (partition by ten)');
|
||||||
|
explain_filter
|
||||||
|
-------------------------------------------------------------------------------------------------------
|
||||||
|
WindowAgg (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: sum(unique1) OVER w, (sum(unique2) OVER w1), (sum(tenthous) OVER w1), ten, hundred
|
||||||
|
Window: w AS (PARTITION BY tenk1.ten)
|
||||||
|
-> WindowAgg (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: ten, hundred, unique1, unique2, tenthous, sum(unique2) OVER w1, sum(tenthous) OVER w1
|
||||||
|
Window: w1 AS (PARTITION BY tenk1.ten ORDER BY tenk1.hundred)
|
||||||
|
-> Sort (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: ten, hundred, unique1, unique2, tenthous
|
||||||
|
Sort Key: tenk1.ten, tenk1.hundred
|
||||||
|
-> Seq Scan on public.tenk1 (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: ten, hundred, unique1, unique2, tenthous
|
||||||
|
(11 rows)
|
||||||
|
|
||||||
|
select explain_filter('explain verbose select sum(unique1) over w1, sum(unique2) over (w1 order by hundred), sum(tenthous) over (w1 order by hundred rows 10 preceding) from tenk1 window w1 as (partition by ten)');
|
||||||
|
explain_filter
|
||||||
|
---------------------------------------------------------------------------------------------------------
|
||||||
|
WindowAgg (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: sum(unique1) OVER w1, (sum(unique2) OVER w2), (sum(tenthous) OVER w3), ten, hundred
|
||||||
|
Window: w1 AS (PARTITION BY tenk1.ten)
|
||||||
|
-> WindowAgg (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: ten, hundred, unique1, unique2, tenthous, (sum(unique2) OVER w2), sum(tenthous) OVER w3
|
||||||
|
Window: w3 AS (PARTITION BY tenk1.ten ORDER BY tenk1.hundred ROWS 'N'::bigint PRECEDING)
|
||||||
|
-> WindowAgg (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: ten, hundred, unique1, unique2, tenthous, sum(unique2) OVER w2
|
||||||
|
Window: w2 AS (PARTITION BY tenk1.ten ORDER BY tenk1.hundred)
|
||||||
|
-> Sort (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: ten, hundred, unique1, unique2, tenthous
|
||||||
|
Sort Key: tenk1.ten, tenk1.hundred
|
||||||
|
-> Seq Scan on public.tenk1 (cost=N.N..N.N rows=N width=N)
|
||||||
|
Output: ten, hundred, unique1, unique2, tenthous
|
||||||
|
(14 rows)
|
||||||
|
|
||||||
-- Check output including I/O timings. These fields are conditional
|
-- Check output including I/O timings. These fields are conditional
|
||||||
-- but always set in JSON format, so check them only in this case.
|
-- but always set in JSON format, so check them only in this case.
|
||||||
set track_io_timing = on;
|
set track_io_timing = on;
|
||||||
@ -742,11 +778,12 @@ select explain_filter('explain (analyze,buffers off,costs off) select sum(n) ove
|
|||||||
explain_filter
|
explain_filter
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
|
WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
|
||||||
|
Window: w1 AS ()
|
||||||
Storage: Memory Maximum Storage: NkB
|
Storage: Memory Maximum Storage: NkB
|
||||||
-> Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
|
-> Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
|
||||||
Planning Time: N.N ms
|
Planning Time: N.N ms
|
||||||
Execution Time: N.N ms
|
Execution Time: N.N ms
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
-- Test tuplestore storage usage in Window aggregate (disk case)
|
-- Test tuplestore storage usage in Window aggregate (disk case)
|
||||||
set work_mem to 64;
|
set work_mem to 64;
|
||||||
@ -754,17 +791,19 @@ select explain_filter('explain (analyze,buffers off,costs off) select sum(n) ove
|
|||||||
explain_filter
|
explain_filter
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
|
WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
|
||||||
|
Window: w1 AS ()
|
||||||
Storage: Disk Maximum Storage: NkB
|
Storage: Disk Maximum Storage: NkB
|
||||||
-> Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
|
-> Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
|
||||||
Planning Time: N.N ms
|
Planning Time: N.N ms
|
||||||
Execution Time: N.N ms
|
Execution Time: N.N ms
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
-- Test tuplestore storage usage in Window aggregate (memory and disk case, final result is disk)
|
-- Test tuplestore storage usage in Window aggregate (memory and disk case, final result is disk)
|
||||||
select explain_filter('explain (analyze,buffers off,costs off) select sum(n) over(partition by m) from (SELECT n < 3 as m, n from generate_series(1,2000) a(n))');
|
select explain_filter('explain (analyze,buffers off,costs off) select sum(n) over(partition by m) from (SELECT n < 3 as m, n from generate_series(1,2000) a(n))');
|
||||||
explain_filter
|
explain_filter
|
||||||
----------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------
|
||||||
WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
|
WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
|
||||||
|
Window: w1 AS (PARTITION BY ((a.n < N)))
|
||||||
Storage: Disk Maximum Storage: NkB
|
Storage: Disk Maximum Storage: NkB
|
||||||
-> Sort (actual time=N.N..N.N rows=N.N loops=N)
|
-> Sort (actual time=N.N..N.N rows=N.N loops=N)
|
||||||
Sort Key: ((a.n < N))
|
Sort Key: ((a.n < N))
|
||||||
@ -772,6 +811,6 @@ select explain_filter('explain (analyze,buffers off,costs off) select sum(n) ove
|
|||||||
-> Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
|
-> Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
|
||||||
Planning Time: N.N ms
|
Planning Time: N.N ms
|
||||||
Execution Time: N.N ms
|
Execution Time: N.N ms
|
||||||
(8 rows)
|
(9 rows)
|
||||||
|
|
||||||
reset work_mem;
|
reset work_mem;
|
||||||
|
@ -1427,6 +1427,7 @@ order by t1.a;
|
|||||||
Sort
|
Sort
|
||||||
Sort Key: t1.a
|
Sort Key: t1.a
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY t2.a)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: t2.a
|
Sort Key: t2.a
|
||||||
-> Nested Loop Left Join
|
-> Nested Loop Left Join
|
||||||
@ -1434,7 +1435,7 @@ order by t1.a;
|
|||||||
-> Seq Scan on gtest32 t1
|
-> Seq Scan on gtest32 t1
|
||||||
-> Materialize
|
-> Materialize
|
||||||
-> Seq Scan on gtest32 t2
|
-> Seq Scan on gtest32 t2
|
||||||
(10 rows)
|
(11 rows)
|
||||||
|
|
||||||
select sum(t2.b) over (partition by t2.a),
|
select sum(t2.b) over (partition by t2.a),
|
||||||
sum(t2.c) over (partition by t2.a),
|
sum(t2.c) over (partition by t2.a),
|
||||||
|
@ -1436,8 +1436,9 @@ explain (costs off)
|
|||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
Sort
|
Sort
|
||||||
Sort Key: (sum((sum(c))) OVER (?)), a, b
|
Sort Key: (sum((sum(c))) OVER w1), a, b
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY a, b)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: a, b
|
Sort Key: a, b
|
||||||
-> MixedAggregate
|
-> MixedAggregate
|
||||||
@ -1446,7 +1447,7 @@ explain (costs off)
|
|||||||
Hash Key: b
|
Hash Key: b
|
||||||
Group Key: ()
|
Group Key: ()
|
||||||
-> Seq Scan on gstest2
|
-> Seq Scan on gstest2
|
||||||
(11 rows)
|
(12 rows)
|
||||||
|
|
||||||
select a, b, sum(v.x)
|
select a, b, sum(v.x)
|
||||||
from (values (1),(2)) v(x), gstest_data(v.x)
|
from (values (1),(2)) v(x), gstest_data(v.x)
|
||||||
@ -2428,8 +2429,9 @@ select a, b, row_number() over (order by a, b nulls first)
|
|||||||
from (values (1, 1), (2, 2)) as t (a, b) where a = b
|
from (values (1, 1), (2, 2)) as t (a, b) where a = b
|
||||||
group by grouping sets((a, b), (a));
|
group by grouping sets((a, b), (a));
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY "*VALUES*".column1, "*VALUES*".column2 ROWS UNBOUNDED PRECEDING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: "*VALUES*".column1, "*VALUES*".column2 NULLS FIRST
|
Sort Key: "*VALUES*".column1, "*VALUES*".column2 NULLS FIRST
|
||||||
-> HashAggregate
|
-> HashAggregate
|
||||||
@ -2437,7 +2439,7 @@ group by grouping sets((a, b), (a));
|
|||||||
Hash Key: "*VALUES*".column1
|
Hash Key: "*VALUES*".column1
|
||||||
-> Values Scan on "*VALUES*"
|
-> Values Scan on "*VALUES*"
|
||||||
Filter: (column1 = column2)
|
Filter: (column1 = column2)
|
||||||
(8 rows)
|
(9 rows)
|
||||||
|
|
||||||
select a, b, row_number() over (order by a, b nulls first)
|
select a, b, row_number() over (order by a, b nulls first)
|
||||||
from (values (1, 1), (2, 2)) as t (a, b) where a = b
|
from (values (1, 1), (2, 2)) as t (a, b) where a = b
|
||||||
|
@ -4678,6 +4678,7 @@ select min(a) over (partition by a order by a) from part_abc where a >= stable_o
|
|||||||
Append
|
Append
|
||||||
-> Subquery Scan on "*SELECT* 1_1"
|
-> Subquery Scan on "*SELECT* 1_1"
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY part_abc.a ORDER BY part_abc.a)
|
||||||
-> Append
|
-> Append
|
||||||
Subplans Removed: 1
|
Subplans Removed: 1
|
||||||
-> Index Scan using part_abc_2_a_idx on part_abc_2 part_abc_1
|
-> Index Scan using part_abc_2_a_idx on part_abc_2 part_abc_1
|
||||||
@ -4694,6 +4695,7 @@ select min(a) over (partition by a order by a) from part_abc where a >= stable_o
|
|||||||
Filter: (d <= stable_one())
|
Filter: (d <= stable_one())
|
||||||
-> Subquery Scan on "*SELECT* 2"
|
-> Subquery Scan on "*SELECT* 2"
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY part_abc_5.a ORDER BY part_abc_5.a)
|
||||||
-> Append
|
-> Append
|
||||||
Subplans Removed: 1
|
Subplans Removed: 1
|
||||||
-> Index Scan using part_abc_2_a_idx on part_abc_2 part_abc_6
|
-> Index Scan using part_abc_2_a_idx on part_abc_2 part_abc_6
|
||||||
@ -4708,7 +4710,7 @@ select min(a) over (partition by a order by a) from part_abc where a >= stable_o
|
|||||||
-> Index Scan using part_abc_3_3_a_idx on part_abc_3_3 part_abc_9
|
-> Index Scan using part_abc_3_3_a_idx on part_abc_3_3 part_abc_9
|
||||||
Index Cond: (a >= (stable_one() + 1))
|
Index Cond: (a >= (stable_one() + 1))
|
||||||
Filter: (d >= stable_one())
|
Filter: (d >= stable_one())
|
||||||
(33 rows)
|
(35 rows)
|
||||||
|
|
||||||
drop view part_abc_view;
|
drop view part_abc_view;
|
||||||
drop table part_abc;
|
drop table part_abc;
|
||||||
|
@ -286,10 +286,11 @@ FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))
|
|||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY (p <-> '(123,456)'::point) ROWS UNBOUNDED PRECEDING)
|
||||||
-> Index Scan using quad_poly_tbl_idx on quad_poly_tbl
|
-> Index Scan using quad_poly_tbl_idx on quad_poly_tbl
|
||||||
Index Cond: (p <@ '((300,300),(400,600),(600,500),(700,200))'::polygon)
|
Index Cond: (p <@ '((300,300),(400,600),(600,500),(700,200))'::polygon)
|
||||||
Order By: (p <-> '(123,456)'::point)
|
Order By: (p <-> '(123,456)'::point)
|
||||||
(4 rows)
|
(5 rows)
|
||||||
|
|
||||||
CREATE TEMP TABLE quad_poly_tbl_ord_idx2 AS
|
CREATE TEMP TABLE quad_poly_tbl_ord_idx2 AS
|
||||||
SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id
|
SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id
|
||||||
|
@ -1130,9 +1130,10 @@ explain (costs off, verbose)
|
|||||||
Aggregate
|
Aggregate
|
||||||
Output: count(*)
|
Output: count(*)
|
||||||
-> Hash Right Semi Join
|
-> Hash Right Semi Join
|
||||||
Hash Cond: ((b.unique1 = a.unique1) AND ((row_number() OVER (?)) = a.two))
|
Hash Cond: ((b.unique1 = a.unique1) AND ((row_number() OVER w1) = a.two))
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Output: b.unique1, row_number() OVER (?)
|
Output: b.unique1, row_number() OVER w1
|
||||||
|
Window: w1 AS (ROWS UNBOUNDED PRECEDING)
|
||||||
-> Gather
|
-> Gather
|
||||||
Output: b.unique1
|
Output: b.unique1
|
||||||
Workers Planned: 4
|
Workers Planned: 4
|
||||||
@ -1145,7 +1146,7 @@ explain (costs off, verbose)
|
|||||||
Workers Planned: 4
|
Workers Planned: 4
|
||||||
-> Parallel Seq Scan on public.tenk1 a
|
-> Parallel Seq Scan on public.tenk1 a
|
||||||
Output: a.unique1, a.two
|
Output: a.unique1, a.two
|
||||||
(18 rows)
|
(19 rows)
|
||||||
|
|
||||||
-- LIMIT/OFFSET within sub-selects can't be pushed to workers.
|
-- LIMIT/OFFSET within sub-selects can't be pushed to workers.
|
||||||
explain (costs off)
|
explain (costs off)
|
||||||
|
@ -1012,16 +1012,17 @@ EXPLAIN (VERBOSE, COSTS OFF)
|
|||||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) OVER (PARTITION BY i % 2)
|
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) OVER (PARTITION BY i % 2)
|
||||||
FROM generate_series(1,5) i;
|
FROM generate_series(1,5) i;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Output: JSON_OBJECTAGG(i : (('111'::text || (i)::text))::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) OVER (?), ((i % 2))
|
Output: JSON_OBJECTAGG(i : (('111'::text || (i)::text))::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) OVER w1, ((i % 2))
|
||||||
|
Window: w1 AS (PARTITION BY ((i.i % 2)))
|
||||||
-> Sort
|
-> Sort
|
||||||
Output: ((i % 2)), i
|
Output: ((i % 2)), i
|
||||||
Sort Key: ((i.i % 2))
|
Sort Key: ((i.i % 2))
|
||||||
-> Function Scan on pg_catalog.generate_series i
|
-> Function Scan on pg_catalog.generate_series i
|
||||||
Output: (i % 2), i
|
Output: (i % 2), i
|
||||||
Function Call: generate_series(1, 5)
|
Function Call: generate_series(1, 5)
|
||||||
(8 rows)
|
(9 rows)
|
||||||
|
|
||||||
CREATE VIEW json_objectagg_view AS
|
CREATE VIEW json_objectagg_view AS
|
||||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
|
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
|
||||||
@ -1048,16 +1049,17 @@ EXPLAIN (VERBOSE, COSTS OFF)
|
|||||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (PARTITION BY i % 2)
|
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (PARTITION BY i % 2)
|
||||||
FROM generate_series(1,5) i;
|
FROM generate_series(1,5) i;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Output: JSON_ARRAYAGG((('111'::text || (i)::text))::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (?), ((i % 2))
|
Output: JSON_ARRAYAGG((('111'::text || (i)::text))::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER w1, ((i % 2))
|
||||||
|
Window: w1 AS (PARTITION BY ((i.i % 2)))
|
||||||
-> Sort
|
-> Sort
|
||||||
Output: ((i % 2)), i
|
Output: ((i % 2)), i
|
||||||
Sort Key: ((i.i % 2))
|
Sort Key: ((i.i % 2))
|
||||||
-> Function Scan on pg_catalog.generate_series i
|
-> Function Scan on pg_catalog.generate_series i
|
||||||
Output: (i % 2), i
|
Output: (i % 2), i
|
||||||
Function Call: generate_series(1, 5)
|
Function Call: generate_series(1, 5)
|
||||||
(8 rows)
|
(9 rows)
|
||||||
|
|
||||||
CREATE VIEW json_arrayagg_view AS
|
CREATE VIEW json_arrayagg_view AS
|
||||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
|
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
|
||||||
|
@ -652,10 +652,11 @@ select first_value(max(x)) over (), y
|
|||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS ()
|
||||||
-> HashAggregate
|
-> HashAggregate
|
||||||
Group Key: (tenk1.ten + tenk1.four)
|
Group Key: (tenk1.ten + tenk1.four)
|
||||||
-> Seq Scan on tenk1
|
-> Seq Scan on tenk1
|
||||||
(4 rows)
|
(5 rows)
|
||||||
|
|
||||||
-- window functions returning pass-by-ref values from different rows
|
-- window functions returning pass-by-ref values from different rows
|
||||||
select x, lag(x, 1) over (order by x), lead(x, 3) over (order by x)
|
select x, lag(x, 1) over (order by x), lead(x, 3) over (order by x)
|
||||||
@ -3538,13 +3539,14 @@ select f1, sum(f1) over (partition by f1 order by f2
|
|||||||
range between 1 preceding and 1 following)
|
range between 1 preceding and 1 following)
|
||||||
from t1 where f1 = f2;
|
from t1 where f1 = f2;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY f1 ORDER BY f2 RANGE BETWEEN '1'::bigint PRECEDING AND '1'::bigint FOLLOWING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: f1
|
Sort Key: f1
|
||||||
-> Seq Scan on t1
|
-> Seq Scan on t1
|
||||||
Filter: (f1 = f2)
|
Filter: (f1 = f2)
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
select f1, sum(f1) over (partition by f1 order by f2
|
select f1, sum(f1) over (partition by f1 order by f2
|
||||||
range between 1 preceding and 1 following)
|
range between 1 preceding and 1 following)
|
||||||
@ -3584,13 +3586,14 @@ select f1, sum(f1) over (partition by f1 order by f2
|
|||||||
groups between 1 preceding and 1 following)
|
groups between 1 preceding and 1 following)
|
||||||
from t1 where f1 = f2;
|
from t1 where f1 = f2;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------
|
--------------------------------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY f1 ORDER BY f2 GROUPS BETWEEN '1'::bigint PRECEDING AND '1'::bigint FOLLOWING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: f1
|
Sort Key: f1
|
||||||
-> Seq Scan on t1
|
-> Seq Scan on t1
|
||||||
Filter: (f1 = f2)
|
Filter: (f1 = f2)
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
select f1, sum(f1) over (partition by f1 order by f2
|
select f1, sum(f1) over (partition by f1 order by f2
|
||||||
groups between 1 preceding and 1 following)
|
groups between 1 preceding and 1 following)
|
||||||
@ -3712,12 +3715,13 @@ SELECT
|
|||||||
CURRENT ROW AND UNBOUNDED FOLLOWING) cd
|
CURRENT ROW AND UNBOUNDED FOLLOWING) cd
|
||||||
FROM empsalary;
|
FROM empsalary;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------
|
--------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY depname ORDER BY enroll_date ROWS UNBOUNDED PRECEDING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: depname, enroll_date
|
Sort Key: depname, enroll_date
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(4 rows)
|
(5 rows)
|
||||||
|
|
||||||
-- Ensure WindowFuncs which cannot support their WindowClause's frameOptions
|
-- Ensure WindowFuncs which cannot support their WindowClause's frameOptions
|
||||||
-- being changed are untouched
|
-- being changed are untouched
|
||||||
@ -3732,17 +3736,19 @@ SELECT
|
|||||||
CURRENT ROW AND CURRENT ROW) cnt
|
CURRENT ROW AND CURRENT ROW) cnt
|
||||||
FROM empsalary;
|
FROM empsalary;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Output: empno, depname, (row_number() OVER (?)), (rank() OVER (?)), count(*) OVER (?), enroll_date
|
Output: empno, depname, (row_number() OVER w1), (rank() OVER w1), count(*) OVER w2, enroll_date
|
||||||
|
Window: w2 AS (PARTITION BY empsalary.depname ORDER BY empsalary.enroll_date RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Output: depname, enroll_date, empno, row_number() OVER (?), rank() OVER (?)
|
Output: depname, enroll_date, empno, row_number() OVER w1, rank() OVER w1
|
||||||
|
Window: w1 AS (PARTITION BY empsalary.depname ORDER BY empsalary.enroll_date ROWS UNBOUNDED PRECEDING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Output: depname, enroll_date, empno
|
Output: depname, enroll_date, empno
|
||||||
Sort Key: empsalary.depname, empsalary.enroll_date
|
Sort Key: empsalary.depname, empsalary.enroll_date
|
||||||
-> Seq Scan on pg_temp.empsalary
|
-> Seq Scan on pg_temp.empsalary
|
||||||
Output: depname, enroll_date, empno
|
Output: depname, enroll_date, empno
|
||||||
(9 rows)
|
(11 rows)
|
||||||
|
|
||||||
-- Ensure the above query gives us the expected results
|
-- Ensure the above query gives us the expected results
|
||||||
SELECT
|
SELECT
|
||||||
@ -3778,15 +3784,17 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE depname = 'sales';
|
WHERE depname = 'sales';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w2 AS ()
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY (((empsalary.depname)::text || 'A'::text)))
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: (((empsalary.depname)::text || 'A'::text))
|
Sort Key: (((empsalary.depname)::text || 'A'::text))
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
Filter: ((depname)::text = 'sales'::text)
|
Filter: ((depname)::text = 'sales'::text)
|
||||||
(7 rows)
|
(9 rows)
|
||||||
|
|
||||||
-- pushdown is unsafe because there's a PARTITION BY clause without depname:
|
-- pushdown is unsafe because there's a PARTITION BY clause without depname:
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
@ -3797,17 +3805,19 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE depname = 'sales';
|
WHERE depname = 'sales';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
Filter: ((emp.depname)::text = 'sales'::text)
|
Filter: ((emp.depname)::text = 'sales'::text)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w2 AS (PARTITION BY empsalary.enroll_date)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.enroll_date
|
Sort Key: empsalary.enroll_date
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY empsalary.depname)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.depname
|
Sort Key: empsalary.depname
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(9 rows)
|
(11 rows)
|
||||||
|
|
||||||
-- Test window function run conditions are properly pushed down into the
|
-- Test window function run conditions are properly pushed down into the
|
||||||
-- WindowAgg
|
-- WindowAgg
|
||||||
@ -3818,13 +3828,14 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE rn < 3;
|
WHERE rn < 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------
|
---------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (row_number() OVER (?) < 3)
|
Window: w1 AS (ORDER BY empsalary.empno ROWS UNBOUNDED PRECEDING)
|
||||||
|
Run Condition: (row_number() OVER w1 < 3)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.empno
|
Sort Key: empsalary.empno
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
-- The following 3 statements should result the same result.
|
-- The following 3 statements should result the same result.
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
@ -3869,13 +3880,14 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE r <= 3;
|
WHERE r <= 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------
|
----------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (rank() OVER (?) <= 3)
|
Window: w1 AS (ORDER BY empsalary.salary ROWS UNBOUNDED PRECEDING)
|
||||||
|
Run Condition: (rank() OVER w1 <= 3)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.salary DESC
|
Sort Key: empsalary.salary DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
(SELECT empno,
|
(SELECT empno,
|
||||||
@ -3899,15 +3911,16 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE dr = 1;
|
WHERE dr = 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
Filter: (emp.dr = 1)
|
Filter: (emp.dr = 1)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Run Condition: (dense_rank() OVER (?) <= 1)
|
Window: w1 AS (ORDER BY empsalary.salary ROWS UNBOUNDED PRECEDING)
|
||||||
|
Run Condition: (dense_rank() OVER w1 <= 1)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.salary DESC
|
Sort Key: empsalary.salary DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(7 rows)
|
(8 rows)
|
||||||
|
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
(SELECT empno,
|
(SELECT empno,
|
||||||
@ -3929,13 +3942,14 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE c <= 3;
|
WHERE c <= 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------
|
---------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (count(*) OVER (?) <= 3)
|
Window: w1 AS (ORDER BY empsalary.salary)
|
||||||
|
Run Condition: (count(*) OVER w1 <= 3)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.salary DESC
|
Sort Key: empsalary.salary DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
(SELECT empno,
|
(SELECT empno,
|
||||||
@ -3958,13 +3972,14 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE c <= 3;
|
WHERE c <= 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
--------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (count(empsalary.empno) OVER (?) <= 3)
|
Window: w1 AS (ORDER BY empsalary.salary)
|
||||||
|
Run Condition: (count(empsalary.empno) OVER w1 <= 3)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.salary DESC
|
Sort Key: empsalary.salary DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
(SELECT empno,
|
(SELECT empno,
|
||||||
@ -3987,13 +4002,14 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE c >= 3;
|
WHERE c >= 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------
|
----------------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (count(*) OVER (?) >= 3)
|
Window: w1 AS (ORDER BY empsalary.salary ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
|
||||||
|
Run Condition: (count(*) OVER w1 >= 3)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.salary DESC
|
Sort Key: empsalary.salary DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
@ -4003,11 +4019,12 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE 11 <= c;
|
WHERE 11 <= c;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------
|
-------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (11 <= count(*) OVER (?))
|
Window: w1 AS ()
|
||||||
|
Run Condition: (11 <= count(*) OVER w1)
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(3 rows)
|
(4 rows)
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
@ -4018,15 +4035,16 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE dr = 1;
|
WHERE dr = 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------
|
----------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
Filter: (emp.dr = 1)
|
Filter: (emp.dr = 1)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Run Condition: (dense_rank() OVER (?) <= 1)
|
Window: w1 AS (ORDER BY empsalary.salary)
|
||||||
|
Run Condition: (dense_rank() OVER w1 <= 1)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.salary DESC
|
Sort Key: empsalary.salary DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(7 rows)
|
(8 rows)
|
||||||
|
|
||||||
-- Ensure we get a run condition when there's a PARTITION BY clause
|
-- Ensure we get a run condition when there's a PARTITION BY clause
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
@ -4037,13 +4055,14 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE rn < 3;
|
WHERE rn < 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (row_number() OVER (?) < 3)
|
Window: w1 AS (PARTITION BY empsalary.depname ORDER BY empsalary.empno ROWS UNBOUNDED PRECEDING)
|
||||||
|
Run Condition: (row_number() OVER w1 < 3)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.depname, empsalary.empno
|
Sort Key: empsalary.depname, empsalary.empno
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
-- and ensure we get the correct results from the above plan
|
-- and ensure we get the correct results from the above plan
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
@ -4072,14 +4091,15 @@ SELECT empno, depname FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE rn < 3;
|
WHERE rn < 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Run Condition: (row_number() OVER (?) < 3)
|
Window: w1 AS (PARTITION BY empsalary.depname ORDER BY empsalary.empno ROWS UNBOUNDED PRECEDING)
|
||||||
|
Run Condition: (row_number() OVER w1 < 3)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.depname, empsalary.empno
|
Sort Key: empsalary.depname, empsalary.empno
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(6 rows)
|
(7 rows)
|
||||||
|
|
||||||
-- likewise with count(empno) instead of row_number()
|
-- likewise with count(empno) instead of row_number()
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
@ -4091,13 +4111,14 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE c <= 3;
|
WHERE c <= 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (count(empsalary.empno) OVER (?) <= 3)
|
Window: w1 AS (PARTITION BY empsalary.depname ORDER BY empsalary.salary)
|
||||||
|
Run Condition: (count(empsalary.empno) OVER w1 <= 3)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.depname, empsalary.salary DESC
|
Sort Key: empsalary.depname, empsalary.salary DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
-- and again, check the results are what we expect.
|
-- and again, check the results are what we expect.
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
@ -4130,11 +4151,12 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE c = 1;
|
WHERE c = 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------------
|
-------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
Run Condition: (count(empsalary.empno) OVER (?) = 1)
|
Window: w1 AS ()
|
||||||
|
Run Condition: (count(empsalary.empno) OVER w1 = 1)
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(3 rows)
|
(4 rows)
|
||||||
|
|
||||||
-- Try another case with a WindowFunc with a byref return type
|
-- Try another case with a WindowFunc with a byref return type
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
@ -4158,22 +4180,25 @@ SELECT * FROM
|
|||||||
FROM empsalary
|
FROM empsalary
|
||||||
) e WHERE rn <= 1 AND c1 <= 3 AND nt < 2;
|
) e WHERE rn <= 1 AND c1 <= 3 AND nt < 2;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------------------
|
||||||
Subquery Scan on e
|
Subquery Scan on e
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Filter: (((row_number() OVER (?)) <= 1) AND ((ntile(2) OVER (?)) < 2))
|
Window: w3 AS (PARTITION BY (((empsalary.depname)::text || ''::text)))
|
||||||
Run Condition: (count(empsalary.salary) OVER (?) <= 3)
|
Run Condition: (count(empsalary.salary) OVER w3 <= 3)
|
||||||
|
Filter: (((row_number() OVER w2) <= 1) AND ((ntile(2) OVER w2) < 2))
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: (((empsalary.depname)::text || ''::text))
|
Sort Key: (((empsalary.depname)::text || ''::text))
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Run Condition: ((row_number() OVER (?) <= 1) AND (ntile(2) OVER (?) < 2))
|
Window: w2 AS (PARTITION BY empsalary.depname)
|
||||||
|
Run Condition: ((row_number() OVER w2 <= 1) AND (ntile(2) OVER w2 < 2))
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.depname
|
Sort Key: empsalary.depname
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY ((''::text || (empsalary.depname)::text)))
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: ((''::text || (empsalary.depname)::text))
|
Sort Key: ((''::text || (empsalary.depname)::text))
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(14 rows)
|
(17 rows)
|
||||||
|
|
||||||
-- Ensure we correctly filter out all of the run conditions from each window
|
-- Ensure we correctly filter out all of the run conditions from each window
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
@ -4200,11 +4225,12 @@ SELECT 1 FROM
|
|||||||
WHERE e1.empno = e2.empno) s
|
WHERE e1.empno = e2.empno) s
|
||||||
WHERE s.c = 1;
|
WHERE s.c = 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
Subquery Scan on s
|
Subquery Scan on s
|
||||||
Filter: (s.c = 1)
|
Filter: (s.c = 1)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Run Condition: (ntile(e2.salary) OVER (?) <= 1)
|
Window: w1 AS (PARTITION BY e1.depname ROWS UNBOUNDED PRECEDING)
|
||||||
|
Run Condition: (ntile(e2.salary) OVER w1 <= 1)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: e1.depname
|
Sort Key: e1.depname
|
||||||
-> Merge Join
|
-> Merge Join
|
||||||
@ -4215,7 +4241,7 @@ WHERE s.c = 1;
|
|||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: e2.empno
|
Sort Key: e2.empno
|
||||||
-> Seq Scan on empsalary e2
|
-> Seq Scan on empsalary e2
|
||||||
(14 rows)
|
(15 rows)
|
||||||
|
|
||||||
-- Ensure the run condition optimization is used in cases where the WindowFunc
|
-- Ensure the run condition optimization is used in cases where the WindowFunc
|
||||||
-- has a Var from another query level
|
-- has a Var from another query level
|
||||||
@ -4225,15 +4251,16 @@ SELECT 1 FROM
|
|||||||
FROM (SELECT (SELECT 1) AS x) AS s1) s
|
FROM (SELECT (SELECT 1) AS x) AS s1) s
|
||||||
WHERE s.c = 1;
|
WHERE s.c = 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
Subquery Scan on s
|
Subquery Scan on s
|
||||||
Filter: (s.c = 1)
|
Filter: (s.c = 1)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
Run Condition: (ntile((InitPlan 1).col1) OVER (?) <= 1)
|
Window: w1 AS (ROWS UNBOUNDED PRECEDING)
|
||||||
|
Run Condition: (ntile((InitPlan 1).col1) OVER w1 <= 1)
|
||||||
InitPlan 1
|
InitPlan 1
|
||||||
-> Result
|
-> Result
|
||||||
-> Result
|
-> Result
|
||||||
(7 rows)
|
(8 rows)
|
||||||
|
|
||||||
-- Tests to ensure we don't push down the run condition when it's not valid to
|
-- Tests to ensure we don't push down the run condition when it's not valid to
|
||||||
-- do so.
|
-- do so.
|
||||||
@ -4247,14 +4274,15 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE c <= 3;
|
WHERE c <= 3;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
Filter: (emp.c <= 3)
|
Filter: (emp.c <= 3)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY empsalary.salary ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.salary DESC
|
Sort Key: empsalary.salary DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(6 rows)
|
(7 rows)
|
||||||
|
|
||||||
-- Ensure we don't push down when the window function's monotonic properties
|
-- Ensure we don't push down when the window function's monotonic properties
|
||||||
-- don't match that of the clauses.
|
-- don't match that of the clauses.
|
||||||
@ -4266,14 +4294,15 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE 3 <= c;
|
WHERE 3 <= c;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------
|
---------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
Filter: (3 <= emp.c)
|
Filter: (3 <= emp.c)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY empsalary.salary)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.salary
|
Sort Key: empsalary.salary
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(6 rows)
|
(7 rows)
|
||||||
|
|
||||||
-- Ensure we don't use a run condition when there's a volatile function in the
|
-- Ensure we don't use a run condition when there's a volatile function in the
|
||||||
-- WindowFunc
|
-- WindowFunc
|
||||||
@ -4285,14 +4314,15 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE c = 1;
|
WHERE c = 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------
|
--------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
Filter: (emp.c = 1)
|
Filter: (emp.c = 1)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY empsalary.empno)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.empno DESC
|
Sort Key: empsalary.empno DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(6 rows)
|
(7 rows)
|
||||||
|
|
||||||
-- Ensure we don't use a run condition when the WindowFunc contains subplans
|
-- Ensure we don't use a run condition when the WindowFunc contains subplans
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
@ -4303,16 +4333,17 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE c = 1;
|
WHERE c = 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------
|
--------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
Filter: (emp.c = 1)
|
Filter: (emp.c = 1)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY empsalary.empno)
|
||||||
InitPlan 1
|
InitPlan 1
|
||||||
-> Result
|
-> Result
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.empno DESC
|
Sort Key: empsalary.empno DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(8 rows)
|
(9 rows)
|
||||||
|
|
||||||
-- Test Sort node collapsing
|
-- Test Sort node collapsing
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
@ -4323,15 +4354,17 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE depname = 'sales';
|
WHERE depname = 'sales';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w2 AS (ORDER BY empsalary.empno)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY empsalary.empno ORDER BY empsalary.enroll_date)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.empno, empsalary.enroll_date
|
Sort Key: empsalary.empno, empsalary.enroll_date
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
Filter: ((depname)::text = 'sales'::text)
|
Filter: ((depname)::text = 'sales'::text)
|
||||||
(7 rows)
|
(9 rows)
|
||||||
|
|
||||||
-- Ensure that the evaluation order of the WindowAggs results in the WindowAgg
|
-- Ensure that the evaluation order of the WindowAggs results in the WindowAgg
|
||||||
-- with the same sort order that's required by the ORDER BY is evaluated last.
|
-- with the same sort order that's required by the ORDER BY is evaluated last.
|
||||||
@ -4344,16 +4377,18 @@ SELECT empno,
|
|||||||
FROM empsalary
|
FROM empsalary
|
||||||
ORDER BY depname, empno;
|
ORDER BY depname, empno;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w2 AS (PARTITION BY depname ORDER BY empno)
|
||||||
-> Incremental Sort
|
-> Incremental Sort
|
||||||
Sort Key: depname, empno
|
Sort Key: depname, empno
|
||||||
Presorted Key: depname
|
Presorted Key: depname
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY depname ORDER BY enroll_date)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: depname, enroll_date
|
Sort Key: depname, enroll_date
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(8 rows)
|
(10 rows)
|
||||||
|
|
||||||
-- As above, but with an adjusted ORDER BY to ensure the above plan didn't
|
-- As above, but with an adjusted ORDER BY to ensure the above plan didn't
|
||||||
-- perform only 2 sorts by accident.
|
-- perform only 2 sorts by accident.
|
||||||
@ -4366,16 +4401,18 @@ SELECT empno,
|
|||||||
FROM empsalary
|
FROM empsalary
|
||||||
ORDER BY depname, enroll_date;
|
ORDER BY depname, enroll_date;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------
|
-------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w2 AS (PARTITION BY depname ORDER BY enroll_date)
|
||||||
-> Incremental Sort
|
-> Incremental Sort
|
||||||
Sort Key: depname, enroll_date
|
Sort Key: depname, enroll_date
|
||||||
Presorted Key: depname
|
Presorted Key: depname
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY depname ORDER BY empno)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: depname, empno
|
Sort Key: depname, empno
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(8 rows)
|
(10 rows)
|
||||||
|
|
||||||
SET enable_hashagg TO off;
|
SET enable_hashagg TO off;
|
||||||
-- Ensure we don't get a sort for both DISTINCT and ORDER BY. We expect the
|
-- Ensure we don't get a sort for both DISTINCT and ORDER BY. We expect the
|
||||||
@ -4390,20 +4427,22 @@ SELECT DISTINCT
|
|||||||
FROM empsalary
|
FROM empsalary
|
||||||
ORDER BY depname, enroll_date;
|
ORDER BY depname, enroll_date;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------
|
||||||
Unique
|
Unique
|
||||||
-> Incremental Sort
|
-> Incremental Sort
|
||||||
Sort Key: depname, enroll_date, empno, (sum(salary) OVER (?)), (min(salary) OVER (?))
|
Sort Key: depname, enroll_date, empno, (sum(salary) OVER w1), (min(salary) OVER w2)
|
||||||
Presorted Key: depname, enroll_date
|
Presorted Key: depname, enroll_date
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w2 AS (PARTITION BY depname ORDER BY enroll_date)
|
||||||
-> Incremental Sort
|
-> Incremental Sort
|
||||||
Sort Key: depname, enroll_date
|
Sort Key: depname, enroll_date
|
||||||
Presorted Key: depname
|
Presorted Key: depname
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY depname ORDER BY empno)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: depname, empno
|
Sort Key: depname, empno
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(12 rows)
|
(14 rows)
|
||||||
|
|
||||||
-- As above but adjust the ORDER BY clause to help ensure the plan with the
|
-- As above but adjust the ORDER BY clause to help ensure the plan with the
|
||||||
-- minimum amount of sorting wasn't a fluke.
|
-- minimum amount of sorting wasn't a fluke.
|
||||||
@ -4417,20 +4456,22 @@ SELECT DISTINCT
|
|||||||
FROM empsalary
|
FROM empsalary
|
||||||
ORDER BY depname, empno;
|
ORDER BY depname, empno;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------
|
||||||
Unique
|
Unique
|
||||||
-> Incremental Sort
|
-> Incremental Sort
|
||||||
Sort Key: depname, empno, enroll_date, (sum(salary) OVER (?)), (min(salary) OVER (?))
|
Sort Key: depname, empno, enroll_date, (sum(salary) OVER w2), (min(salary) OVER w1)
|
||||||
Presorted Key: depname, empno
|
Presorted Key: depname, empno
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w2 AS (PARTITION BY depname ORDER BY empno)
|
||||||
-> Incremental Sort
|
-> Incremental Sort
|
||||||
Sort Key: depname, empno
|
Sort Key: depname, empno
|
||||||
Presorted Key: depname
|
Presorted Key: depname
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY depname ORDER BY enroll_date)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: depname, enroll_date
|
Sort Key: depname, enroll_date
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(12 rows)
|
(14 rows)
|
||||||
|
|
||||||
RESET enable_hashagg;
|
RESET enable_hashagg;
|
||||||
-- Test Sort node reordering
|
-- Test Sort node reordering
|
||||||
@ -4440,13 +4481,15 @@ SELECT
|
|||||||
lag(1) OVER (PARTITION BY depname ORDER BY salary,enroll_date,empno)
|
lag(1) OVER (PARTITION BY depname ORDER BY salary,enroll_date,empno)
|
||||||
FROM empsalary;
|
FROM empsalary;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
WindowAgg
|
WindowAgg
|
||||||
|
Window: w2 AS (PARTITION BY depname ORDER BY salary, enroll_date)
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY depname ORDER BY salary, enroll_date, empno)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: depname, salary, enroll_date, empno
|
Sort Key: depname, salary, enroll_date, empno
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(5 rows)
|
(7 rows)
|
||||||
|
|
||||||
-- Test incremental sorting
|
-- Test incremental sorting
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
@ -4460,18 +4503,20 @@ SELECT * FROM
|
|||||||
FROM empsalary) emp
|
FROM empsalary) emp
|
||||||
WHERE first_emp = 1 OR last_emp = 1;
|
WHERE first_emp = 1 OR last_emp = 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------
|
||||||
Subquery Scan on emp
|
Subquery Scan on emp
|
||||||
Filter: ((emp.first_emp = 1) OR (emp.last_emp = 1))
|
Filter: ((emp.first_emp = 1) OR (emp.last_emp = 1))
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w2 AS (PARTITION BY empsalary.depname ORDER BY empsalary.enroll_date ROWS UNBOUNDED PRECEDING)
|
||||||
-> Incremental Sort
|
-> Incremental Sort
|
||||||
Sort Key: empsalary.depname, empsalary.enroll_date
|
Sort Key: empsalary.depname, empsalary.enroll_date
|
||||||
Presorted Key: empsalary.depname
|
Presorted Key: empsalary.depname
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (PARTITION BY empsalary.depname ORDER BY empsalary.enroll_date ROWS UNBOUNDED PRECEDING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: empsalary.depname, empsalary.enroll_date DESC
|
Sort Key: empsalary.depname, empsalary.enroll_date DESC
|
||||||
-> Seq Scan on empsalary
|
-> Seq Scan on empsalary
|
||||||
(10 rows)
|
(12 rows)
|
||||||
|
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
(SELECT depname,
|
(SELECT depname,
|
||||||
@ -5299,11 +5344,12 @@ LIMIT 1;
|
|||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
Limit
|
Limit
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY t1.unique1)
|
||||||
-> Nested Loop
|
-> Nested Loop
|
||||||
-> Index Only Scan using tenk1_unique1 on tenk1 t1
|
-> Index Only Scan using tenk1_unique1 on tenk1 t1
|
||||||
-> Index Only Scan using tenk1_thous_tenthous on tenk1 t2
|
-> Index Only Scan using tenk1_thous_tenthous on tenk1 t2
|
||||||
Index Cond: (tenthous = t1.unique1)
|
Index Cond: (tenthous = t1.unique1)
|
||||||
(6 rows)
|
(7 rows)
|
||||||
|
|
||||||
-- Ensure we get a cheap total plan. Lack of ORDER BY in the WindowClause
|
-- Ensure we get a cheap total plan. Lack of ORDER BY in the WindowClause
|
||||||
-- means that all rows must be read from the join, so a cheap startup plan
|
-- means that all rows must be read from the join, so a cheap startup plan
|
||||||
@ -5317,13 +5363,14 @@ LIMIT 1;
|
|||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Limit
|
Limit
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS ()
|
||||||
-> Hash Join
|
-> Hash Join
|
||||||
Hash Cond: (t1.unique1 = t2.tenthous)
|
Hash Cond: (t1.unique1 = t2.tenthous)
|
||||||
-> Index Only Scan using tenk1_unique1 on tenk1 t1
|
-> Index Only Scan using tenk1_unique1 on tenk1 t1
|
||||||
-> Hash
|
-> Hash
|
||||||
-> Seq Scan on tenk1 t2
|
-> Seq Scan on tenk1 t2
|
||||||
Filter: (two = 1)
|
Filter: (two = 1)
|
||||||
(8 rows)
|
(9 rows)
|
||||||
|
|
||||||
-- Ensure we get a cheap total plan. This time use UNBOUNDED FOLLOWING, which
|
-- Ensure we get a cheap total plan. This time use UNBOUNDED FOLLOWING, which
|
||||||
-- needs to read all join rows to output the first WindowAgg row.
|
-- needs to read all join rows to output the first WindowAgg row.
|
||||||
@ -5332,16 +5379,17 @@ SELECT COUNT(*) OVER (ORDER BY t1.unique1 ROWS BETWEEN UNBOUNDED PRECEDING AND U
|
|||||||
FROM tenk1 t1 INNER JOIN tenk1 t2 ON t1.unique1 = t2.tenthous
|
FROM tenk1 t1 INNER JOIN tenk1 t2 ON t1.unique1 = t2.tenthous
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------
|
||||||
Limit
|
Limit
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY t1.unique1 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
|
||||||
-> Merge Join
|
-> Merge Join
|
||||||
Merge Cond: (t1.unique1 = t2.tenthous)
|
Merge Cond: (t1.unique1 = t2.tenthous)
|
||||||
-> Index Only Scan using tenk1_unique1 on tenk1 t1
|
-> Index Only Scan using tenk1_unique1 on tenk1 t1
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: t2.tenthous
|
Sort Key: t2.tenthous
|
||||||
-> Index Only Scan using tenk1_thous_tenthous on tenk1 t2
|
-> Index Only Scan using tenk1_thous_tenthous on tenk1 t2
|
||||||
(8 rows)
|
(9 rows)
|
||||||
|
|
||||||
-- Ensure we get a cheap total plan. This time use 10000 FOLLOWING so we need
|
-- Ensure we get a cheap total plan. This time use 10000 FOLLOWING so we need
|
||||||
-- to read all join rows.
|
-- to read all join rows.
|
||||||
@ -5350,16 +5398,17 @@ SELECT COUNT(*) OVER (ORDER BY t1.unique1 ROWS BETWEEN UNBOUNDED PRECEDING AND 1
|
|||||||
FROM tenk1 t1 INNER JOIN tenk1 t2 ON t1.unique1 = t2.tenthous
|
FROM tenk1 t1 INNER JOIN tenk1 t2 ON t1.unique1 = t2.tenthous
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------
|
||||||
Limit
|
Limit
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w1 AS (ORDER BY t1.unique1 ROWS BETWEEN UNBOUNDED PRECEDING AND '10000'::bigint FOLLOWING)
|
||||||
-> Merge Join
|
-> Merge Join
|
||||||
Merge Cond: (t1.unique1 = t2.tenthous)
|
Merge Cond: (t1.unique1 = t2.tenthous)
|
||||||
-> Index Only Scan using tenk1_unique1 on tenk1 t1
|
-> Index Only Scan using tenk1_unique1 on tenk1 t1
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: t2.tenthous
|
Sort Key: t2.tenthous
|
||||||
-> Index Only Scan using tenk1_thous_tenthous on tenk1 t2
|
-> Index Only Scan using tenk1_thous_tenthous on tenk1 t2
|
||||||
(8 rows)
|
(9 rows)
|
||||||
|
|
||||||
-- Tests for problems with failure to walk or mutate expressions
|
-- Tests for problems with failure to walk or mutate expressions
|
||||||
-- within window frame clauses.
|
-- within window frame clauses.
|
||||||
@ -5385,13 +5434,14 @@ AS $$
|
|||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
EXPLAIN (costs off) SELECT * FROM pg_temp.f(2);
|
EXPLAIN (costs off) SELECT * FROM pg_temp.f(2);
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------
|
----------------------------------------------------------------------------------------
|
||||||
Subquery Scan on f
|
Subquery Scan on f
|
||||||
-> WindowAgg
|
-> WindowAgg
|
||||||
|
Window: w AS (ORDER BY s.s ROWS BETWEEN CURRENT ROW AND '2'::bigint FOLLOWING)
|
||||||
-> Sort
|
-> Sort
|
||||||
Sort Key: s.s
|
Sort Key: s.s
|
||||||
-> Function Scan on generate_series s
|
-> Function Scan on generate_series s
|
||||||
(5 rows)
|
(6 rows)
|
||||||
|
|
||||||
SELECT * FROM pg_temp.f(2);
|
SELECT * FROM pg_temp.f(2);
|
||||||
f
|
f
|
||||||
|
@ -70,6 +70,11 @@ select explain_filter('explain (analyze, serialize, buffers, format yaml) select
|
|||||||
select explain_filter('explain (buffers, format text) select * from int8_tbl i8');
|
select explain_filter('explain (buffers, format text) select * from int8_tbl i8');
|
||||||
select explain_filter('explain (buffers, format json) select * from int8_tbl i8');
|
select explain_filter('explain (buffers, format json) select * from int8_tbl i8');
|
||||||
|
|
||||||
|
-- Check expansion of window definitions
|
||||||
|
|
||||||
|
select explain_filter('explain verbose select sum(unique1) over w, sum(unique2) over (w order by hundred), sum(tenthous) over (w order by hundred) from tenk1 window w as (partition by ten)');
|
||||||
|
select explain_filter('explain verbose select sum(unique1) over w1, sum(unique2) over (w1 order by hundred), sum(tenthous) over (w1 order by hundred rows 10 preceding) from tenk1 window w1 as (partition by ten)');
|
||||||
|
|
||||||
-- Check output including I/O timings. These fields are conditional
|
-- Check output including I/O timings. These fields are conditional
|
||||||
-- but always set in JSON format, so check them only in this case.
|
-- but always set in JSON format, so check them only in this case.
|
||||||
set track_io_timing = on;
|
set track_io_timing = on;
|
||||||
|
Reference in New Issue
Block a user