]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix incorrect Result node flattening logic
authorDavid Rowley <drowley@postgresql.org>
Fri, 31 Jul 2026 01:15:52 +0000 (13:15 +1200)
committerDavid Rowley <drowley@postgresql.org>
Fri, 31 Jul 2026 01:15:52 +0000 (13:15 +1200)
This fixes some incorrect flattening of nested Result nodes during
create_plan that was introduced by f2bae51df.  That commit failed to
maintain the logic that checks for subplans and gating quals from the
nested Result node before flattening, and that could result in the nested
gating qual and subplan being lost, which could produce incorrect results.

Bug: #19579
Reported-by: Viktor Leis <leis@in.tum.de>
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/19579-e6296b6c9fc0591c@postgresql.org
Backpatch-through: 19

src/backend/optimizer/plan/createplan.c
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index 2c696ea02688e26d7df6f37bab7a60f5048bd0a9..ab392ef60fe1bfe021443dfb984692689e8f49df 100644 (file)
@@ -1033,21 +1033,22 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
                                                           (Node *) gating_quals, plan);
 
        /*
-        * We might have had a trivial Result plan already.  Stacking one Result
-        * atop another is silly, so if that applies, just discard the input plan.
-        * (We're assuming its targetlist is uninteresting; it should be either
-        * the same as the result of build_path_tlist, or a simplified version.
-        * However, we preserve the set of relids that it purports to scan and
-        * attribute that to our replacement Result instead, and likewise for the
-        * result_type.)
+        * See if we can reduce down stacked Result nodes to a single node.  This
+        * is only possible when the nested Result has no subplan and no gating
+        * qual.  If we do remove the nested Result, we maintain the relids and
+        * result_type for EXPLAIN.
         */
        if (IsA(plan, Result))
        {
                Result     *rplan = (Result *) plan;
 
-               gplan->plan.lefttree = NULL;
-               gplan->relids = rplan->relids;
-               gplan->result_type = rplan->result_type;
+               if (rplan->plan.lefttree == NULL &&
+                       rplan->resconstantqual == NULL)
+               {
+                       gplan->plan.lefttree = NULL;
+                       gplan->relids = rplan->relids;
+                       gplan->result_type = rplan->result_type;
+               }
        }
 
        /*
index 19e2cca548b68b97f3826ee6eb914f6fedffbea5..05f359d3aa79bb00f0713f72bd5c63775596da4a 100644 (file)
@@ -6707,6 +6707,17 @@ select p.* from
    One-Time Filter: false
 (3 rows)
 
+-- Ensure multiple gating quals are evaluated correctly
+select * from (
+  select c0 from
+    (select null::int as c0 from ((select 1) union all (select 2))) t1
+  full join (select 1) t2 on true
+  where c0 is not null
+) t3 where now() is not null;
+ c0 
+----
+(0 rows)
+
 -- bug 5255: this is not optimizable by join removal
 begin;
 CREATE TEMP TABLE a (id int PRIMARY KEY);
index 85aed7bf70470c89f8c58c31ce921a34a6035166..450bd5bbf2c417fa0f7ed3c8d2ebc5e1dc785925 100644 (file)
@@ -2487,6 +2487,14 @@ select p.* from
   (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k
   where p.k = 1 and p.k = 2;
 
+-- Ensure multiple gating quals are evaluated correctly
+select * from (
+  select c0 from
+    (select null::int as c0 from ((select 1) union all (select 2))) t1
+  full join (select 1) t2 on true
+  where c0 is not null
+) t3 where now() is not null;
+
 -- bug 5255: this is not optimizable by join removal
 begin;