diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 942392218cf..1130889ab29 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4325,20 +4325,29 @@ create_nestloop_plan(PlannerInfo *root, NestLoop *join_plan; Plan *outer_plan; Plan *inner_plan; + Relids outerrelids; List *tlist = build_path_tlist(root, &best_path->jpath.path); List *joinrestrictclauses = best_path->jpath.joinrestrictinfo; List *joinclauses; List *otherclauses; - Relids outerrelids; List *nestParams; Relids saveOuterRels = root->curOuterRels; /* NestLoop can project, so no need to be picky about child tlists */ outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, 0); - /* For a nestloop, include outer relids in curOuterRels for inner side */ - root->curOuterRels = bms_union(root->curOuterRels, - best_path->jpath.outerjoinpath->parent->relids); + /* + * Include the outer relids in curOuterRels while building the inner side. + * If the outer rel is a child rel, also include its top parent's relids. + * We need both forms, since Vars in the inner side refer to the child rel + * while PlaceHolderInfo.ph_eval_at is expressed in terms of top parent + * rels. + */ + outerrelids = best_path->jpath.outerjoinpath->parent->relids; + if (best_path->jpath.outerjoinpath->parent->top_parent_relids) + outerrelids = bms_union(outerrelids, + best_path->jpath.outerjoinpath->parent->top_parent_relids); + root->curOuterRels = bms_union(root->curOuterRels, outerrelids); inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, 0); @@ -4377,7 +4386,6 @@ create_nestloop_plan(PlannerInfo *root, * Identify any nestloop parameters that should be supplied by this join * node, and remove them from root->curOuterParams. */ - outerrelids = best_path->jpath.outerjoinpath->parent->relids; nestParams = identify_current_nestloop_params(root, outerrelids); join_plan = make_nestloop(tlist, diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index 320b7273e74..cdcd140c8dd 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -675,6 +675,117 @@ SELECT a, b FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) RESET enable_partitionwise_aggregate; RESET enable_hashjoin; +-- bug with PlaceHolderVars supplied as nestloop params by a child join +SET enable_hashjoin TO false; +SET enable_mergejoin TO false; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN + (SELECT b, COALESCE(c, 'x') AS c FROM prt2 WHERE a = 0) t2 ON t1.a = t2.b + WHERE t1.c = t2.c ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Nested Loop + -> Seq Scan on prt2_p1 prt2_1 + Filter: (a = 0) + -> Index Scan using iprt1_p1_a on prt1_p1 t1_1 + Index Cond: (a = prt2_1.b) + Filter: (((COALESCE(prt2_1.c, 'x'::character varying)))::text = (c)::text) + -> Nested Loop + -> Seq Scan on prt2_p2 prt2_2 + Filter: (a = 0) + -> Index Scan using iprt1_p2_a on prt1_p2 t1_2 + Index Cond: (a = prt2_2.b) + Filter: (((COALESCE(prt2_2.c, 'x'::character varying)))::text = (c)::text) + -> Nested Loop + -> Seq Scan on prt2_p3 prt2_3 + Filter: (a = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = prt2_3.b) + Filter: (((COALESCE(prt2_3.c, 'x'::character varying)))::text = (c)::text) +(21 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN + (SELECT b, COALESCE(c, 'x') AS c FROM prt2 WHERE a = 0) t2 ON t1.a = t2.b + WHERE t1.c = t2.c ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 + 300 | 0300 | 300 | 0300 + 450 | 0450 | 450 | 0450 +(4 rows) + +-- same, with the PlaceHolderVar needed by a parameterized child join nested +-- inside the child join that supplies the parameter +CREATE TABLE prt3 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt3_p1 PARTITION OF prt3 FOR VALUES FROM (0) TO (250); +CREATE TABLE prt3_p2 PARTITION OF prt3 FOR VALUES FROM (250) TO (500); +CREATE TABLE prt3_p3 PARTITION OF prt3 FOR VALUES FROM (500) TO (600); +INSERT INTO prt3 SELECT lb + i / 2, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 99) i, (VALUES (0), (250), (500)) v(lb); +CREATE INDEX iprt3_a ON prt3(a); +ANALYZE prt3; +CREATE TABLE prt4 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt4_p1 PARTITION OF prt4 FOR VALUES FROM (0) TO (250); +CREATE TABLE prt4_p2 PARTITION OF prt4 FOR VALUES FROM (250) TO (500); +CREATE TABLE prt4_p3 PARTITION OF prt4 FOR VALUES FROM (500) TO (600); +INSERT INTO prt4 SELECT lb + i / 5, i % 25, to_char(i % 5, 'FM0000') FROM generate_series(0, 249) i, (VALUES (0), (250), (500)) v(lb); +CREATE INDEX iprt4_c_a ON prt4(c, a); +ANALYZE prt4; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt4 t1 LEFT JOIN + (SELECT t3.a, COALESCE(t3.c, t4.c) AS c FROM prt3 t3 JOIN prt1 t4 ON t3.a = t4.a + WHERE t4.b = 0) t2 ON t1.a = t2.a + WHERE t1.c = t2.c AND t2.a IS NOT NULL ORDER BY t1.a, t1.c; + QUERY PLAN +--------------------------------------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + -> Nested Loop + -> Seq Scan on prt1_p1 t4_1 + Filter: (b = 0) + -> Index Scan using prt3_p1_a_idx on prt3_p1 t3_1 + Index Cond: ((a = t4_1.a) AND (a IS NOT NULL)) + -> Index Only Scan using prt4_p1_c_a_idx on prt4_p1 t1_1 + Index Cond: ((c = ((COALESCE(t3_1.c, t4_1.c)))::text) AND (a = t3_1.a)) + -> Nested Loop + -> Nested Loop + -> Seq Scan on prt1_p2 t4_2 + Filter: (b = 0) + -> Index Scan using prt3_p2_a_idx on prt3_p2 t3_2 + Index Cond: ((a = t4_2.a) AND (a IS NOT NULL)) + -> Index Only Scan using prt4_p2_c_a_idx on prt4_p2 t1_2 + Index Cond: ((c = ((COALESCE(t3_2.c, t4_2.c)))::text) AND (a = t3_2.a)) + -> Nested Loop + -> Nested Loop + -> Seq Scan on prt1_p3 t4_3 + Filter: (b = 0) + -> Index Scan using prt3_p3_a_idx on prt3_p3 t3_3 + Index Cond: ((a = t4_3.a) AND (a IS NOT NULL)) + -> Index Only Scan using prt4_p3_c_a_idx on prt4_p3 t1_3 + Index Cond: ((c = ((COALESCE(t3_3.c, t4_3.c)))::text) AND (a = t3_3.a)) +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM prt4 t1 LEFT JOIN + (SELECT t3.a, COALESCE(t3.c, t4.c) AS c FROM prt3 t3 JOIN prt1 t4 ON t3.a = t4.a + WHERE t4.b = 0) t2 ON t1.a = t2.a + WHERE t1.c = t2.c AND t2.a IS NOT NULL ORDER BY t1.a, t1.c; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 0 | 0001 | 0 | 0001 + 250 | 0000 | 250 | 0000 + 250 | 0001 | 250 | 0001 + 500 | 0000 | 500 | 0000 + 500 | 0001 | 500 | 0001 +(6 rows) + +RESET enable_hashjoin; +RESET enable_mergejoin; -- -- partitioned by expression -- diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql index 775ab8462c1..0522f8098de 100644 --- a/src/test/regress/sql/partition_join.sql +++ b/src/test/regress/sql/partition_join.sql @@ -142,6 +142,49 @@ SELECT a, b FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) RESET enable_partitionwise_aggregate; RESET enable_hashjoin; +-- bug with PlaceHolderVars supplied as nestloop params by a child join +SET enable_hashjoin TO false; +SET enable_mergejoin TO false; + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN + (SELECT b, COALESCE(c, 'x') AS c FROM prt2 WHERE a = 0) t2 ON t1.a = t2.b + WHERE t1.c = t2.c ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN + (SELECT b, COALESCE(c, 'x') AS c FROM prt2 WHERE a = 0) t2 ON t1.a = t2.b + WHERE t1.c = t2.c ORDER BY t1.a, t2.b; + +-- same, with the PlaceHolderVar needed by a parameterized child join nested +-- inside the child join that supplies the parameter +CREATE TABLE prt3 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt3_p1 PARTITION OF prt3 FOR VALUES FROM (0) TO (250); +CREATE TABLE prt3_p2 PARTITION OF prt3 FOR VALUES FROM (250) TO (500); +CREATE TABLE prt3_p3 PARTITION OF prt3 FOR VALUES FROM (500) TO (600); +INSERT INTO prt3 SELECT lb + i / 2, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 99) i, (VALUES (0), (250), (500)) v(lb); +CREATE INDEX iprt3_a ON prt3(a); +ANALYZE prt3; + +CREATE TABLE prt4 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt4_p1 PARTITION OF prt4 FOR VALUES FROM (0) TO (250); +CREATE TABLE prt4_p2 PARTITION OF prt4 FOR VALUES FROM (250) TO (500); +CREATE TABLE prt4_p3 PARTITION OF prt4 FOR VALUES FROM (500) TO (600); +INSERT INTO prt4 SELECT lb + i / 5, i % 25, to_char(i % 5, 'FM0000') FROM generate_series(0, 249) i, (VALUES (0), (250), (500)) v(lb); +CREATE INDEX iprt4_c_a ON prt4(c, a); +ANALYZE prt4; + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt4 t1 LEFT JOIN + (SELECT t3.a, COALESCE(t3.c, t4.c) AS c FROM prt3 t3 JOIN prt1 t4 ON t3.a = t4.a + WHERE t4.b = 0) t2 ON t1.a = t2.a + WHERE t1.c = t2.c AND t2.a IS NOT NULL ORDER BY t1.a, t1.c; +SELECT t1.a, t1.c, t2.a, t2.c FROM prt4 t1 LEFT JOIN + (SELECT t3.a, COALESCE(t3.c, t4.c) AS c FROM prt3 t3 JOIN prt1 t4 ON t3.a = t4.a + WHERE t4.b = 0) t2 ON t1.a = t2.a + WHERE t1.c = t2.c AND t2.a IS NOT NULL ORDER BY t1.a, t1.c; + +RESET enable_hashjoin; +RESET enable_mergejoin; + -- -- partitioned by expression --