]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Disable run condition optimization for some WindowFuncs
authorDavid Rowley <drowley@postgresql.org>
Wed, 1 May 2024 04:35:37 +0000 (16:35 +1200)
committerDavid Rowley <drowley@postgresql.org>
Wed, 1 May 2024 04:35:37 +0000 (16:35 +1200)
94985c210 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/18170-f1d17bf9a0d58b24@postgresql.org
Backpatch-through 15 (master will be fixed independently)

src/backend/utils/adt/int8.c
src/backend/utils/adt/windowfuncs.c
src/test/regress/expected/window.out
src/test/regress/sql/window.sql

index 98d432375566bf2a630432af6ddf3176c0727c2a..464b64bb5e740cbaa8c7810b94bdce3416b93a67 100644 (file)
@@ -833,6 +833,21 @@ 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));
+
+                       /*
+                        * Due to the Node representation of WindowClause runConditions in
+                        * version prior to v17, we need to insist that the count arg is
+                        * Const to allow safe application of the runCondition
+                        * optimization.
+                        */
+                       if (!IsA(expr, Const))
+                               PG_RETURN_POINTER(NULL);
+               }
 
                /* No ORDER BY clause then all rows are peers */
                if (req->window_clause->orderClause == NIL)
index 596564fa15c54a5e91f0b7f8cb2803beac465fbe..871119eeeccb4c7313b1dedbbc8bc09d520de712 100644 (file)
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "nodes/supportnodes.h"
+#include "optimizer/optimizer.h"
 #include "utils/builtins.h"
 #include "windowapi.h"
 
index 1f23baa3ccbc409e80a110e8e5eef6d11dc36ce7..f8914c0401c0054cfcffd312a59391a9ff925001 100644 (file)
@@ -3481,13 +3481,13 @@ EXPLAIN (COSTS OFF)
 SELECT * FROM
   (SELECT empno,
           salary,
-          count(empno) OVER (ORDER BY salary DESC) c
+          count(1) OVER (ORDER BY salary DESC) c
    FROM empsalary) emp
 WHERE c <= 3;
-                       QUERY PLAN                        
----------------------------------------------------------
+                QUERY PLAN                 
+-------------------------------------------
  WindowAgg
-   Run Condition: (count(empsalary.empno) OVER (?) <= 3)
+   Run Condition: (count(1) OVER (?) <= 3)
    ->  Sort
          Sort Key: empsalary.salary DESC
          ->  Seq Scan on empsalary
@@ -3496,7 +3496,7 @@ WHERE c <= 3;
 SELECT * FROM
   (SELECT empno,
           salary,
-          count(empno) OVER (ORDER BY salary DESC) c
+          count(1) OVER (ORDER BY salary DESC) c
    FROM empsalary) emp
 WHERE c <= 3;
  empno | salary | c 
@@ -3608,19 +3608,19 @@ WHERE rn < 3;
                ->  Seq Scan on empsalary
 (6 rows)
 
--- likewise with count(empno) instead of row_number()
+-- likewise with count(1) instead of row_number()
 EXPLAIN (COSTS OFF)
 SELECT * FROM
   (SELECT empno,
           depname,
           salary,
-          count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
+          count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
    FROM empsalary) emp
 WHERE c <= 3;
                          QUERY PLAN                         
 ------------------------------------------------------------
  WindowAgg
-   Run Condition: (count(empsalary.empno) OVER (?) <= 3)
+   Run Condition: (count(1) OVER (?) <= 3)
    ->  Sort
          Sort Key: empsalary.depname, empsalary.salary DESC
          ->  Seq Scan on empsalary
@@ -3631,7 +3631,7 @@ SELECT * FROM
   (SELECT empno,
           depname,
           salary,
-          count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
+          count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
    FROM empsalary) emp
 WHERE c <= 3;
  empno |  depname  | salary | c 
@@ -3653,13 +3653,13 @@ SELECT * FROM
   (SELECT empno,
           depname,
           salary,
-          count(empno) OVER () c
+          count(1) OVER () c
    FROM empsalary) emp
 WHERE c = 1;
