Handle WindowClause.runCondition in tree walker/mutator functions.
authorTom Lane <[email protected]>
Wed, 10 Jan 2024 18:36:33 +0000 (13:36 -0500)
committerTom Lane <[email protected]>
Wed, 10 Jan 2024 18:36:33 +0000 (13:36 -0500)
Commit 9d9c02ccd, which added the notion of a "run condition" for
window functions, neglected to teach nodeFuncs.c to process the new
field.  Remarkably, that doesn't seem to have had any ill effects
before we invented Var.varnullingrels, but now it can cause visible
failures in join-removal scenarios.

I have no faith that there's not reachable problems in v15 too,
so back-patch the code change to v15 where 9d9c02ccd came in.
The test case seems irrelevant to v15, though.

Per bug #18277 from Zuming Jiang.  Diagnosis and patch by
Richard Guo.

Discussion: https://postgr.es/m/18277-089ead83b329a2fd@postgresql.org

src/backend/nodes/nodeFuncs.c
src/test/regress/expected/window.out
src/test/regress/sql/window.sql

index 030463cb429c64c1e4b3500ce9c7fb3c04e76d83..e1a5bc7e95dc9c3ddfe2d27aeb829adaf19e2b86 100644 (file)
@@ -2283,6 +2283,8 @@ expression_tree_walker_impl(Node *node,
                    return true;
                if (WALK(wc->endOffset))
                    return true;
+               if (WALK(wc->runCondition))
+                   return true;
            }
            break;
        case T_CTECycleClause:
@@ -2627,6 +2629,8 @@ query_tree_walker_impl(Query *query,
                return true;
            if (WALK(wc->endOffset))
                return true;
+           if (WALK(wc->runCondition))
+               return true;
        }
    }
 
@@ -3312,6 +3316,7 @@ 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;
@@ -3641,6 +3646,7 @@ 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);
        }
index 2201740c1858863254a02bc6f3ba94499adf1766..60de2cbf96d03da6cfb3155355832185698a4d89 100644 (file)
@@ -4181,6 +4181,32 @@ 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)
+
 -- 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
index 437e948d6c266dff77d8f1632b8ea03b8324df90..678aa1a21c81a94e791d5f413ec51a03f907ad7d 100644 (file)
@@ -1368,6 +1368,15 @@ 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;
+
 -- Tests to ensure we don't push down the run condition when it's not valid to
 -- do so.