Skip to content

Commit 003d80f

Browse files
committed
Mark finished Plan nodes with parallel_safe flags.
We'd managed to avoid doing this so far, but it seems pretty obvious that it would be forced on us some day, and this is much the cleanest way of approaching the open problem that parallel-unsafe subplans are being transmitted to parallel workers. Anyway there's no space cost due to alignment considerations, and the time cost is pretty minimal since we're just copying the flag from the corresponding Path node. (At least in most cases ... some of the klugier spots in createplan.c have to work a bit harder.) In principle we could perhaps get rid of SubPlan.parallel_safe, but I thought it better to keep that in case there are reasons to consider a SubPlan unsafe even when its child plan is parallel-safe. This patch doesn't actually do anything with the new flags, but I thought I'd commit it separately anyway. Note: although this touches outfuncs/readfuncs, there's no need for a catversion bump because Plan trees aren't stored on disk. Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
1 parent 35b5f7b commit 003d80f

File tree

7 files changed

+39
-18
lines changed

7 files changed

+39
-18
lines changed

src/backend/nodes/copyfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ CopyPlanFields(const Plan *from, Plan *newnode)
118118
COPY_SCALAR_FIELD(plan_rows);
119119
COPY_SCALAR_FIELD(plan_width);
120120
COPY_SCALAR_FIELD(parallel_aware);
121+
COPY_SCALAR_FIELD(parallel_safe);
121122
COPY_SCALAR_FIELD(plan_node_id);
122123
COPY_NODE_FIELD(targetlist);
123124
COPY_NODE_FIELD(qual);

src/backend/nodes/outfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ _outPlanInfo(StringInfo str, const Plan *node)
275275
WRITE_FLOAT_FIELD(plan_rows, "%.0f");
276276
WRITE_INT_FIELD(plan_width);
277277
WRITE_BOOL_FIELD(parallel_aware);
278+
WRITE_BOOL_FIELD(parallel_safe);
278279
WRITE_INT_FIELD(plan_node_id);
279280
WRITE_NODE_FIELD(targetlist);
280281
WRITE_NODE_FIELD(qual);

src/backend/nodes/readfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -1480,6 +1480,7 @@ ReadCommonPlan(Plan *local_node)
14801480
READ_FLOAT_FIELD(plan_rows);
14811481
READ_INT_FIELD(plan_width);
14821482
READ_BOOL_FIELD(parallel_aware);
1483+
READ_BOOL_FIELD(parallel_safe);
14831484
READ_INT_FIELD(plan_node_id);
14841485
READ_NODE_FIELD(targetlist);
14851486
READ_NODE_FIELD(qual);

src/backend/optimizer/plan/createplan.c

+25-6
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path,
8888
int flags);
8989
static Gather *create_gather_plan(PlannerInfo *root, GatherPath *best_path);
9090
static Plan *create_projection_plan(PlannerInfo *root, ProjectionPath *best_path);
91-
static Plan *inject_projection_plan(Plan *subplan, List *tlist);
91+
static Plan *inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe);
9292
static Sort *create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags);
9393
static Group *create_group_plan(PlannerInfo *root, GroupPath *best_path);
9494
static Unique *create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path,
@@ -920,6 +920,9 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
920920
*/
921921
copy_plan_costsize(gplan, plan);
922922

923+
/* Gating quals could be unsafe, so better use the Path's safety flag */
924+
gplan->parallel_safe = path->parallel_safe;
925+
923926
return gplan;
924927
}
925928

