diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 40eb58341c1..02dd9724924 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -2021,11 +2021,31 @@ consider_parallel_nestloop(PlannerInfo *root, JoinPathExtraData *extra) { JoinType save_jointype = jointype; + Path *inner_cheapest_total = innerrel->cheapest_total_path; + Path *matpath = NULL; ListCell *lc1; if (jointype == JOIN_UNIQUE_INNER) jointype = JOIN_INNER; + /* + * Consider materializing the cheapest inner path, unless: 1) we're doing + * JOIN_UNIQUE_INNER, because in this case we have to unique-ify the + * cheapest inner path, 2) enable_material is off, 3) the cheapest inner + * path is not parallel-safe, 4) the cheapest inner path is parameterized + * by the outer rel, or 5) the cheapest inner path materializes its output + * anyway. + */ + if (save_jointype != JOIN_UNIQUE_INNER && + enable_material && inner_cheapest_total->parallel_safe && + !PATH_PARAM_BY_REL(inner_cheapest_total, outerrel) && + !ExecMaterializesOutput(inner_cheapest_total->pathtype)) + { + matpath = (Path *) + create_material_path(innerrel, inner_cheapest_total); + Assert(matpath->parallel_safe); + } + foreach(lc1, outerrel->partial_pathlist) { Path *outerpath = (Path *) lfirst(lc1); @@ -2082,6 +2102,11 @@ consider_parallel_nestloop(PlannerInfo *root, try_partial_nestloop_path(root, joinrel, outerpath, mpath, pathkeys, jointype, extra); } + + /* Also consider materialized form of the cheapest inner path */ + if (matpath != NULL) + try_partial_nestloop_path(root, joinrel, outerpath, matpath, + pathkeys, jointype, extra); } } diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index c96285d1bb6..7487ea0b820 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -653,6 +653,36 @@ select count(*) from tenk1, tenk2 where tenk1.unique1 = tenk2.unique1; reset enable_hashjoin; reset enable_nestloop; +-- test parallel nestloop join path with materialization of the inner path +alter table tenk2 set (parallel_workers = 0); +explain (costs off) + select * from tenk1 t1, tenk2 t2 where t1.two > t2.two; + QUERY PLAN +------------------------------------------- + Gather + Workers Planned: 4 + -> Nested Loop + Join Filter: (t1.two > t2.two) + -> Parallel Seq Scan on tenk1 t1 + -> Materialize + -> Seq Scan on tenk2 t2 +(7 rows) + +-- the joinrel is not parallel-safe due to the OFFSET clause in the subquery +explain (costs off) + select * from tenk1 t1, (select * from tenk2 t2 offset 0) t2 where t1.two > t2.two; + QUERY PLAN +------------------------------------------- + Nested Loop + Join Filter: (t1.two > t2.two) + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 t1 + -> Materialize + -> Seq Scan on tenk2 t2 +(7 rows) + +alter table tenk2 reset (parallel_workers); -- test gather merge set enable_hashagg = false; explain (costs off) diff --git a/src/test/regress/sql/select_parallel.sql b/src/test/regress/sql/select_parallel.sql index 20376c03fae..9b019d31ed7 100644 --- a/src/test/regress/sql/select_parallel.sql +++ b/src/test/regress/sql/select_parallel.sql @@ -266,6 +266,16 @@ select count(*) from tenk1, tenk2 where tenk1.unique1 = tenk2.unique1; reset enable_hashjoin; reset enable_nestloop; +-- test parallel nestloop join path with materialization of the inner path +alter table tenk2 set (parallel_workers = 0); +explain (costs off) + select * from tenk1 t1, tenk2 t2 where t1.two > t2.two; + +-- the joinrel is not parallel-safe due to the OFFSET clause in the subquery +explain (costs off) + select * from tenk1 t1, (select * from tenk2 t2 offset 0) t2 where t1.two > t2.two; +alter table tenk2 reset (parallel_workers); + -- test gather merge set enable_hashagg = false;