0002-minimal-fix-for-pull_varnos.patch
text/x-diff
Filename: 0002-minimal-fix-for-pull_varnos.patch
Type: text/x-diff
Part: 1
Patch
Format: unified
Series: patch 0002
| File | + | − |
|---|---|---|
| src/backend/optimizer/util/var.c | 16 | 21 |
| src/test/regress/expected/join.out | 48 | 8 |
| src/test/regress/sql/join.sql | 16 | 0 |
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 19b2bba707..ad275cba3a 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -159,33 +159,28 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
}
if (IsA(node, PlaceHolderVar))
{
- /*
- * A PlaceHolderVar acts as a variable of its syntactic scope, or
- * lower than that if it references only a subset of the rels in its
- * syntactic scope. It might also contain lateral references, but we
- * should ignore such references when computing the set of varnos in
- * an expression tree. Also, if the PHV contains no variables within
- * its syntactic scope, it will be forced to be evaluated exactly at
- * the syntactic scope, so take that as the relid set.
- */
PlaceHolderVar *phv = (PlaceHolderVar *) node;
- pull_varnos_context subcontext;
- subcontext.varnos = NULL;
- subcontext.sublevels_up = context->sublevels_up;
- (void) pull_varnos_walker((Node *) phv->phexpr, &subcontext);
+ /*
+ * If a PlaceHolderVar is not of the target query level, ignore it,
+ * instead recursing into its expression to see if it contains any
+ * vars that are of the target level.
+ */
if (phv->phlevelsup == context->sublevels_up)
{
- subcontext.varnos = bms_int_members(subcontext.varnos,
- phv->phrels);
- if (bms_is_empty(subcontext.varnos))
- context->varnos = bms_add_members(context->varnos,
- phv->phrels);
+ /*
+ * Ideally, the PHV's contribution to context->varnos is its
+ * ph_eval_at set. However, this code can be invoked before
+ * that's been computed, and we lack easy access to it anyway.
+ * Instead make the conservative assumption that the PHV will be
+ * evaluated at its syntactic level (phv->phrels).
+ */
+ context->varnos = bms_add_members(context->varnos,
+ phv->phrels);
+ return false; /* don't recurse into expression */
}
- context->varnos = bms_join(context->varnos, subcontext.varnos);
- return false;
}
- if (IsA(node, Query))
+ else if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
bool result;
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 81b42c601b..43d684a11a 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -4726,18 +4726,22 @@ from
left join uniquetbl u1 ON u1.f1 = t1.string4) ss
on t0.f1 = ss.case1
where ss.stringu2 !~* ss.case1;
- QUERY PLAN
---------------------------------------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------------------------------------------
Nested Loop
Join Filter: (CASE t1.ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END = t0.f1)
- -> Nested Loop
- -> Seq Scan on int4_tbl i4
- -> Index Scan using tenk1_unique2 on tenk1 t1
- Index Cond: (unique2 = i4.f1)
- Filter: (stringu2 !~* CASE ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END)
+ -> Hash Right Join
+ Hash Cond: (u1.f1 = t1.string4)
+ Filter: (t1.stringu2 !~* (CASE t1.ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END))
+ -> Seq Scan on uniquetbl u1
+ -> Hash
+ -> Nested Loop
+ -> Seq Scan on int4_tbl i4
+ -> Index Scan using tenk1_unique2 on tenk1 t1
+ Index Cond: (unique2 = i4.f1)
-> Materialize
-> Seq Scan on text_tbl t0
-(9 rows)
+(13 rows)
select t0.*
from
@@ -4756,6 +4760,42 @@ where ss.stringu2 !~* ss.case1;
(1 row)
rollback;
+-- test case to expose miscomputation of required relid set for a PHV
+explain (verbose, costs off)
+select i8.*, ss.v, t.unique2
+ from int8_tbl i8
+ left join int4_tbl i4 on i4.f1 = 1
+ left join lateral (select i4.f1 + 1 as v) as ss on true
+ left join tenk1 t on t.unique2 = ss.v
+where q2 = 456;
+ QUERY PLAN
+-------------------------------------------------------------
+ Nested Loop Left Join
+ Output: i8.q1, i8.q2, ((i4.f1 + 1)), t.unique2
+ -> Nested Loop Left Join
+ Output: i8.q1, i8.q2, (i4.f1 + 1)
+ -> Seq Scan on public.int8_tbl i8
+ Output: i8.q1, i8.q2
+ Filter: (i8.q2 = 456)
+ -> Seq Scan on public.int4_tbl i4
+ Output: i4.f1
+ Filter: (i4.f1 = 1)
+ -> Index Only Scan using tenk1_unique2 on public.tenk1 t
+ Output: t.unique2
+ Index Cond: (t.unique2 = ((i4.f1 + 1)))
+(13 rows)
+
+select i8.*, ss.v, t.unique2
+ from int8_tbl i8
+ left join int4_tbl i4 on i4.f1 = 1
+ left join lateral (select i4.f1 + 1 as v) as ss on true
+ left join tenk1 t on t.unique2 = ss.v
+where q2 = 456;
+ q1 | q2 | v | unique2
+-----+-----+---+---------
+ 123 | 456 | |
+(1 row)
+
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
select * from
int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; -- error
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index 9887fe0c0b..6a209a27aa 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -1696,6 +1696,22 @@ where ss.stringu2 !~* ss.case1;
rollback;
+-- test case to expose miscomputation of required relid set for a PHV
+explain (verbose, costs off)
+select i8.*, ss.v, t.unique2
+ from int8_tbl i8
+ left join int4_tbl i4 on i4.f1 = 1
+ left join lateral (select i4.f1 + 1 as v) as ss on true
+ left join tenk1 t on t.unique2 = ss.v
+where q2 = 456;
+
+select i8.*, ss.v, t.unique2
+ from int8_tbl i8
+ left join int4_tbl i4 on i4.f1 = 1
+ left join lateral (select i4.f1 + 1 as v) as ss on true
+ left join tenk1 t on t.unique2 = ss.v
+where q2 = 456;
+
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
select * from