@@ -1313,7 +1316,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
13131316
*/
13141317
if (!is_projection_capable_plan(subplan) &&
13151318
!tlist_same_exprs(newtlist, subplan->targetlist))
1316-
subplan = inject_projection_plan(subplan, newtlist);
1319+
subplan = inject_projection_plan(subplan, newtlist,
1320+
best_path->path.parallel_safe);
13171321
else
13181322
subplan->targetlist = newtlist;
13191323
}
@@ -1572,7 +1576,8 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path)
15721576
plan->total_cost = best_path->path.total_cost;
15731577
plan->plan_rows = best_path->path.rows;
15741578
plan->plan_width = best_path->path.pathtarget->width;
1575-
/* ... but be careful not to munge subplan's parallel-aware flag */
1579+
plan->parallel_safe = best_path->path.parallel_safe;
1580+
/* ... but don't change subplan's parallel_aware flag */
15761581
}
15771582
else
15781583
{
@@ -1592,9 +1597,12 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path)
15921597
* This is used in a few places where we decide on-the-fly that we need a
15931598
* projection step as part of the tree generated for some Path node.
15941599
* We should try to get rid of this in favor of doing it more honestly.
1600+
*
1601+
* One reason it's ugly is we have to be told the right parallel_safe marking
1602+
* to apply (since the tlist might be unsafe even if the child plan is safe).
15951603
*/
15961604
static Plan *
1597-
inject_projection_plan(Plan *subplan, List *tlist)
1605+
inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe)
15981606
{
15991607
Plan *plan;
16001608

@@ -1608,6 +1616,7 @@ inject_projection_plan(Plan *subplan, List *tlist)
16081616
* consistent not more so. Hence, just copy the subplan's cost.
16091617
*/
16101618
copy_plan_costsize(plan, subplan);
1619+
plan->parallel_safe = parallel_safe;
16111620

16121621
return plan;
16131622
}
@@ -1984,6 +1993,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
19841993
plan->plan_rows = 1;
19851994
plan->plan_width = mminfo->path->pathtarget->width;
19861995
plan->parallel_aware = false;
1996+
plan->parallel_safe = mminfo->path->parallel_safe;
19871997

19881998
/* Convert the plan into an InitPlan in the outer query. */
19891999
SS_make_initplan_from_plan(root, subroot, plan, mminfo->param);
@@ -2829,6 +2839,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
28292839
clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples);
28302840
plan->plan_width = 0; /* meaningless */
28312841
plan->parallel_aware = false;
2842+
plan->parallel_safe = apath->path.parallel_safe;
28322843
*qual = subquals;
28332844
*indexqual = subindexquals;
28342845
*indexECs = subindexECs;
@@ -2892,6 +2903,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
28922903
clamp_row_est(opath->bitmapselectivity * opath->path.parent->tuples);
28932904
plan->plan_width = 0; /* meaningless */
28942905
plan->parallel_aware = false;
2906+
plan->parallel_safe = opath->path.parallel_safe;
28952907
}
28962908

28972909
/*
@@ -2936,6 +2948,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
29362948
clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
29372949
plan->plan_width = 0; /* meaningless */
29382950
plan->parallel_aware = false;
2951+
plan->parallel_safe = ipath->path.parallel_safe;
29392952
*qual = get_actual_clauses(ipath->indexclauses);
29402953
*indexqual = get_actual_clauses(ipath->indexquals);
29412954
foreach(l, ipath->indexinfo->indpred)
@@ -4834,7 +4847,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
48344847
/*
48354848
* Copy cost and size info from a Path node to the Plan node created from it.
48364849
* The executor usually won't use this info, but it's needed by EXPLAIN.
4837-
* Also copy the parallel-aware flag, which the executor *will* use.
4850+
* Also copy the parallel-related flags, which the executor *will* use.
48384851
*/
48394852
static void
48404853
copy_generic_path_info(Plan *dest, Path *src)
@@ -4844,6 +4857,7 @@ copy_generic_path_info(Plan *dest, Path *src)
48444857
dest->plan_rows = src->rows;
48454858
dest->plan_width = src->pathtarget->width;
48464859
dest->parallel_aware = src->parallel_aware;
4860+
dest->parallel_safe = src->parallel_safe;
48474861
}
48484862

48494863
/*
@@ -4859,6 +4873,8 @@ copy_plan_costsize(Plan *dest, Plan *src)
48594873
dest->plan_width = src->plan_width;
48604874
/* Assume the inserted node is not parallel-aware. */
48614875
dest->parallel_aware = false;
4876+
/* Assume the inserted node is parallel-safe, if child plan is. */
4877+
dest->parallel_safe = src->parallel_safe;
48624878
}
48634879

48644880
/*
@@ -4888,6 +4904,7 @@ label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples)
48884904
plan->plan.plan_rows = lefttree->plan_rows;
48894905
plan->plan.plan_width = lefttree->plan_width;
48904906
plan->plan.parallel_aware = false;
4907+
plan->plan.parallel_safe = lefttree->parallel_safe;
48914908
}
48924909

48934910
/*
@@ -5696,7 +5713,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
56965713
{
56975714
/* copy needed so we don't modify input's tlist below */
56985715
tlist = copyObject(tlist);
5699-
lefttree = inject_projection_plan(lefttree, tlist);
5716+
lefttree = inject_projection_plan(lefttree, tlist,
5717+
lefttree->parallel_safe);
57005718
}
57015719

