Skip to content

Commit 9d36b88

Browse files
committed
Disable run condition optimization for some WindowFuncs
94985c2 added code to detect when WindowFuncs were monotonic and allowed additional quals to be "pushed down" into the subquery to be used as WindowClause runConditions in order to short-circuit execution in nodeWindowAgg.c. The Node representation of runConditions wasn't well selected and because we do qual pushdown before planning the subquery, the planning of the subquery could perform subquery pull-up of nested subqueries. For WindowFuncs with args, the arguments could be changed after pushing the qual down to the subquery. This was made more difficult by the fact that the code duplicated the WindowFunc inside an OpExpr to include in the WindowClauses runCondition field. This could result in duplication of subqueries and a pull-up of such a subquery could result in another initplan parameter being issued for the 2nd version of the subplan. This could result in errors such as: ERROR: WindowFunc not found in subplan target lists Here in the backbranches, we don't have the flexibility to improve the Node representation to resolve this, so instead we just disable the runCondition optimization for ntile() unless the argument is a Const, (v16 only) and likewise for count(expr) (both v15 and v16). count(*) is unaffected. All other window functions which support this optimization all take zero arguments and therefore are unaffected. Bug: #18170 Reported-by: Zuming Jiang Discussion: https://postgr.es/m/[email protected] Backpatch-through 15 (master will be fixed independently)
1 parent f199436 commit 9d36b88

File tree

4 files changed

+116
-65
lines changed

4 files changed

+116
-65
lines changed

src/backend/utils/adt/int8.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,21 @@ int8inc_support(PG_FUNCTION_ARGS)
833833
SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
834834
MonotonicFunction monotonic = MONOTONICFUNC_NONE;
835835
int frameOptions = req->window_clause->frameOptions;
836+
WindowFunc *wfunc = req->window_func;
837+
838+
if (list_length(wfunc->args) == 1)
839+
{
840+
Node *expr = eval_const_expressions(NULL, linitial(wfunc->args));
841+
842+
/*
843+
* Due to the Node representation of WindowClause runConditions in
844+
* version prior to v17, we need to insist that the count arg is
845+
* Const to allow safe application of the runCondition
846+
* optimization.
847+
*/
848+
if (!IsA(expr, Const))
849+
PG_RETURN_POINTER(NULL);
850+
}
836851

837852
/* No ORDER BY clause then all rows are peers */
838853
if (req->window_clause->orderClause == NIL)

src/backend/utils/adt/windowfuncs.c

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "postgres.h"
1515

1616
#include "nodes/supportnodes.h"
17+
#include "optimizer/optimizer.h"
1718
#include "utils/builtins.h"
1819
#include "windowapi.h"
1920

@@ -486,13 +487,29 @@ window_ntile_support(PG_FUNCTION_ARGS)
486487
if (IsA(rawreq, SupportRequestWFuncMonotonic))
487488
{
488489
SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
490+
WindowFunc *wfunc = req->window_func;
489491

490-
/*
491-
* ntile() is monotonically increasing as the number of buckets cannot
492-
* change after the first call
493-
*/
494-
req->monotonic = MONOTONICFUNC_INCREASING;
495-
PG_RETURN_POINTER(req);
492+
if (list_length(wfunc->args) == 1)
493+
{
494+
Node *expr = eval_const_expressions(NULL, linitial(wfunc->args));
495+
496+
/*
497+
* Due to the Node representation of WindowClause runConditions in
498+
* version prior to v17, we need to insist that ntile arg is Const
499+
* to allow safe application of the runCondition optimization.
500+
*/
501+
if (IsA(expr, Const))
502+
{
503+
/*
504+
* ntile() is monotonically increasing as the number of
505+
* buckets cannot change after the first call
506+
*/
507+
req->monotonic = MONOTONICFUNC_INCREASING;
508+
PG_RETURN_POINTER(req);
509+
}
510+
}
511+
512+
PG_RETURN_POINTER(NULL);
496513
}
497514

498515
if (IsA(rawreq, SupportRequestOptimizeWindowClause))

