v2-0002-Re-instate-ntile-var-and-count-var-run-condition-.patch
text/plain
Filename: v2-0002-Re-instate-ntile-var-and-count-var-run-condition-.patch
Type: text/plain
Part: 1
Patch
Format: format-patch
Series: patch v2-0002
Subject: Re-instate ntile(var) and count(var) run condition optimization
| File | + | − |
|---|---|---|
| src/backend/nodes/nodeFuncs.c | 20 | 6 |
| src/backend/optimizer/path/allpaths.c | 12 | 24 |
| src/backend/optimizer/plan/createplan.c | 1 | 1 |
| src/backend/optimizer/plan/planner.c | 44 | 10 |
| src/backend/optimizer/prep/prepjointree.c | 0 | 8 |
| src/backend/optimizer/util/clauses.c | 1 | 0 |
| src/backend/optimizer/util/pathnode.c | 3 | 0 |
| src/backend/parser/parse_clause.c | 0 | 1 |
| src/backend/parser/parse_expr.c | 1 | 0 |
| src/backend/parser/parse_func.c | 1 | 0 |
| src/backend/utils/adt/int8.c | 0 | 9 |
| src/backend/utils/adt/windowfuncs.c | 6 | 18 |
| src/include/nodes/parsenodes.h | 0 | 2 |
| src/include/nodes/pathnodes.h | 1 | 0 |
| src/include/nodes/primnodes.h | 30 | 0 |
| src/include/optimizer/pathnode.h | 1 | 0 |
| src/test/regress/expected/window.out | 60 | 52 |
| src/test/regress/sql/window.sql | 25 | 26 |
| src/tools/pgindent/typedefs.list | 1 | 0 |
From 761e6f3f2fe0ee6dff68adf0abd22c58592ac57f Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Wed, 24 Apr 2024 12:49:08 +1200
Subject: [PATCH v2 2/2] Re-instate ntile(var) and count(var) run condition
optimization
Remember cat version bump
---
src/backend/nodes/nodeFuncs.c | 26 +++--
src/backend/optimizer/path/allpaths.c | 36 +++----
src/backend/optimizer/plan/createplan.c | 2 +-
src/backend/optimizer/plan/planner.c | 54 +++++++++--
src/backend/optimizer/prep/prepjointree.c | 8 --
src/backend/optimizer/util/clauses.c | 1 +
src/backend/optimizer/util/pathnode.c | 3 +
src/backend/parser/parse_clause.c | 1 -
src/backend/parser/parse_expr.c | 1 +
src/backend/parser/parse_func.c | 1 +
src/backend/utils/adt/int8.c | 9 --
src/backend/utils/adt/windowfuncs.c | 24 ++---
src/include/nodes/parsenodes.h | 2 -
src/include/nodes/pathnodes.h | 1 +
src/include/nodes/primnodes.h | 30 ++++++
src/include/optimizer/pathnode.h | 1 +
src/test/regress/expected/window.out | 112 ++++++++++++----------
src/test/regress/sql/window.sql | 51 +++++-----
src/tools/pgindent/typedefs.list | 1 +
19 files changed, 207 insertions(+), 157 deletions(-)
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index e1df1894b6..3db4a42dd7 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -2163,6 +2163,16 @@ expression_tree_walker_impl(Node *node,
return true;
if (WALK(expr->aggfilter))
return true;
+ if (WALK(expr->runCondition))
+ return true;
+ }
+ break;
+ case T_WindowFuncRunCondition:
+ {
+ WindowFuncRunCondition *expr = (WindowFuncRunCondition *) node;
+
+ if (WALK(expr->arg))
+ return true;
}
break;
case T_SubscriptingRef:
@@ -2400,8 +2410,6 @@ expression_tree_walker_impl(Node *node,
return true;
if (WALK(wc->endOffset))
return true;
- if (WALK(wc->runCondition))
- return true;
}
break;
case T_CTECycleClause:
@@ -2752,8 +2760,6 @@ query_tree_walker_impl(Query *query,
return true;
if (WALK(wc->endOffset))
return true;
- if (WALK(wc->runCondition))
- return true;
}
}
@@ -3053,6 +3059,16 @@ expression_tree_mutator_impl(Node *node,
return (Node *) newnode;
}
break;
+ case T_WindowFuncRunCondition:
+ {
+ WindowFuncRunCondition *expr = (WindowFuncRunCondition *) node;
+ WindowFuncRunCondition *newnode;
+
+ FLATCOPY(newnode, expr, WindowFuncRunCondition);
+ MUTATE(newnode->arg, expr->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
case T_SubscriptingRef:
{
SubscriptingRef *sbsref = (SubscriptingRef *) node;
@@ -3466,7 +3482,6 @@ expression_tree_mutator_impl(Node *node,
MUTATE(newnode->orderClause, wc->orderClause, List *);
MUTATE(newnode->startOffset, wc->startOffset, Node *);
MUTATE(newnode->endOffset, wc->endOffset, Node *);
- MUTATE(newnode->runCondition, wc->runCondition, List *);
return (Node *) newnode;
}
break;
@@ -3799,7 +3814,6 @@ query_tree_mutator_impl(Query *query,
FLATCOPY(newnode, wc, WindowClause);
MUTATE(newnode->startOffset, wc->startOffset, Node *);
MUTATE(newnode->endOffset, wc->endOffset, Node *);
- MUTATE(newnode->runCondition, wc->runCondition, List *);
resultlist = lappend(resultlist, (Node *) newnode);
}
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index cc51ae1757..f21ae3ecda 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2205,7 +2205,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
* the run condition will handle all of the required filtering.
*
* Returns true if 'opexpr' was found to be useful and was added to the
- * WindowClauses runCondition. We also set *keep_original accordingly and add
+ * WindowFunc's runCondition. We also set *keep_original accordingly and add
* 'attno' to *run_cond_attrs offset by FirstLowInvalidHeapAttributeNumber.
* If the 'opexpr' cannot be used then we set *keep_original to true and
* return false.
@@ -2358,7 +2358,7 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
*keep_original = true;
runopexpr = opexpr;
- /* determine the operator to use for the runCondition qual */
+ /* determine the operator to use for the WindowFuncRunCondition */
runoperator = get_opfamily_member(opinfo->opfamily_id,
opinfo->oplefttype,
opinfo->oprighttype,
@@ -2369,27 +2369,15 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
if (runopexpr != NULL)
{
- Expr *newexpr;
+ WindowFuncRunCondition *runcond;
- /*
- * Build the qual required for the run condition keeping the
- * WindowFunc on the same side as it was originally.
- */
- if (wfunc_left)
- newexpr = make_opclause(runoperator,
- runopexpr->opresulttype,
- runopexpr->opretset, (Expr *) wfunc,
- otherexpr, runopexpr->opcollid,
- runopexpr->inputcollid);
- else
- newexpr = make_opclause(runoperator,
- runopexpr->opresulttype,
- runopexpr->opretset,
- otherexpr, (Expr *) wfunc,
- runopexpr->opcollid,
- runopexpr->inputcollid);
+ runcond = makeNode(WindowFuncRunCondition);
+ runcond->opno = runoperator;
+ runcond->inputcollid = runopexpr->inputcollid;
+ runcond->wfunc_left = wfunc_left;
+ runcond->arg = copyObject(otherexpr);
- wclause->runCondition = lappend(wclause->runCondition, newexpr);
+ wfunc->runCondition = lappend(wfunc->runCondition, runcond);
/* record that this attno was used in a run condition */
*run_cond_attrs = bms_add_member(*run_cond_attrs,
@@ -2403,9 +2391,9 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
/*
* check_and_push_window_quals
- * Check if 'clause' is a qual that can be pushed into a WindowFunc's
- * WindowClause as a 'runCondition' qual. These, when present, allow
- * some unnecessary work to be skipped during execution.
+ * Check if 'clause' is a qual that can be pushed into a WindowFunc
+ * as a 'runCondition' qual. These, when present, allow some unnecessary
+ * work to be skipped during execution.
*
* 'run_cond_attrs' will be populated with all targetlist resnos of subquery
* targets (offset by FirstLowInvalidHeapAttributeNumber) that we pushed
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 3b77886567..6b64c4a362 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2699,7 +2699,7 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
wc->inRangeColl,
wc->inRangeAsc,
wc->inRangeNullsFirst,
- wc->runCondition,
+ best_path->runCondition,
best_path->qual,
best_path->topwindow,
subplan);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5320da51a0..b149b5690d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -870,9 +870,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
EXPRKIND_LIMIT);
wc->endOffset = preprocess_expression(root, wc->endOffset,
EXPRKIND_LIMIT);
- wc->runCondition = (List *) preprocess_expression(root,
- (Node *) wc->runCondition,
- EXPRKIND_TARGET);
}
parse->limitOffset = preprocess_expression(root, parse->limitOffset,
@@ -4527,9 +4524,11 @@ create_one_window_path(PlannerInfo *root,
{
WindowClause *wc = lfirst_node(WindowClause, l);
List *window_pathkeys;
+ List *runcondition = NIL;
int presorted_keys;
bool is_sorted;
bool topwindow;
+ ListCell *lc2;
window_pathkeys = make_pathkeys_for_window(root,
wc,
@@ -4577,7 +4576,6 @@ create_one_window_path(PlannerInfo *root,
* we do need to account for the increase in tlist width.
*/
int64 tuple_width = window_target->width;
- ListCell *lc2;
window_target = copy_pathtarget(window_target);
foreach(lc2, wflists->windowFuncs[wc->winref])
@@ -4599,17 +4597,53 @@ create_one_window_path(PlannerInfo *root,
topwindow = foreach_current_index(l) == list_length(activeWindows) - 1;
/*
- * Accumulate all of the runConditions from each intermediate
- * WindowClause. The top-level WindowAgg must pass these as a qual so
- * that it filters out unwanted tuples correctly.
+ * Collect the WindowFuncRunConditions from each WindowFunc and
+ * convert them into OpExprs
*/
- if (!topwindow)
- topqual = list_concat(topqual, wc->runCondition);
+ foreach(lc2, wflists->windowFuncs[wc->winref])
+ {
+ ListCell *lc3;
+ WindowFunc *wfunc = lfirst_node(WindowFunc, lc2);
+
+ foreach(lc3, wfunc->runCondition)
+ {
+ WindowFuncRunCondition *rc =
+ lfirst_node(WindowFuncRunCondition, lc3);
+ Expr *opexpr;
+ Expr *leftop;
+ Expr *rightop;
+
+ if (rc->wfunc_left)
+ {
+ leftop = (Expr *) copyObject(wfunc);
+ rightop = copyObject(rc->arg);
+ }
+ else
+ {
+ leftop = copyObject(rc->arg);
+ rightop = (Expr *) copyObject(wfunc);
+ }
+
+ opexpr = make_opclause(rc->opno,
+ BOOLOID,
+ false,
+ leftop,
+ rightop,
+ InvalidOid,
+ rc->inputcollid);
+
+ runcondition = lappend(runcondition, opexpr);
+
+ if (!topwindow)
+ topqual = lappend(topqual, opexpr);
+ }
+ }
path = (Path *)
create_windowagg_path(root, window_rel, path, window_target,
wflists->windowFuncs[wc->winref],
- wc, topwindow ? topqual : NIL, topwindow);
+ runcondition, wc,
+ topwindow ? topqual : NIL, topwindow);
}
add_path(window_rel, path);
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 41da670f15..5482ab85a7 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -2175,14 +2175,6 @@ perform_pullup_replace_vars(PlannerInfo *root,
parse->returningList = (List *)
pullup_replace_vars((Node *) parse->returningList, rvcontext);
- foreach(lc, parse->windowClause)
- {
- WindowClause *wc = lfirst_node(WindowClause, lc);
-
- if (wc->runCondition != NIL)
- wc->runCondition = (List *)
- pullup_replace_vars((Node *) wc->runCondition, rvcontext);
- }
if (parse->onConflict)
{
parse->onConflict->onConflictSet = (List *)
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 59487cbd79..b4e085e9d4 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2566,6 +2566,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->inputcollid = expr->inputcollid;
newexpr->args = args;
newexpr->aggfilter = aggfilter;
+ newexpr->runCondition = expr->runCondition;
newexpr->winref = expr->winref;
newexpr->winstar = expr->winstar;
newexpr->winagg = expr->winagg;
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 3cf1dac087..3491c3af1c 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -3471,6 +3471,7 @@ create_minmaxagg_path(PlannerInfo *root,
* 'subpath' is the path representing the source of data
* 'target' is the PathTarget to be computed
* 'windowFuncs' is a list of WindowFunc structs
+ * 'runCondition' is a list of OpExprs to short-circuit WindowAgg execution
* 'winclause' is a WindowClause that is common to all the WindowFuncs
* 'qual' WindowClause.runconditions from lower-level WindowAggPaths.
* Must always be NIL when topwindow == false
@@ -3486,6 +3487,7 @@ create_windowagg_path(PlannerInfo *root,
Path *subpath,
PathTarget *target,
List *windowFuncs,
+ List *runCondition,
WindowClause *winclause,
List *qual,
bool topwindow)
@@ -3510,6 +3512,7 @@ create_windowagg_path(PlannerInfo *root,
pathnode->subpath = subpath;
pathnode->winclause = winclause;
pathnode->qual = qual;
+ pathnode->runCondition = runCondition;
pathnode->topwindow = topwindow;
/*
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 4fc5fc87e0..8118036495 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -2956,7 +2956,6 @@ transformWindowDefinitions(ParseState *pstate,
rangeopfamily, rangeopcintype,
&wc->endInRangeFunc,
windef->endOffset);
- wc->runCondition = NIL;
wc->winref = winref;
result = lappend(result, wc);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 1c1c86aa3e..aba3546ed1 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -3826,6 +3826,7 @@ transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
/* wincollid and inputcollid will be set by parse_collate.c */
wfunc->args = args;
wfunc->aggfilter = aggfilter;
+ wfunc->runCondition = NIL;
/* winref will be set by transformWindowFuncCall */
wfunc->winstar = false;
wfunc->winagg = true;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 0cbc950c95..9b23344a3b 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -834,6 +834,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
wfunc->winstar = agg_star;
wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
wfunc->aggfilter = agg_filter;
+ wfunc->runCondition = NIL;
wfunc->location = location;
/*
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index e7b1d389b3..54fa3bc379 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -832,15 +832,6 @@ int8inc_support(PG_FUNCTION_ARGS)
SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
MonotonicFunction monotonic = MONOTONICFUNC_NONE;
int frameOptions = req->window_clause->frameOptions;
- WindowFunc *wfunc = req->window_func;
-
- if (list_length(wfunc->args) == 1)
- {
- Node *expr = eval_const_expressions(NULL, linitial(wfunc->args));
-
- if (!IsA(expr, Const))
- PG_RETURN_POINTER(NULL);
- }
/* No ORDER BY clause then all rows are peers */
if (req->window_clause->orderClause == NIL)
diff --git a/src/backend/utils/adt/windowfuncs.c b/src/backend/utils/adt/windowfuncs.c
index b49e48109d..473c61569f 100644
--- a/src/backend/utils/adt/windowfuncs.c
+++ b/src/backend/utils/adt/windowfuncs.c
@@ -16,7 +16,6 @@
#include "nodes/parsenodes.h"
#include "nodes/supportnodes.h"
#include "utils/fmgrprotos.h"
-#include "optimizer/optimizer.h"
#include "windowapi.h"
/*
@@ -488,24 +487,13 @@ window_ntile_support(PG_FUNCTION_ARGS)
if (IsA(rawreq, SupportRequestWFuncMonotonic))
{
SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
- WindowFunc *wfunc = req->window_func;
- if (list_length(wfunc->args) == 1)
- {
- Node *expr = eval_const_expressions(NULL, linitial(wfunc->args));
-
- if (IsA(expr, Const))
- {
- /*
- * ntile() is monotonically increasing as the number of
- * buckets cannot change after the first call
- */
- req->monotonic = MONOTONICFUNC_INCREASING;
- PG_RETURN_POINTER(req);
- }
- }
-
- PG_RETURN_POINTER(NULL);
+ /*
+ * ntile() is monotonically increasing as the number of buckets cannot
+ * change after the first call
+ */
+ req->monotonic = MONOTONICFUNC_INCREASING;
+ PG_RETURN_POINTER(req);
}
if (IsA(rawreq, SupportRequestOptimizeWindowClause))
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index c5f34efe27..28d969f47a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1549,8 +1549,6 @@ typedef struct WindowClause
int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
- /* qual to help short-circuit execution */
- List *runCondition pg_node_attr(query_jumble_ignore);
/* in_range function for startOffset */
Oid startInRangeFunc pg_node_attr(query_jumble_ignore);
/* in_range function for endOffset */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b8141f141a..99bd371e0a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2308,6 +2308,7 @@ typedef struct WindowAggPath
Path *subpath; /* path representing input source */
WindowClause *winclause; /* WindowClause we'll be using */
List *qual; /* lower-level WindowAgg runconditions */
+ List *runCondition; /* OpExpr List to short-circuit execution */
bool topwindow; /* false for all apart from the WindowAgg
* that's closest to the root of the plan */
} WindowAggPath;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 247cecb4b4..1ea5b65f53 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -575,6 +575,8 @@ typedef struct WindowFunc
List *args;
/* FILTER expression, if any */
Expr *aggfilter;
+ /* List of WindowFuncRunConditions to help short-circuit execution */
+ List *runCondition pg_node_attr(query_jumble_ignore);
/* index of associated WindowClause */
Index winref;
/* true if argument list was really '*' */
@@ -585,6 +587,34 @@ typedef struct WindowFunc
ParseLoc location;
} WindowFunc;
+/*
+ * WindowFuncRunCondition
+ *
+ * Represents intermediate OpExprs which will be used by WindowAgg to
+ * short-circuit execution.
+ */
+typedef struct WindowFuncRunCondition
+{
+ Expr xpr;
+
+ /* PG_OPERATOR OID of the operator */
+ Oid opno;
+ /* OID of collation that operator should use */
+ Oid inputcollid pg_node_attr(query_jumble_ignore);
+
+ /*
+ * true of WindowFunc belongs on the left of the resulting OpExpr or false
+ * if the WindowFunc is on the right.
+ */
+ bool wfunc_left;
+
+ /*
+ * The Expr being compared to the WindowFunc to use in the OpExpr in the
+ * WindowAgg's runCondition
+ */
+ Expr *arg;
+} WindowFuncRunCondition;
+
/*
* MergeSupportFunc
*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index c5c4756b0f..112e7c23d4 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -250,6 +250,7 @@ extern WindowAggPath *create_windowagg_path(PlannerInfo *root,
Path *subpath,
PathTarget *target,
List *windowFuncs,
+ List *runCondition,
WindowClause *winclause,
List *qual,
bool topwindow);
diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out
index 2f2cb112b1..ae4e8851f8 100644
--- a/src/test/regress/expected/window.out
+++ b/src/test/regress/expected/window.out
@@ -3954,13 +3954,13 @@ EXPLAIN (COSTS OFF)
SELECT * FROM
(SELECT empno,
salary,
- count(1) OVER (ORDER BY salary DESC) c
+ count(empno) OVER (ORDER BY salary DESC) c
FROM empsalary) emp
WHERE c <= 3;
- QUERY PLAN
--------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------
WindowAgg
- Run Condition: (count(1) OVER (?) <= 3)
+ Run Condition: (count(empsalary.empno) OVER (?) <= 3)
-> Sort
Sort Key: empsalary.salary DESC
-> Seq Scan on empsalary
@@ -3969,7 +3969,7 @@ WHERE c <= 3;
SELECT * FROM
(SELECT empno,
salary,
- count(1) OVER (ORDER BY salary DESC) c
+ count(empno) OVER (ORDER BY salary DESC) c
FROM empsalary) emp
WHERE c <= 3;
empno | salary | c
@@ -4081,19 +4081,19 @@ WHERE rn < 3;
-> Seq Scan on empsalary
(6 rows)
--- likewise with count(1) instead of row_number()
+-- likewise with count(empno) instead of row_number()
EXPLAIN (COSTS OFF)
SELECT * FROM
(SELECT empno,
depname,
salary,
- count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
+ count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
FROM empsalary) emp
WHERE c <= 3;
QUERY PLAN
------------------------------------------------------------
WindowAgg
- Run Condition: (count(1) OVER (?) <= 3)
+ Run Condition: (count(empsalary.empno) OVER (?) <= 3)
-> Sort
Sort Key: empsalary.depname, empsalary.salary DESC
-> Seq Scan on empsalary
@@ -4104,7 +4104,7 @@ SELECT * FROM
(SELECT empno,
depname,
salary,
- count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
+ count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
FROM empsalary) emp
WHERE c <= 3;
empno | depname | salary | c
@@ -4126,13 +4126,13 @@ SELECT * FROM
(SELECT empno,
depname,
salary,
- count(1) OVER () c
+ count(empno) OVER () c
FROM empsalary) emp
WHERE c = 1;
- QUERY PLAN
-------------------------------------------
+ QUERY PLAN
+--------------------------------------------------------
WindowAgg
- Run Condition: (count(1) OVER (?) = 1)
+ Run Condition: (count(empsalary.empno) OVER (?) = 1)
-> Seq Scan on empsalary
(3 rows)
@@ -4140,7 +4140,7 @@ WHERE c = 1;
EXPLAIN (COSTS OFF)
SELECT * FROM
(SELECT *,
- count(1) OVER (PARTITION BY depname || '') c1, -- w1
+ count(salary) OVER (PARTITION BY depname || '') c1, -- w1
row_number() OVER (PARTITION BY depname) rn, -- w2
count(*) OVER (PARTITION BY depname) c2, -- w2
count(*) OVER (PARTITION BY '' || depname) c3, -- w3
@@ -4152,7 +4152,7 @@ SELECT * FROM
Subquery Scan on e
-> WindowAgg
Filter: (((row_number() OVER (?)) <= 1) AND ((ntile(2) OVER (?)) < 2))
- Run Condition: (count(1) OVER (?) <= 3)
+ Run Condition: (count(empsalary.salary) OVER (?) <= 3)
-> Sort
Sort Key: (((empsalary.depname)::text || ''::text))
-> WindowAgg
@@ -4168,7 +4168,7 @@ SELECT * FROM
-- Ensure we correctly filter out all of the run conditions from each window
SELECT * FROM
(SELECT *,
- count(1) OVER (PARTITION BY depname || '') c1, -- w1
+ count(salary) OVER (PARTITION BY depname || '') c1, -- w1
row_number() OVER (PARTITION BY depname) rn, -- w2
count(*) OVER (PARTITION BY depname) c2, -- w2
count(*) OVER (PARTITION BY '' || depname) c3, -- w3
@@ -4181,6 +4181,50 @@ SELECT * FROM
sales | 3 | 4800 | 08-01-2007 | 3 | 1 | 3 | 3 | 1
(2 rows)
+-- Ensure we remove references to reduced outer joins as nulling rels in run
+-- conditions
+EXPLAIN (COSTS OFF)
+SELECT 1 FROM
+ (SELECT ntile(e2.salary) OVER (PARTITION BY e1.depname) AS c
+ FROM empsalary e1 LEFT JOIN empsalary e2 ON TRUE
+ WHERE e1.empno = e2.empno) s
+WHERE s.c = 1;
+ QUERY PLAN
+---------------------------------------------------------
+ Subquery Scan on s
+ Filter: (s.c = 1)
+ -> WindowAgg
+ Run Condition: (ntile(e2.salary) OVER (?) <= 1)
+ -> Sort
+ Sort Key: e1.depname
+ -> Merge Join
+ Merge Cond: (e1.empno = e2.empno)
+ -> Sort
+ Sort Key: e1.empno
+ -> Seq Scan on empsalary e1
+ -> Sort
+ Sort Key: e2.empno
+ -> Seq Scan on empsalary e2
+(14 rows)
+
+-- Ensure the run condition optimization is used in cases where the WindowFunc
+-- has a Var from another query level
+EXPLAIN (COSTS OFF)
+SELECT 1 FROM
+ (SELECT ntile(s1.x) OVER () AS c
+ FROM (SELECT (SELECT 1) AS x) AS s1) s
+WHERE s.c = 1;
+ QUERY PLAN
+-----------------------------------------------------------------
+ Subquery Scan on s
+ Filter: (s.c = 1)
+ -> WindowAgg
+ Run Condition: (ntile((InitPlan 1).col1) OVER (?) <= 1)
+ InitPlan 1
+ -> Result
+ -> Result
+(7 rows)
+
-- Tests to ensure we don't push down the run condition when it's not valid to
-- do so.
-- Ensure we don't push down when the frame options show that the window
@@ -4240,42 +4284,6 @@ WHERE c = 1;
-> Seq Scan on empsalary
(6 rows)
--- Ensure we don't use a run condition when the WindowFunc arg contains a Var
-EXPLAIN (COSTS OFF)
-SELECT * FROM
- (SELECT empno,
- salary,
- count(empno) OVER (ORDER BY empno DESC) c
- FROM empsalary) emp
-WHERE c = 1;
- QUERY PLAN
-----------------------------------------------
- Subquery Scan on emp
- Filter: (emp.c = 1)
- -> WindowAgg
- -> Sort
- Sort Key: empsalary.empno DESC
- -> Seq Scan on empsalary
-(6 rows)
-
--- As above but with ntile().
-EXPLAIN (COSTS OFF)
-SELECT * FROM
- (SELECT empno,
- salary,
- ntile(empno::int) OVER (ORDER BY empno DESC) nt
- FROM empsalary) emp
-WHERE nt = 1;
- QUERY PLAN
-----------------------------------------------
- Subquery Scan on emp
- Filter: (emp.nt = 1)
- -> WindowAgg
- -> Sort
- Sort Key: empsalary.empno DESC
- -> Seq Scan on empsalary
-(6 rows)
-
-- Ensure we don't use a run condition when the WindowFunc contains subplans
EXPLAIN (COSTS OFF)
SELECT * FROM
diff --git a/src/test/regress/sql/window.sql b/src/test/regress/sql/window.sql
index 284f47b514..6de5493b05 100644
--- a/src/test/regress/sql/window.sql
+++ b/src/test/regress/sql/window.sql
@@ -1252,14 +1252,14 @@ EXPLAIN (COSTS OFF)
SELECT * FROM
(SELECT empno,
salary,
- count(1) OVER (ORDER BY salary DESC) c
+ count(empno) OVER (ORDER BY salary DESC) c
FROM empsalary) emp
WHERE c <= 3;
SELECT * FROM
(SELECT empno,
salary,
- count(1) OVER (ORDER BY salary DESC) c
+ count(empno) OVER (ORDER BY salary DESC) c
FROM empsalary) emp
WHERE c <= 3;
@@ -1315,13 +1315,13 @@ SELECT empno, depname FROM
FROM empsalary) emp
WHERE rn < 3;
--- likewise with count(1) instead of row_number()
+-- likewise with count(empno) instead of row_number()
EXPLAIN (COSTS OFF)
SELECT * FROM
(SELECT empno,
depname,
salary,
- count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
+ count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
FROM empsalary) emp
WHERE c <= 3;
@@ -1330,7 +1330,7 @@ SELECT * FROM
(SELECT empno,
depname,
salary,
- count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
+ count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
FROM empsalary) emp
WHERE c <= 3;
@@ -1341,7 +1341,7 @@ SELECT * FROM
(SELECT empno,
depname,
salary,
- count(1) OVER () c
+ count(empno) OVER () c
FROM empsalary) emp
WHERE c = 1;
@@ -1349,7 +1349,7 @@ WHERE c = 1;
EXPLAIN (COSTS OFF)
SELECT * FROM
(SELECT *,
- count(1) OVER (PARTITION BY depname || '') c1, -- w1
+ count(salary) OVER (PARTITION BY depname || '') c1, -- w1
row_number() OVER (PARTITION BY depname) rn, -- w2
count(*) OVER (PARTITION BY depname) c2, -- w2
count(*) OVER (PARTITION BY '' || depname) c3, -- w3
@@ -1360,7 +1360,7 @@ SELECT * FROM
-- Ensure we correctly filter out all of the run conditions from each window
SELECT * FROM
(SELECT *,
- count(1) OVER (PARTITION BY depname || '') c1, -- w1
+ count(salary) OVER (PARTITION BY depname || '') c1, -- w1
row_number() OVER (PARTITION BY depname) rn, -- w2
count(*) OVER (PARTITION BY depname) c2, -- w2
count(*) OVER (PARTITION BY '' || depname) c3, -- w3
@@ -1368,6 +1368,23 @@ SELECT * FROM
FROM empsalary
) e WHERE rn <= 1 AND c1 <= 3 AND nt < 2;
+-- Ensure we remove references to reduced outer joins as nulling rels in run
+-- conditions
+EXPLAIN (COSTS OFF)
+SELECT 1 FROM
+ (SELECT ntile(e2.salary) OVER (PARTITION BY e1.depname) AS c
+ FROM empsalary e1 LEFT JOIN empsalary e2 ON TRUE
+ WHERE e1.empno = e2.empno) s
+WHERE s.c = 1;
+
+-- Ensure the run condition optimization is used in cases where the WindowFunc
+-- has a Var from another query level
+EXPLAIN (COSTS OFF)
+SELECT 1 FROM
+ (SELECT ntile(s1.x) OVER () AS c
+ FROM (SELECT (SELECT 1) AS x) AS s1) s
+WHERE s.c = 1;
+
-- Tests to ensure we don't push down the run condition when it's not valid to
-- do so.
@@ -1401,24 +1418,6 @@ SELECT * FROM
FROM empsalary) emp
WHERE c = 1;
--- Ensure we don't use a run condition when the WindowFunc arg contains a Var
-EXPLAIN (COSTS OFF)
-SELECT * FROM
- (SELECT empno,
- salary,
- count(empno) OVER (ORDER BY empno DESC) c
- FROM empsalary) emp
-WHERE c = 1;
-
--- As above but with ntile().
-EXPLAIN (COSTS OFF)
-SELECT * FROM
- (SELECT empno,
- salary,
- ntile(empno::int) OVER (ORDER BY empno DESC) nt
- FROM empsalary) emp
-WHERE nt = 1;
-
-- Ensure we don't use a run condition when the WindowFunc contains subplans
EXPLAIN (COSTS OFF)
SELECT * FROM
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index d551ada325..f32a4cc272 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3121,6 +3121,7 @@ WindowDef
WindowFunc
WindowFuncExprState
WindowFuncLists
+WindowFuncRunCondition
WindowObject
WindowObjectData
WindowStatePerAgg
--
2.40.1