57025720
/* Don't bother testing is_projection_capable_plan again */
@@ -5975,6 +5993,7 @@ materialize_finished_plan(Plan *subplan)
59755993
matplan->plan_rows = subplan->plan_rows;
59765994
matplan->plan_width = subplan->plan_width;
59775995
matplan->parallel_aware = false;
5996+
matplan->parallel_safe = subplan->parallel_safe;
59785997

59795998
return matplan;
59805999
}

src/backend/optimizer/plan/planner.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,9 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
351351

352352
/*
353353
* Optionally add a Gather node for testing purposes, provided this is
354-
* actually a safe thing to do. (Note: we assume adding a Material node
355-
* above did not change the parallel safety of the plan, so we can still
356-
* rely on best_path->parallel_safe.)
354+
* actually a safe thing to do.
357355
*/
358-
if (force_parallel_mode != FORCE_PARALLEL_OFF && best_path->parallel_safe)
356+
if (force_parallel_mode != FORCE_PARALLEL_OFF && top_plan->parallel_safe)
359357
{
360358
Gather *gather = makeNode(Gather);
361359

@@ -378,6 +376,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
378376
gather->plan.plan_rows = top_plan->plan_rows;
379377
gather->plan.plan_width = top_plan->plan_width;
380378
gather->plan.parallel_aware = false;
379+
gather->plan.parallel_safe = false;
381380

382381
/* use parallel mode for parallel plans. */
383382
root->glob->parallelModeNeeded = true;

src/backend/optimizer/plan/subselect.c

+7-8
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ static Node *build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
5858
List *plan_params,
5959
SubLinkType subLinkType, int subLinkId,
6060
Node *testexpr, bool adjust_testexpr,
61-
bool unknownEqFalse, bool parallel_safe);
61+
bool unknownEqFalse);
6262
static List *generate_subquery_params(PlannerInfo *root, List *tlist,
6363
List **paramIds);
6464
static List *generate_subquery_vars(PlannerInfo *root, List *tlist,
@@ -550,8 +550,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
550550
/* And convert to SubPlan or InitPlan format. */
551551
result = build_subplan(root, plan, subroot, plan_params,
552552
subLinkType, subLinkId,
553-
testexpr, true, isTopQual,
554-
best_path->parallel_safe);
553+
testexpr, true, isTopQual);
555554

556555
/*
557556
* If it's a correlated EXISTS with an unimportant targetlist, we might be
@@ -605,8 +604,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
605604
plan_params,
606605
ANY_SUBLINK, 0,
607606
newtestexpr,
608-
false, true,
609-
best_path->parallel_safe));
607+
false, true));
610608
/* Check we got what we expected */
611609
Assert(hashplan->parParam == NIL);
612610
Assert(hashplan->useHashTable);
@@ -635,7 +633,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
635633
List *plan_params,
636634
SubLinkType subLinkType, int subLinkId,
637635
Node *testexpr, bool adjust_testexpr,
638-
bool unknownEqFalse, bool parallel_safe)
636+
bool unknownEqFalse)
639637
{
640638
Node *result;
641639
SubPlan *splan;
@@ -654,7 +652,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
654652
&splan->firstColCollation);
655653
splan->useHashTable = false;
656654
splan->unknownEqFalse = unknownEqFalse;
657-
splan->parallel_safe = parallel_safe;
655+
splan->parallel_safe = plan->parallel_safe;
658656
splan->setParam = NIL;
659657
splan->parParam = NIL;
660658
splan->args = NIL;
@@ -1218,7 +1216,8 @@ SS_process_ctes(PlannerInfo *root)
12181216

12191217
/*
12201218
* CTE scans are not considered for parallelism (cf
1221-
* set_rel_consider_parallel).
1219+
* set_rel_consider_parallel), and even if they were, initPlans aren't
1220+
* parallel-safe.
12221221
*/
12231222
splan->parallel_safe = false;
12241223
splan->setParam = NIL;

src/include/nodes/plannodes.h

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ typedef struct Plan
124124
* information needed for parallel query
125125
*/
126126
bool parallel_aware; /* engage parallel-aware logic? */
127+
bool parallel_safe; /* OK to use as part of parallel plan? */
127128

128129
/*
129130
* Common structural data for all Plan types.

0 commit comments

Comments
 (0)