Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 54f5c51

Browse files
committed
Try again to fix the way the scanjoin_target is used with partial paths.
Commit 04ae11f removed some broken code to apply the scan/join target to partial paths, but its theory that this processing step is totally unnecessary turns out to be wrong. Put similar code back again, but this time, check for parallel-safety and avoid in-place modifications to paths that may already have been used as part of some other path. (This is not an entirely elegant solution to this problem; it might be better, for example, to postpone generate_gather_paths for the topmost scan/join rel until after the scan/join target has been applied. But this is not the time for such redesign work.) Amit Kapila and Robert Haas
1 parent ede62e5 commit 54f5c51

File tree

7 files changed

+133
-10
lines changed

7 files changed

+133
-10
lines changed

src/backend/optimizer/plan/planagg.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
465465
* cheapest path.)
466466
*/
467467
sorted_path = apply_projection_to_path(subroot, final_rel, sorted_path,
468-
create_pathtarget(subroot, tlist));
468+
create_pathtarget(subroot, tlist),
469+
false);
469470

470471
/*
471472
* Determine cost to get just the first row of the presorted path.

src/backend/optimizer/plan/planner.c

+78-3
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
15001500
PathTarget *grouping_target;
15011501
PathTarget *scanjoin_target;
15021502
bool have_grouping;
1503+
bool scanjoin_target_parallel_safe = false;
15031504
WindowFuncLists *wflists = NULL;
15041505
List *activeWindows = NIL;
15051506
List *rollup_lists = NIL;
@@ -1730,7 +1731,16 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
17301731
scanjoin_target = grouping_target;
17311732

17321733
/*
1733-
* Forcibly apply that target to all the Paths for the scan/join rel.
1734+
* Check whether scan/join target is parallel safe ... unless there
1735+
* are no partial paths, in which case we don't care.
1736+
*/
1737+
if (current_rel->partial_pathlist &&
1738+
!has_parallel_hazard((Node *) scanjoin_target->exprs, false))
1739+
scanjoin_target_parallel_safe = true;
1740+
1741+
/*
1742+
* Forcibly apply scan/join target to all the Paths for the scan/join
1743+
* rel.
17341744
*
17351745
* In principle we should re-run set_cheapest() here to identify the
17361746
* cheapest path, but it seems unlikely that adding the same tlist
@@ -1746,7 +1756,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
17461756

17471757
Assert(subpath->param_info == NULL);
17481758
path = apply_projection_to_path(root, current_rel,
1749-
subpath, scanjoin_target);
1759+
subpath, scanjoin_target,
1760+
scanjoin_target_parallel_safe);
17501761
/* If we had to add a Result, path is different from subpath */
17511762
if (path != subpath)
17521763
{
@@ -1758,6 +1769,70 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
17581769
}
17591770
}
17601771

1772+
/*
1773+
* Upper planning steps which make use of the top scan/join rel's
1774+
* partial pathlist will expect partial paths for that rel to produce
1775+
* the same output as complete paths ... and we just changed the
1776+
* output for the complete paths, so we'll need to do the same thing
1777+
* for partial paths.
1778+
*/
1779+
if (scanjoin_target_parallel_safe)
1780+
{
1781+
/*
1782+
* Apply the scan/join target to each partial path. Otherwise,
1783+
* anything that attempts to use the partial paths for further
1784+
* upper planning may go wrong.
1785+
*/
1786+
foreach(lc, current_rel->partial_pathlist)
1787+
{
1788+
Path *subpath = (Path *) lfirst(lc);
1789+
Path *newpath;
1790+
1791+
/*
1792+
* We can't use apply_projection_to_path() here, because there
1793+
* could already be pointers to these paths, and therefore we
1794+
* cannot modify them in place. Instead, we must use
1795+
* create_projection_path(). The good news is this won't
1796+
* actually insert a Result node into the final plan unless
1797+
* it's needed, but the bad news is that it will charge for
1798+
* the node whether it's needed or not. Therefore, if the
1799+
* target list is already what we need it to be, just leave
1800+
* this partial path alone.
1801+
*/
1802+
if (equal(scanjoin_target->exprs, subpath->pathtarget->exprs))
1803+
continue;
1804+
1805+
Assert(subpath->param_info == NULL);
1806+
newpath = (Path *) create_projection_path(root,
1807+
current_rel,
1808+
subpath,
1809+
scanjoin_target);
1810+
if (is_projection_capable_path(subpath))
1811+
{
1812+
/*
1813+
* Since the target lists differ, a projection path is
1814+
* essential, but it will disappear at plan creation time
1815+
* because the subpath is projection-capable. So avoid
1816+
* charging anything for the disappearing node.
1817+
*/
1818+
newpath->startup_cost = subpath->startup_cost;
1819+
newpath->total_cost = subpath->total_cost;
1820+
}
1821+
1822+
lfirst(lc) = newpath;
1823+
}
1824+
}
1825+
else
1826+
{
1827+
/*
1828+
* In the unfortunate event that scanjoin_target is not
1829+
* parallel-safe, we can't apply it to the partial paths; in that
1830+
* case, we'll need to forget about the partial paths, which
1831+
* aren't valid input for upper planning steps.
1832+
*/
1833+
current_rel->partial_pathlist = NIL;
1834+
}
1835+
17611836
/*
17621837
* Save the various upper-rel PathTargets we just computed into
17631838
* root->upper_targets[]. The core code doesn't use this, but it
@@ -4153,7 +4228,7 @@ create_ordered_paths(PlannerInfo *root,
41534228
/* Add projection step if needed */
41544229
if (path->pathtarget != target)
41554230
path = apply_projection_to_path(root, ordered_rel,
4156-
path, target);
4231+
path, target, false);
41574232

