diff options
author | Alexander Korotkov <akorotkov@postgresql.org> | 2025-04-29 14:34:44 +0300 |
---|---|---|
committer | Alexander Korotkov <akorotkov@postgresql.org> | 2025-04-29 14:34:44 +0300 |
commit | 2260c7f6d90ecf76d3806d32890a0890688b41e8 (patch) | |
tree | ca339e2ad4f5db7da9c05b8b9f7e7ff6d83c9cdb /src | |
parent | 15b1b4dd3fccfd2576f44841863153c312de011b (diff) | |
download | postgresql-2260c7f6d90ecf76d3806d32890a0890688b41e8.tar.gz postgresql-2260c7f6d90ecf76d3806d32890a0890688b41e8.zip |
Fixes for ChangeVarNodes_walker()
This commit fixes two bug in ChangeVarNodes_walker() function.
* When considering RestrictInfo, walk down to its clauses based on the
presense of relid to be deleted not just in clause_relids but also in
required_relids.
* Incrementally adjust num_base_rels based on the change of clause_relids
instead of recalculating it using clause_relids, which could contain
outer-join relids.
Reported-by: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49PE3CvnV8vrQ0Dr%3DHqgZZmX0tdNbzVNJxqc8yg-8kDQQ%40mail.gmail.com
Author: Andrei Lepikhov <lepihov@gmail.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/rewrite/rewriteManip.c | 28 | ||||
-rw-r--r-- | src/test/regress/expected/join.out | 16 | ||||
-rw-r--r-- | src/test/regress/sql/join.sql | 8 |
3 files changed, 46 insertions, 6 deletions
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index e190f169fb3..3f8b8b6eed9 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -644,14 +644,34 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) bool clause_relids_is_multiple = (bms_membership(rinfo->clause_relids) == BMS_MULTIPLE); - if (bms_is_member(context->rt_index, rinfo->clause_relids)) + /* + * Recurse down into clauses if the target relation is present in + * clause_relids or required_relids. We must check required_relids + * because the relation not present in clause_relids might still be + * present somewhere in orclause. + */ + if (bms_is_member(context->rt_index, rinfo->clause_relids) || + bms_is_member(context->rt_index, rinfo->required_relids)) { + Relids new_clause_relids; + expression_tree_walker((Node *) rinfo->clause, ChangeVarNodes_walker, (void *) context); expression_tree_walker((Node *) rinfo->orclause, ChangeVarNodes_walker, (void *) context); - rinfo->clause_relids = - adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index); - rinfo->num_base_rels = bms_num_members(rinfo->clause_relids); + new_clause_relids = adjust_relid_set(rinfo->clause_relids, + context->rt_index, + context->new_index); + + /* + * Incrementally adjust num_base_rels based on the change of + * clause_relids, which could contain both base relids and + * outer-join relids. This operation is legal until we remove + * only baserels. + */ + rinfo->num_base_rels -= bms_num_members(rinfo->clause_relids) - + bms_num_members(new_clause_relids); + + rinfo->clause_relids = new_clause_relids; rinfo->left_relids = adjust_relid_set(rinfo->left_relids, context->rt_index, context->new_index); rinfo->right_relids = diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index fa2c7405519..f35a0b18c37 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -7260,7 +7260,21 @@ WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code; Index Cond: (id = emp1.id) (5 rows) -INSERT INTO emp1 VALUES (1, 1), (2, 1); +-- Check that SJE correctly replaces relations in OR-clauses +EXPLAIN (COSTS OFF) +SELECT * FROM emp1 t1 + INNER JOIN emp1 t2 ON t1.id = t2.id + LEFT JOIN emp1 t3 ON t1.code = 1 AND (t2.code = t3.code OR t2.code = 1); + QUERY PLAN +--------------------------------------------------------------------------- + Nested Loop Left Join + Join Filter: ((t2.code = 1) AND ((t2.code = t3.code) OR (t2.code = 1))) + -> Seq Scan on emp1 t2 + -> Materialize + -> Seq Scan on emp1 t3 +(5 rows) + + INSERT INTO emp1 VALUES (1, 1), (2, 1); WITH t1 AS (SELECT * FROM emp1) UPDATE emp1 SET code = t1.code + 1 FROM t1 WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code; diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index d01d1da4ef8..cc5128add4d 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -2807,7 +2807,13 @@ WITH t1 AS (SELECT * FROM emp1) UPDATE emp1 SET code = t1.code + 1 FROM t1 WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code; -INSERT INTO emp1 VALUES (1, 1), (2, 1); +-- Check that SJE correctly replaces relations in OR-clauses +EXPLAIN (COSTS OFF) +SELECT * FROM emp1 t1 + INNER JOIN emp1 t2 ON t1.id = t2.id + LEFT JOIN emp1 t3 ON t1.code = 1 AND (t2.code = t3.code OR t2.code = 1); + + INSERT INTO emp1 VALUES (1, 1), (2, 1); WITH t1 AS (SELECT * FROM emp1) UPDATE emp1 SET code = t1.code + 1 FROM t1 |