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
Message: Re: BUG #18305: Unexpected error: "WindowFunc not found in subplan target lists" triggered by subqueries

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