41584233
add_path(ordered_rel, path);
41594234
}

src/backend/optimizer/prep/prepunion.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
325325
refnames_tlist);
326326

327327
path = apply_projection_to_path(root, rel, path,
328-
create_pathtarget(root, tlist));
328+
create_pathtarget(root, tlist),
329+
false);
329330

330331
/* Return the fully-fledged tlist to caller, too */
331332
*pTargetList = tlist;
@@ -394,7 +395,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
394395
path->parent,
395396
path,
396397
create_pathtarget(root,
397-
*pTargetList));
398+
*pTargetList),
399+
false);
398400
}
399401
return path;
400402
}

src/backend/optimizer/util/pathnode.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -2217,12 +2217,14 @@ create_projection_path(PlannerInfo *root,
22172217
* 'rel' is the parent relation associated with the result
22182218
* 'path' is the path representing the source of data
22192219
* 'target' is the PathTarget to be computed
2220+
* 'target_parallel' indicates that target expressions are all parallel-safe
22202221
*/
22212222
Path *
22222223
apply_projection_to_path(PlannerInfo *root,
22232224
RelOptInfo *rel,
22242225
Path *path,
2225-
PathTarget *target)
2226+
PathTarget *target,
2227+
bool target_parallel)
22262228
{
22272229
QualCost oldcost;
22282230

@@ -2248,8 +2250,7 @@ apply_projection_to_path(PlannerInfo *root,
22482250
* project. But if there is something that is not parallel-safe in the
22492251
* target expressions, then we can't.
22502252
*/
2251-
if (IsA(path, GatherPath) &&
2252-
!has_parallel_hazard((Node *) target->exprs, false))
2253+
if (IsA(path, GatherPath) &&target_parallel)
22532254
{
22542255
GatherPath *gpath = (GatherPath *) path;
22552256

src/include/optimizer/pathnode.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ extern ProjectionPath *create_projection_path(PlannerInfo *root,
143143
extern Path *apply_projection_to_path(PlannerInfo *root,
144144
RelOptInfo *rel,
145145
Path *path,
146-
PathTarget *target);
146+
PathTarget *target,
147+
bool target_parallel);
147148
extern SortPath *create_sort_path(PlannerInfo *root,
148149
RelOptInfo *rel,
149150
Path *subpath,

src/test/regress/expected/select_parallel.out

+32
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,38 @@ select parallel_restricted(unique1) from tenk1
5151
Filter: (tenk1.stringu1 = 'GRAAAA'::name)
5252
(9 rows)
5353

54+
-- test parallel plan when group by expression is in target list.
55+
explain (costs off)
56+
select length(stringu1) from tenk1 group by length(stringu1);
57+
QUERY PLAN
58+
---------------------------------------------------
59+
Finalize HashAggregate
60+
Group Key: (length((stringu1)::text))
61+
-> Gather
62+
Workers Planned: 4
63+
-> Partial HashAggregate
64+
Group Key: length((stringu1)::text)
65+
-> Parallel Seq Scan on tenk1
66+
(7 rows)
67+
68+
select length(stringu1) from tenk1 group by length(stringu1);
69+
length
70+
--------
71+
6
72+
(1 row)
73+
74+
-- test that parallel plan for aggregates is not selected when
75+
-- target list contains parallel restricted clause.
76+
explain (costs off)
77+
select sum(parallel_restricted(unique1)) from tenk1
78+
group by(parallel_restricted(unique1));
79+
QUERY PLAN
80+
----------------------------------------------------
81+
HashAggregate
82+
Group Key: parallel_restricted(unique1)
83+
-> Index Only Scan using tenk1_unique1 on tenk1
84+
(3 rows)
85+
5486
set force_parallel_mode=1;
5587
explain (costs off)
5688
select stringu1::int2 from tenk1 where unique1 = 1;

src/test/regress/sql/select_parallel.sql

+11
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ explain (verbose, costs off)
2424
select parallel_restricted(unique1) from tenk1
2525
where stringu1 = 'GRAAAA' order by 1;
2626

27+
-- test parallel plan when group by expression is in target list.
28+
explain (costs off)
29+
select length(stringu1) from tenk1 group by length(stringu1);
30+
select length(stringu1) from tenk1 group by length(stringu1);
31+
32+
-- test that parallel plan for aggregates is not selected when
33+
-- target list contains parallel restricted clause.
34+
explain (costs off)
35+
select sum(parallel_restricted(unique1)) from tenk1
36+
group by(parallel_restricted(unique1));
37+
2738
set force_parallel_mode=1;
2839

2940
explain (costs off)

0 commit comments

Comments
 (0)