mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix join removal when Vars reference the removed join via its alias
When a join has an alias, the parser marks every Var referenced through that alias with the join's RT index as its syntactic referent (varnosyn), even though varno points at the underlying base relation. Since commit2ebf25e7d, join removal strips the removed relids from the query tree using ChangeVarNodes() with INVALID_VAR, which asserts that the relid doesn't appear in any field identifying a single relation. A varnosyn that names the removed join violates that, so a query like SELECT j.x FROM (t1 s(x) LEFT JOIN t2 ON s.x = t2.a) j; with t2.a unique fails an assertion. In non-assert builds, varnosyn is silently set to INVALID_VAR. Such syntactic references are still valid after the removal: the join is only taken out of the jointree, and its RTE remains in the rangetable through to the finished plan. So, when deleting a relid, leave varnosyn unchanged, as join removal did before2ebf25e7d. Back-patch to v16, as with commit2ebf25e7d. Author: Richard Guo <guofenglinux@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/CAMbWs484SQSdzWTgZYKvwtuC6J4m7acw0LfBjp3aWPvav7gC_w@mail.gmail.com Backpatch-through: 16
This commit is contained in:
@@ -553,7 +553,9 @@ offset_relid_set(Relids relids, int offset)
|
||||
* Also, new_index can be INVALID_VAR to indicate that we are deleting the
|
||||
* given relid from the tree. In this case we expect to find rt_index only
|
||||
* in Relids fields (varnullingrels, phnullingrels, phrels), never in any
|
||||
* field that identifies a single relation.
|
||||
* field that identifies a single relation. The exception is varnosyn,
|
||||
* which may name an aliased join being removed; the join's RTE remains in
|
||||
* the rangetable, so we leave such syntactic references unchanged.
|
||||
*
|
||||
* NOTE: although this has the form of a walker, we cheat and modify the
|
||||
* nodes in-place. The given expression tree should have been copied
|
||||
@@ -586,11 +588,10 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
|
||||
var->varnullingrels = adjust_relid_set(var->varnullingrels,
|
||||
context->rt_index,
|
||||
context->new_index);
|
||||
if (var->varnosyn == context->rt_index)
|
||||
{
|
||||
Assert(context->new_index != INVALID_VAR);
|
||||
/* when deleting, leave syntactic refs to a removed join alone */
|
||||
if (var->varnosyn == context->rt_index &&
|
||||
context->new_index != INVALID_VAR)
|
||||
var->varnosyn = context->new_index;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6265,6 +6265,18 @@ select c.id, ss.a from c
|
||||
-> Seq Scan on c
|
||||
(7 rows)
|
||||
|
||||
-- check join removal when Vars reference the removed join via its alias
|
||||
explain (verbose, costs off)
|
||||
select j.b_id, (select j.b_id) from (a left join b on a.b_id = b.id) j;
|
||||
QUERY PLAN
|
||||
-------------------------------
|
||||
Seq Scan on pg_temp.a
|
||||
Output: a.b_id, (SubPlan 1)
|
||||
SubPlan 1
|
||||
-> Result
|
||||
Output: a.b_id
|
||||
(5 rows)
|
||||
|
||||
CREATE TEMP TABLE parted_b (id int PRIMARY KEY) partition by range(id);
|
||||
CREATE TEMP TABLE parted_b1 partition of parted_b for values from (0) to (10);
|
||||
-- test join removals on a partitioned table
|
||||
|
||||
@@ -2287,6 +2287,10 @@ select c.id, ss.a from c
|
||||
left join (select d.a from onerow, d left join b on d.a = b.id) ss
|
||||
on c.id = ss.a;
|
||||
|
||||
-- check join removal when Vars reference the removed join via its alias
|
||||
explain (verbose, costs off)
|
||||
select j.b_id, (select j.b_id) from (a left join b on a.b_id = b.id) j;
|
||||
|
||||
CREATE TEMP TABLE parted_b (id int PRIMARY KEY) partition by range(id);
|
||||
CREATE TEMP TABLE parted_b1 partition of parted_b for values from (0) to (10);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user