src/test/regress/expected/window.out

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3577,13 +3577,13 @@ EXPLAIN (COSTS OFF)
35773577
SELECT * FROM
35783578
(SELECT empno,
35793579
salary,
3580-
count(empno) OVER (ORDER BY salary DESC) c
3580+
count(1) OVER (ORDER BY salary DESC) c
35813581
FROM empsalary) emp
35823582
WHERE c <= 3;
3583-
QUERY PLAN
3584-
---------------------------------------------------------
3583+
QUERY PLAN
3584+
-------------------------------------------
35853585
WindowAgg
3586-
Run Condition: (count(empsalary.empno) OVER (?) <= 3)
3586+
Run Condition: (count(1) OVER (?) <= 3)
35873587
-> Sort
35883588
Sort Key: empsalary.salary DESC
35893589
-> Seq Scan on empsalary
@@ -3592,7 +3592,7 @@ WHERE c <= 3;
35923592
SELECT * FROM
35933593
(SELECT empno,
35943594
salary,
3595-
count(empno) OVER (ORDER BY salary DESC) c
3595+
count(1) OVER (ORDER BY salary DESC) c
35963596
FROM empsalary) emp
35973597
WHERE c <= 3;
35983598
empno | salary | c
@@ -3704,19 +3704,19 @@ WHERE rn < 3;
37043704
-> Seq Scan on empsalary
37053705
(6 rows)
37063706

3707-
-- likewise with count(empno) instead of row_number()
3707+
-- likewise with count(1) instead of row_number()
37083708
EXPLAIN (COSTS OFF)
37093709
SELECT * FROM
37103710
(SELECT empno,
37113711
depname,
37123712
salary,
3713-
count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
3713+
count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
37143714
FROM empsalary) emp
37153715
WHERE c <= 3;
37163716
QUERY PLAN
37173717
------------------------------------------------------------
37183718
WindowAgg
3719-
Run Condition: (count(empsalary.empno) OVER (?) <= 3)
3719+
Run Condition: (count(1) OVER (?) <= 3)
37203720
-> Sort
37213721
Sort Key: empsalary.depname, empsalary.salary DESC
37223722
-> Seq Scan on empsalary
@@ -3727,7 +3727,7 @@ SELECT * FROM
37273727
(SELECT empno,
37283728
depname,
37293729
salary,
3730-
count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
3730+
count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
37313731
FROM empsalary) emp
37323732
WHERE c <= 3;
37333733
empno | depname | salary | c
@@ -3749,21 +3749,21 @@ SELECT * FROM
37493749
(SELECT empno,
37503750
depname,
37513751
salary,
3752-
count(empno) OVER () c
3752+
count(1) OVER () c
37533753
FROM empsalary) emp
37543754
WHERE c = 1;
3755-
QUERY PLAN
3756-
--------------------------------------------------------
3755+
QUERY PLAN
3756+
------------------------------------------
37573757
WindowAgg
3758-
Run Condition: (count(empsalary.empno) OVER (?) = 1)
3758+
Run Condition: (count(1) OVER (?) = 1)
37593759
-> Seq Scan on empsalary
37603760
(3 rows)
37613761