-                       QUERY PLAN                       
---------------------------------------------------------
+                QUERY PLAN                
+------------------------------------------
  WindowAgg
-   Run Condition: (count(empsalary.empno) OVER (?) = 1)
+   Run Condition: (count(1) OVER (?) = 1)
    ->  Seq Scan on empsalary
 (3 rows)
 
@@ -3667,7 +3667,7 @@ WHERE c = 1;
 EXPLAIN (COSTS OFF)
 SELECT * FROM
   (SELECT *,
-          count(salary) OVER (PARTITION BY depname || '') c1, -- w1
+          count(1) 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
@@ -3678,7 +3678,7 @@ SELECT * FROM
  Subquery Scan on e
    ->  WindowAgg
          Filter: ((row_number() OVER (?)) <= 1)
-         Run Condition: (count(empsalary.salary) OVER (?) <= 3)
+         Run Condition: (count(1) OVER (?) <= 3)
          ->  Sort
                Sort Key: (((empsalary.depname)::text || ''::text))
                ->  WindowAgg
@@ -3694,7 +3694,7 @@ SELECT * FROM
 -- Ensure we correctly filter out all of the run conditions from each window
 SELECT * FROM
   (SELECT *,
-          count(salary) OVER (PARTITION BY depname || '') c1, -- w1
+          count(1) 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
@@ -3765,6 +3765,24 @@ 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)
+
 -- Ensure we don't use a run condition when the WindowFunc contains subplans
 EXPLAIN (COSTS OFF)
 SELECT * FROM
index 253c1b70892d8e49d4638f1332e553b43191b8b4..2a5aa38d29a2d4506091e0610510cf7bec77b2a7 100644 (file)
@@ -1068,14 +1068,14 @@ EXPLAIN (COSTS OFF)
 SELECT * FROM
   (SELECT empno,
           salary,
-          count(empno) OVER (ORDER BY salary DESC) c
+          count(1) OVER (ORDER BY salary DESC) c
    FROM empsalary) emp
 WHERE c <= 3;
 
 SELECT * FROM
   (SELECT empno,
           salary,
-          count(empno) OVER (ORDER BY salary DESC) c
+          count(1) OVER (ORDER BY salary DESC) c
    FROM empsalary) emp
 WHERE c <= 3;
 
@@ -1131,13 +1131,13 @@ SELECT empno, depname FROM
    FROM empsalary) emp
 WHERE rn < 3;
 
--- likewise with count(empno) instead of row_number()
+-- likewise with count(1) instead of row_number()
 EXPLAIN (COSTS OFF)
 SELECT * FROM
   (SELECT empno,
           depname,
           salary,
-          count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
+          count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
    FROM empsalary) emp
 WHERE c <= 3;
 
@@ -1146,7 +1146,7 @@ SELECT * FROM
   (SELECT empno,
           depname,
           salary,
-          count(empno) OVER (PARTITION BY depname ORDER BY salary DESC) c
+          count(1) OVER (PARTITION BY depname ORDER BY salary DESC) c
    FROM empsalary) emp
 WHERE c <= 3;
 
@@ -1157,7 +1157,7 @@ SELECT * FROM
   (SELECT empno,
           depname,
           salary,
-          count(empno) OVER () c
+          count(1) OVER () c
    FROM empsalary) emp
 WHERE c = 1;
 
@@ -1165,7 +1165,7 @@ WHERE c = 1;
 EXPLAIN (COSTS OFF)
 SELECT * FROM
   (SELECT *,
-          count(salary) OVER (PARTITION BY depname || '') c1, -- w1
+          count(1) 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
@@ -1175,7 +1175,7 @@ SELECT * FROM
 -- Ensure we correctly filter out all of the run conditions from each window
 SELECT * FROM
   (SELECT *,
-          count(salary) OVER (PARTITION BY depname || '') c1, -- w1
+          count(1) 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
@@ -1215,6 +1215,15 @@ 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;
+
 -- Ensure we don't use a run condition when the WindowFunc contains subplans
 EXPLAIN (COSTS OFF)
 SELECT * FROM