Skip to content

Commit a21049f

Browse files
committed
Fix pull_varnos to cope with translated PlaceHolderVars.
Commit 55dc86e changed pull_varnos to use (if possible) the associated ph_eval_at for a PlaceHolderVar. I missed a fine point though: we might be looking at a PHV in the quals or tlist of a child appendrel, in which case we need to compute a ph_eval_at value that's been translated in the same way that the PHV itself has been (cf. adjust_appendrel_attrs). Fortunately, enough info is available in the PlaceHolderInfo to make such translation possible without additional outside data, so we don't need another round of uglification of planner APIs. This is a little bit complicated, but since it's a hard-to-hit corner case, I'm not much worried about adding cycles here. Per report from Jaime Casanova. Back-patch to v12, like the previous commit. Discussion: https://postgr.es/m/20210915230959.GB17635@ahch-to
1 parent cddcf78 commit a21049f

File tree

3 files changed

+82
-3
lines changed

3 files changed

+82
-3
lines changed

src/backend/optimizer/util/var.c

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
188188
* join that forces delay of evaluation of a given qual clause
189189
* will be processed before we examine that clause here, so the
190190
* ph_eval_at value should have been updated to include it.
191+
*
192+
* Another problem is that a PlaceHolderVar can appear in quals or
193+
* tlists that have been translated for use in a child appendrel.
194+
* Typically such a PHV is a parameter expression sourced by some
195+
* other relation, so that the translation from parent appendrel
196+
* to child doesn't change its phrels, and we should still take
197+
* ph_eval_at at face value. But in corner cases, the PHV's
198+
* original phrels can include the parent appendrel itself, in
199+
* which case the translated PHV will have the child appendrel in
200+
* phrels, and we must translate ph_eval_at to match.
191201
*/
192202
PlaceHolderInfo *phinfo = NULL;
193203

@@ -203,12 +213,37 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
203213
phinfo = NULL;
204214
}
205215
}
206-
if (phinfo != NULL)
216+
if (phinfo == NULL)
217+
{
218+
/* No PlaceHolderInfo yet, use phrels */
219+
context->varnos = bms_add_members(context->varnos,
220+
phv->phrels);
221+
}
222+
else if (bms_equal(phv->phrels, phinfo->ph_var->phrels))
223+
{
224+
/* Normal case: use ph_eval_at */
207225
context->varnos = bms_add_members(context->varnos,
208226
phinfo->ph_eval_at);
227+
}
209228
else
210-
context->varnos = bms_add_members(context->varnos,
211-
phv->phrels);
229+
{
230+
/* Translated PlaceHolderVar: translate ph_eval_at to match */
231+
Relids newevalat,
232+
delta;
233+
234+
/* remove what was removed from phv->phrels ... */
235+
delta = bms_difference(phinfo->ph_var->phrels, phv->phrels);
236+
newevalat = bms_difference(phinfo->ph_eval_at, delta);
237+
/* ... then if that was in fact part of ph_eval_at ... */
238+
if (!bms_equal(newevalat, phinfo->ph_eval_at))
239+
{
240+
/* ... add what was added */
241+
delta = bms_difference(phv->phrels, phinfo->ph_var->phrels);
242+
newevalat = bms_join(newevalat, delta);
243+
}
244+
context->varnos = bms_join(context->varnos,
245+
newevalat);
246+
}
212247
return false; /* don't recurse into expression */
213248
}
214249
}

src/test/regress/expected/join.out

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4870,6 +4870,34 @@ where q2 = 456;
48704870
123 | 456 | |
48714871
(1 row)
48724872

4873+
-- and check a related issue where we miscompute required relids for
4874+
-- a PHV that's been translated to a child rel
4875+
create temp table parttbl (a integer primary key) partition by range (a);
4876+
create temp table parttbl1 partition of parttbl for values from (1) to (100);
4877+
insert into parttbl values (11), (12);
4878+
explain (costs off)
4879+
select * from
4880+
(select *, 12 as phv from parttbl) as ss
4881+
right join int4_tbl on true
4882+
where ss.a = ss.phv and f1 = 0;
4883+
QUERY PLAN
4884+
------------------------------------
4885+
Nested Loop
4886+
-> Seq Scan on int4_tbl
4887+
Filter: (f1 = 0)
4888+
-> Seq Scan on parttbl1 parttbl
4889+
Filter: (a = 12)
4890+
(5 rows)
4891+
4892+
select * from
4893+
(select *, 12 as phv from parttbl) as ss
4894+
right join int4_tbl on true
4895+
where ss.a = ss.phv and f1 = 0;
4896+
a | phv | f1
4897+
----+-----+----
4898+
12 | 12 | 0
4899+
(1 row)
4900+
48734901
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
48744902
select * from
48754903
int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; -- error

src/test/regress/sql/join.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,22 @@ select i8.*, ss.v, t.unique2
17281728
left join tenk1 t on t.unique2 = ss.v
17291729
where q2 = 456;
17301730

1731+
-- and check a related issue where we miscompute required relids for
1732+
-- a PHV that's been translated to a child rel
1733+
create temp table parttbl (a integer primary key) partition by range (a);
1734+
create temp table parttbl1 partition of parttbl for values from (1) to (100);
1735+
insert into parttbl values (11), (12);
1736+
explain (costs off)
1737+
select * from
1738+
(select *, 12 as phv from parttbl) as ss
1739+
right join int4_tbl on true
1740+
where ss.a = ss.phv and f1 = 0;
1741+
1742+
select * from
1743+
(select *, 12 as phv from parttbl) as ss
1744+
right join int4_tbl on true
1745+
where ss.a = ss.phv and f1 = 0;
1746+
17311747
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
17321748

17331749
select * from

0 commit comments

Comments
 (0)