37623762
-- Some more complex cases with multiple window clauses
37633763
EXPLAIN (COSTS OFF)
37643764
SELECT * FROM
37653765
(SELECT *,
3766-
count(salary) OVER (PARTITION BY depname || '') c1, -- w1
3766+
count(1) OVER (PARTITION BY depname || '') c1, -- w1
37673767
row_number() OVER (PARTITION BY depname) rn, -- w2
37683768
count(*) OVER (PARTITION BY depname) c2, -- w2
37693769
count(*) OVER (PARTITION BY '' || depname) c3, -- w3
@@ -3775,7 +3775,7 @@ SELECT * FROM
37753775
Subquery Scan on e
37763776
-> WindowAgg
37773777
Filter: (((row_number() OVER (?)) <= 1) AND ((ntile(2) OVER (?)) < 2))
3778-
Run Condition: (count(empsalary.salary) OVER (?) <= 3)
3778+
Run Condition: (count(1) OVER (?) <= 3)
37793779
-> Sort
37803780
Sort Key: (((empsalary.depname)::text || ''::text))
37813781
-> WindowAgg
@@ -3791,7 +3791,7 @@ SELECT * FROM
37913791
-- Ensure we correctly filter out all of the run conditions from each window
37923792
SELECT * FROM
37933793
(SELECT *,
3794-
count(salary) OVER (PARTITION BY depname || '') c1, -- w1
3794+
count(1) OVER (PARTITION BY depname || '') c1, -- w1
37953795
row_number() OVER (PARTITION BY depname) rn, -- w2
37963796
count(*) OVER (PARTITION BY depname) c2, -- w2
37973797
count(*) OVER (PARTITION BY '' || depname) c3, -- w3
@@ -3804,32 +3804,6 @@ SELECT * FROM
38043804
sales | 3 | 4800 | 08-01-2007 | 3 | 1 | 3 | 3 | 1
38053805
(2 rows)
38063806

3807-
-- Ensure we remove references to reduced outer joins as nulling rels in run
3808-
-- conditions
3809-
EXPLAIN (COSTS OFF)
3810-
SELECT 1 FROM
3811-
(SELECT ntile(e2.salary) OVER (PARTITION BY e1.depname) AS c
3812-
FROM empsalary e1 LEFT JOIN empsalary e2 ON TRUE
3813-
WHERE e1.empno = e2.empno) s
3814-
WHERE s.c = 1;
3815-
QUERY PLAN
3816-
---------------------------------------------------------
3817-
Subquery Scan on s
3818-
Filter: (s.c = 1)
3819-
-> WindowAgg
3820-
Run Condition: (ntile(e2.salary) OVER (?) <= 1)
3821-
-> Sort
3822-
Sort Key: e1.depname
3823-
-> Merge Join
3824-
Merge Cond: (e1.empno = e2.empno)
3825-
-> Sort
3826-
Sort Key: e1.empno
3827-
-> Seq Scan on empsalary e1
3828-
-> Sort
3829-
Sort Key: e2.empno
3830-
-> Seq Scan on empsalary e2
3831-
(14 rows)
3832-
38333807
-- Tests to ensure we don't push down the run condition when it's not valid to
38343808
-- do so.
38353809
-- Ensure we don't push down when the frame options show that the window
@@ -3889,6 +3863,42 @@ WHERE c = 1;
38893863
-> Seq Scan on empsalary
38903864
(6 rows)
38913865

3866+
-- Ensure we don't use a run condition when the WindowFunc arg contains a Var
3867+
EXPLAIN (COSTS OFF)
3868+
SELECT * FROM
3869+
(SELECT empno,
3870+
salary,
3871+
count(empno) OVER (ORDER BY empno DESC) c
3872+
FROM empsalary) emp
3873+
WHERE c = 1;
3874+
QUERY PLAN
3875+
----------------------------------------------
3876+
Subquery Scan on emp
3877+
Filter: (emp.c = 1)
3878+
-> WindowAgg
3879+
-> Sort
3880+
Sort Key: empsalary.empno DESC
3881+
-> Seq Scan on empsalary
3882+
(6 rows)
3883+
3884+
-- As above but with ntile().
3885+
EXPLAIN (COSTS OFF)
3886+
SELECT * FROM
3887+
(SELECT empno,
3888+
salary,
3889+
ntile(empno::int) OVER (ORDER BY empno DESC) nt
3890+
FROM empsalary) emp
3891+
WHERE nt = 1;
3892+
QUERY PLAN
3893+
----------------------------------------------
3894+
Subquery Scan on emp
3895+
Filter: (emp.nt = 1)
3896+
-> WindowAgg
3897+
-> Sort
3898+
Sort Key: empsalary.empno DESC
3899+
-> Seq Scan on empsalary
3900+
(6 rows)
3901+
38923902
-- Ensure we don't use a run condition when the WindowFunc contains subplans
38933903
EXPLAIN (COSTS OFF)
38943904
SELECT * FROM

src/test/regress/sql/window.sql

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,14 +1120,14 @@ EXPLAIN (COSTS OFF)
11201120
SELECT * FROM
11211121
(SELECT empno,
11221122
salary,
1123-
count(empno) OVER (ORDER BY salary DESC) c
1123+
count(1) OVER (ORDER BY salary DESC) c
11241124
FROM empsalary) emp
11251125
WHERE c <= 3;
11261126

11271127
SELECT * FROM
11281128
(SELECT empno,
11291129
salary,
1130-
count(empno) OVER (ORDER BY salary DESC) c
1130+
count(1) OVER (ORDER BY salary DESC) c
11311131
FROM empsalary) emp
11321132
WHERE c <= 3;
11331133

@@ -1183,13 +1183,13 @@ SELECT empno, depname FROM
11831183
FROM empsalary) emp
11841184
WHERE rn < 3;
11851185

1186-
-- likewise with count(empno) instead of row_number()
1186+
-- likewise with count(1) instead of row_number()
11871187
EXPLAIN (COSTS OFF)
11881188
SELECT * FROM
11891189
(SELECT empno,
11901190
depname,
11911191
salary,
1192-
count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
1192+
count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
11931193
FROM empsalary) emp
11941194
WHERE c <= 3;
11951195

@@ -1198,7 +1198,7 @@ SELECT * FROM
11981198
(SELECT empno,
11991199
depname,
12001200
salary,
1201-
count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
1201+
count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
12021202
FROM empsalary) emp
12031203
WHERE c <= 3;
12041204

@@ -1209,15 +1209,15 @@ SELECT * FROM
12091209
(SELECT empno,
12101210
depname,
12111211
salary,
1212-
count(empno) OVER () c
1212+
count(1) OVER () c
12131213
FROM empsalary) emp
12141214
WHERE c = 1;
12151215

12161216
-- Some more complex cases with multiple window clauses
12171217
EXPLAIN (COSTS OFF)
12181218
SELECT * FROM
12191219
(SELECT *,
1220-
count(salary) OVER (PARTITION BY depname || '') c1, -- w1
1220+
count(1) OVER (PARTITION BY depname || '') c1, -- w1
12211221
row_number() OVER (PARTITION BY depname) rn, -- w2
12221222
count(*) OVER (PARTITION BY depname) c2, -- w2
12231223
count(*) OVER (PARTITION BY '' || depname) c3, -- w3
@@ -1228,23 +1228,14 @@ SELECT * FROM
12281228
-- Ensure we correctly filter out all of the run conditions from each window
12291229
SELECT * FROM
12301230
(SELECT *,
1231-
count(salary) OVER (PARTITION BY depname || '') c1, -- w1
1231+
count(1) OVER (PARTITION BY depname || '') c1, -- w1
12321232
row_number() OVER (PARTITION BY depname) rn, -- w2
12331233
count(*) OVER (PARTITION BY depname) c2, -- w2
12341234
count(*) OVER (PARTITION BY '' || depname) c3, -- w3
12351235
ntile(2) OVER (PARTITION BY depname) nt -- w2
12361236
FROM empsalary
12371237
) e WHERE rn <= 1 AND c1 <= 3 AND nt < 2;
12381238

1239-
-- Ensure we remove references to reduced outer joins as nulling rels in run
1240-
-- conditions
1241-
EXPLAIN (COSTS OFF)
1242-
SELECT 1 FROM
1243-
(SELECT ntile(e2.salary) OVER (PARTITION BY e1.depname) AS c
1244-
FROM empsalary e1 LEFT JOIN empsalary e2 ON TRUE
1245-
WHERE e1.empno = e2.empno) s
1246-
WHERE s.c = 1;
1247-
12481239
-- Tests to ensure we don't push down the run condition when it's not valid to
12491240
-- do so.
12501241

@@ -1278,6 +1269,24 @@ SELECT * FROM
12781269
FROM empsalary) emp
12791270
WHERE c = 1;
12801271

1272+
-- Ensure we don't use a run condition when the WindowFunc arg contains a Var
1273+
EXPLAIN (COSTS OFF)
1274+
SELECT * FROM
1275+
(SELECT empno,
1276+
salary,
1277+
count(empno) OVER (ORDER BY empno DESC) c
1278+
FROM empsalary) emp
1279+
WHERE c = 1;
1280+
1281+
-- As above but with ntile().
1282+
EXPLAIN (COSTS OFF)
1283+
SELECT * FROM
1284+
(SELECT empno,
1285+
salary,
1286+
ntile(empno::int) OVER (ORDER BY empno DESC) nt
1287+
FROM empsalary) emp
1288+
WHERE nt = 1;
1289+
12811290
-- Ensure we don't use a run condition when the WindowFunc contains subplans
12821291
EXPLAIN (COSTS OFF)
12831292
SELECT * FROM

0 commit comments

Comments
 (0)