0002-fix-variable-not-found-in-subplan-target-lists.patch
text/x-patch
Filename: 0002-fix-variable-not-found-in-subplan-target-lists.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0002
Subject: fix: variable not found in subplan target lists
| File | + | − |
|---|---|---|
| src/backend/optimizer/path/equivclass.c | 15 | 6 |
| src/test/regress/expected/join.out | 47 | 0 |
| src/test/regress/sql/join.sql | 26 | 0 |
From dad22e812b3147e159f0203a41e9bf042a1d25d4 Mon Sep 17 00:00:00 2001
From: Sergey Soloviev <sergey.soloviev@tantorlabs.ru>
Date: Mon, 25 Aug 2025 19:53:39 +0300
Subject: [PATCH] fix: variable not found in subplan target lists
When restoring 'attr_needed' after self-join removal we should also
consider EC with constants because some plan nodes can use such constant
attributes.
For example, after converting `EXISTS` to JOIN planner can create
`UniquePath` referencing attribute which is contained in EC with
constant. But because `attr_needed` is NULL then this attribute is not
returned by JOIN, so at `set_plan_references` fails to find this
attribute in targetlist of node below.
---
src/backend/optimizer/path/equivclass.c | 21 +++++++----
src/test/regress/expected/join.out | 47 +++++++++++++++++++++++++
src/test/regress/sql/join.sql | 26 ++++++++++++++
3 files changed, 88 insertions(+), 6 deletions(-)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 441f12f6c50..58ca75ab7a8 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2586,18 +2586,27 @@ rebuild_eclass_attr_needed(PlannerInfo *root)
*/
Assert(ec->ec_childmembers == NULL);
- /* Need do anything only for a multi-member, no-const EC. */
- if (list_length(ec->ec_members) > 1 && !ec->ec_has_const)
+ /*
+ * Need do anything only for a multi-member. Note that some
+ * wrapping plan nodes (i.e. UniquePath) can reference attributes
+ * contained in EC with constants.
+ */
+ if (list_length(ec->ec_members) > 1)
{
ListCell *lc2;
foreach(lc2, ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
- List *vars = pull_var_clause((Node *) cur_em->em_expr,
- PVC_RECURSE_AGGREGATES |
- PVC_RECURSE_WINDOWFUNCS |
- PVC_INCLUDE_PLACEHOLDERS);
+ List *vars;
+
+ if (cur_em->em_is_const)
+ continue;
+
+ vars = pull_var_clause((Node *) cur_em->em_expr,
+ PVC_RECURSE_AGGREGATES |
+ PVC_RECURSE_WINDOWFUNCS |
+ PVC_INCLUDE_PLACEHOLDERS);
add_vars_to_attr_needed(root, vars, ec->ec_relids);
list_free(vars);
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 98b05c94a11..71654f528b1 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -6500,6 +6500,53 @@ where t1.a = s.c;
----------
(0 rows)
+rollback;
+-- yet another join removal bug: EquivalenceClass cleanup must preserve
+-- dependency with JOINs above even if EC has constant
+begin;
+create table t (a int, b int);
+create unique index on t (a);
+explain (costs off)
+select t1.a from t t1
+ left join t t2 on t1.a = t2.a
+ join t t3 on true
+where exists (select 1 from t t4
+ join t t5 on t4.b = t5.b
+ join t t6 on t5.b = t6.b
+ where t1.a = t4.a and t3.a = t5.a and t4.a = 2);
+ QUERY PLAN
+----------------------------------------------------------------------------
+ Nested Loop
+ -> Nested Loop
+ -> Index Only Scan using t_a_idx on t t1
+ Index Cond: (a = 2)
+ -> HashAggregate
+ Group Key: t4.a, t5.a
+ -> Hash Join
+ Hash Cond: (t6.b = t4.b)
+ -> Seq Scan on t t6
+ -> Hash
+ -> Hash Join
+ Hash Cond: (t5.b = t4.b)
+ -> Seq Scan on t t5
+ -> Hash
+ -> Index Scan using t_a_idx on t t4
+ Index Cond: (a = 2)
+ -> Index Only Scan using t_a_idx on t t3
+ Index Cond: (a = t5.a)
+(18 rows)
+
+select t1.a from t t1
+ left join t t2 on t1.a = t2.a
+ join t t3 on true
+where exists (select 1 from t t4
+ join t t5 on t4.b = t5.b
+ join t t6 on t5.b = t6.b
+ where t1.a = t4.a and t3.a = t5.a and t4.a = 2);
+ a
+---
+(0 rows)
+
rollback;
-- test cases where we can remove a join, but not a PHV computed at it
begin;
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index 5f0a475894d..d62b4abd817 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -2420,6 +2420,32 @@ where t1.a = s.c;
rollback;
+-- yet another join removal bug: EquivalenceClass cleanup must preserve
+-- dependency with JOINs above even if EC has constant
+begin;
+
+create table t (a int, b int);
+create unique index on t (a);
+
+explain (costs off)
+select t1.a from t t1
+ left join t t2 on t1.a = t2.a
+ join t t3 on true
+where exists (select 1 from t t4
+ join t t5 on t4.b = t5.b
+ join t t6 on t5.b = t6.b
+ where t1.a = t4.a and t3.a = t5.a and t4.a = 2);
+
+select t1.a from t t1
+ left join t t2 on t1.a = t2.a
+ join t t3 on true
+where exists (select 1 from t t4
+ join t t5 on t4.b = t5.b
+ join t t6 on t5.b = t6.b
+ where t1.a = t4.a and t3.a = t5.a and t4.a = 2);
+
+rollback;
+
-- test cases where we can remove a join, but not a PHV computed at it
begin;
--
2.43.0