Skip to content

Commit e0b0d1e

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 8767a86 commit e0b0d1e

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
@@ -4799,6 +4799,34 @@ where q2 = 456;
47994799
123 | 456 | |
48004800
(1 row)
48014801

4802+
-- and check a related issue where we miscompute required relids for
4803+
-- a PHV that's been translated to a child rel
4804+
create temp table parttbl (a integer primary key) partition by range (a);
4805+
create temp table parttbl1 partition of parttbl for values from (1) to (100);
4806+
insert into parttbl values (11), (12);
4807+
explain (costs off)
4808+
select * from
4809+
(select *, 12 as phv from parttbl) as ss
4810+
right join int4_tbl on true
4811+
where ss.a = ss.phv and f1 = 0;
4812+
QUERY PLAN
4813+
------------------------------------
4814+
Nested Loop
4815+
-> Seq Scan on int4_tbl
4816+
Filter: (f1 = 0)
4817+
-> Seq Scan on parttbl1 parttbl
4818+
Filter: (a = 12)
4819+
(5 rows)
4820+
4821+
select * from
4822+
(select *, 12 as phv from parttbl) as ss
4823+
right join int4_tbl on true
4824+
where ss.a = ss.phv and f1 = 0;
4825+
a | phv | f1
4826+
----+-----+----
4827+
12 | 12 | 0
4828+
(1 row)
4829+
48024830
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
48034831
select * from
48044832
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
@@ -1714,6 +1714,22 @@ select i8.*, ss.v, t.unique2
17141714
left join tenk1 t on t.unique2 = ss.v
17151715
where q2 = 456;
17161716

1717+
-- and check a related issue where we miscompute required relids for
1718+
-- a PHV that's been translated to a child rel
1719+
create temp table parttbl (a integer primary key) partition by range (a);
1720+
create temp table parttbl1 partition of parttbl for values from (1) to (100);
1721+
insert into parttbl values (11), (12);
1722+
explain (costs off)
1723+
select * from
1724+
(select *, 12 as phv from parttbl) as ss
1725+
right join int4_tbl on true
1726+
where ss.a = ss.phv and f1 = 0;
1727+
1728+
select * from
1729+
(select *, 12 as phv from parttbl) as ss
1730+
right join int4_tbl on true
1731+
where ss.a = ss.phv and f1 = 0;
1732+
17171733
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
17181734

17191735
select * from

0 commit comments

Comments
 (0)