Skip to content

Commit febe013

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 24c57aa commit febe013

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
@@ -200,6 +200,16 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
200200
* join that forces delay of evaluation of a given qual clause
201201
* will be processed before we examine that clause here, so the
202202
* ph_eval_at value should have been updated to include it.
203+
*
204+
* Another problem is that a PlaceHolderVar can appear in quals or
205+
* tlists that have been translated for use in a child appendrel.
206+
* Typically such a PHV is a parameter expression sourced by some
207+
* other relation, so that the translation from parent appendrel
208+
* to child doesn't change its phrels, and we should still take
209+
* ph_eval_at at face value. But in corner cases, the PHV's
210+
* original phrels can include the parent appendrel itself, in
211+
* which case the translated PHV will have the child appendrel in
212+
* phrels, and we must translate ph_eval_at to match.
203213
*/
204214
PlaceHolderInfo *phinfo = NULL;
205215

@@ -215,12 +225,37 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
215225
phinfo = NULL;
216226
}
217227
}
218-
if (phinfo != NULL)
228+
if (phinfo == NULL)
229+
{
230+
/* No PlaceHolderInfo yet, use phrels */
231+
context->varnos = bms_add_members(context->varnos,
232+
phv->phrels);
233+
}
234+
else if (bms_equal(phv->phrels, phinfo->ph_var->phrels))
235+
{
236+
/* Normal case: use ph_eval_at */
219237
context->varnos = bms_add_members(context->varnos,
220238
phinfo->ph_eval_at);
239+
}
221240
else
222-
context->varnos = bms_add_members(context->varnos,
223-
phv->phrels);
241+
{
242+
/* Translated PlaceHolderVar: translate ph_eval_at to match */
243+
Relids newevalat,
244+
delta;
245+
246+
/* remove what was removed from phv->phrels ... */
247+
delta = bms_difference(phinfo->ph_var->phrels, phv->phrels);
248+
newevalat = bms_difference(phinfo->ph_eval_at, delta);
249+
/* ... then if that was in fact part of ph_eval_at ... */
250+
if (!bms_equal(newevalat, phinfo->ph_eval_at))
251+
{
252+
/* ... add what was added */
253+
delta = bms_difference(phv->phrels, phinfo->ph_var->phrels);
254+
newevalat = bms_join(newevalat, delta);
255+
}
256+
context->varnos = bms_join(context->varnos,
257+
newevalat);
258+
}
224259
return false; /* don't recurse into expression */
225260
}
226261
}

src/test/regress/expected/join.out

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4629,6 +4629,34 @@ where q2 = 456;
46294629
123 | 456 | |
46304630
(1 row)
46314631

4632+
-- and check a related issue where we miscompute required relids for
4633+
-- a PHV that's been translated to a child rel
4634+
create temp table parttbl (a integer primary key) partition by range (a);
4635+
create temp table parttbl1 partition of parttbl for values from (1) to (100);
4636+
insert into parttbl values (11), (12);
4637+
explain (costs off)
4638+
select * from
4639+
(select *, 12 as phv from parttbl) as ss
4640+
right join int4_tbl on true
4641+
where ss.a = ss.phv and f1 = 0;
4642+
QUERY PLAN
4643+
----------------------------
4644+
Nested Loop
4645+
-> Seq Scan on int4_tbl
4646+
Filter: (f1 = 0)
4647+
-> Seq Scan on parttbl1
4648+
Filter: (a = 12)
4649+
(5 rows)
4650+
4651+
select * from
4652+
(select *, 12 as phv from parttbl) as ss
4653+
right join int4_tbl on true
4654+
where ss.a = ss.phv and f1 = 0;
4655+
a | phv | f1
4656+
----+-----+----
4657+
12 | 12 | 0
4658+
(1 row)
4659+
46324660
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
46334661
select * from
46344662
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
@@ -1635,6 +1635,22 @@ select i8.*, ss.v, t.unique2
16351635
left join tenk1 t on t.unique2 = ss.v
16361636
where q2 = 456;
16371637

1638+
-- and check a related issue where we miscompute required relids for
1639+
-- a PHV that's been translated to a child rel
1640+
create temp table parttbl (a integer primary key) partition by range (a);
1641+
create temp table parttbl1 partition of parttbl for values from (1) to (100);
1642+
insert into parttbl values (11), (12);
1643+
explain (costs off)
1644+
select * from
1645+
(select *, 12 as phv from parttbl) as ss
1646+
right join int4_tbl on true
1647+
where ss.a = ss.phv and f1 = 0;
1648+
1649+
select * from
1650+
(select *, 12 as phv from parttbl) as ss
1651+
right join int4_tbl on true
1652+
where ss.a = ss.phv and f1 = 0;
1653+
16381654
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
16391655

16401656
select * from

0 commit comments

Comments
 (0)