Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite08d74c

Browse files
committed
Allow plan nodes with initPlans to be considered parallel-safe.
If the plan itself is parallel-safe, and the initPlans are too,there's no reason anymore to prevent the plan from being markedparallel-safe. That restriction (dating to commitab77a5a) wasreally a special case of the fact that we couldn't transmit subplansto parallel workers at all. We fixed that in commit5e6d8d2 andfollow-ons, but this case never got addressed.We still forbid attaching initPlans to a Gather node that'sinserted pursuant to debug_parallel_query = regress. That's because,when we hide the Gather from EXPLAIN output, we'd hide the initPlanstoo, causing cosmetic regression diffs. It seems inadvisable tokluge EXPLAIN to the extent required to make the output look thesame, so just don't do it in that case.Along the way, this also takes care of some sloppiness about updatingpath costs to match when we move initplans from one place to anotherduring createplan.c and setrefs.c. Since all the planning decisionsare already made by that point, this is just cosmetic; but it seemsgood to keep EXPLAIN output consistent with where the initplans are.The diff in query_planner() might be worth remarking on. I found thatone because after fixing things to allow parallel-safe initplans, onepartition_prune test case changed plans (as shown in the patch) ---but only when debug_parallel_query was active. The reason proved tobe that we only bothered to mark Result nodes as potentiallyparallel-safe when debug_parallel_query is on. This neglects the factthat parallel-safety may be of interest for a sub-query even thoughthe Result itself doesn't parallelize.Discussion:https://postgr.es/m/1129530.1681317832@sss.pgh.pa.us
1 parentd0d4404 commite08d74c

File tree

8 files changed

+169
-71
lines changed

8 files changed

+169
-71
lines changed

‎src/backend/optimizer/plan/createplan.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6481,27 +6481,34 @@ materialize_finished_plan(Plan *subplan)
64816481
{
64826482
Plan*matplan;
64836483
Pathmatpath;/* dummy for result of cost_material */
6484+
Costinitplan_cost;
6485+
boolunsafe_initplans;
64846486

64856487
matplan= (Plan*)make_material(subplan);
64866488

64876489
/*
64886490
* XXX horrid kluge: if there are any initPlans attached to the subplan,
64896491
* move them up to the Material node, which is now effectively the top
64906492
* plan node in its query level. This prevents failure in
6491-
* SS_finalize_plan(), which see for comments. We don't bother adjusting
6492-
* the subplan's cost estimate for this.
6493+
* SS_finalize_plan(), which see for comments.
64936494
*/
64946495
matplan->initPlan=subplan->initPlan;
64956496
subplan->initPlan=NIL;
64966497

6498+
/* Move the initplans' cost delta, as well */
6499+
SS_compute_initplan_cost(matplan->initPlan,
6500+
&initplan_cost,&unsafe_initplans);
6501+
subplan->startup_cost-=initplan_cost;
6502+
subplan->total_cost-=initplan_cost;
6503+
64976504
/* Set cost data */
64986505
cost_material(&matpath,
64996506
subplan->startup_cost,
65006507
subplan->total_cost,
65016508
subplan->plan_rows,
65026509
subplan->plan_width);
6503-
matplan->startup_cost=matpath.startup_cost;
6504-
matplan->total_cost=matpath.total_cost;
6510+
matplan->startup_cost=matpath.startup_cost+initplan_cost;
6511+
matplan->total_cost=matpath.total_cost+initplan_cost;
65056512
matplan->plan_rows=subplan->plan_rows;
65066513
matplan->plan_width=subplan->plan_width;
65076514
matplan->parallel_aware= false;

‎src/backend/optimizer/plan/planmain.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,17 @@ query_planner(PlannerInfo *root,
112112
* quals are parallel-restricted. (We need not check
113113
* final_rel->reltarget because it's empty at this point.
114114
* Anything parallel-restricted in the query tlist will be
115-
* dealt with later.) This is normally pretty silly, because
116-
* a Result-only plan would never be interesting to
117-
* parallelize. However, if debug_parallel_query is on, then
118-
* we want to execute the Result in a parallel worker if
119-
* possible, so we must do this.
115+
* dealt with later.) We should always do this in a subquery,
116+
* since it might be useful to use the subquery in parallel
117+
* paths in the parent level. At top level this is normally
118+
* not worth the cycles, because a Result-only plan would
119+
* never be interesting to parallelize. However, if
120+
* debug_parallel_query is on, then we want to execute the
121+
* Result in a parallel worker if possible, so we must check.
120122
*/
121123
if (root->glob->parallelModeOK&&
122-
debug_parallel_query!=DEBUG_PARALLEL_OFF)
124+
(root->query_level>1||
125+
debug_parallel_query!=DEBUG_PARALLEL_OFF))
123126
final_rel->consider_parallel=
124127
is_parallel_safe(root,parse->jointree->quals);
125128

‎src/backend/optimizer/plan/planner.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -432,16 +432,23 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
432432
/*
433433
* Optionally add a Gather node for testing purposes, provided this is
434434
* actually a safe thing to do.
435-
*/
436-
if (debug_parallel_query!=DEBUG_PARALLEL_OFF&&top_plan->parallel_safe)
435+
*
436+
* We can add Gather even when top_plan has parallel-safe initPlans, but
437+
* then we have to move the initPlans to the Gather node because of
438+
* SS_finalize_plan's limitations. That would cause cosmetic breakage of
439+
* regression tests when debug_parallel_query = regress, because initPlans
440+
* that would normally appear on the top_plan move to the Gather, causing
441+
* them to disappear from EXPLAIN output. That doesn't seem worth kluging
442+
* EXPLAIN to hide, so skip it when debug_parallel_query = regress.
443+
*/
444+
if (debug_parallel_query!=DEBUG_PARALLEL_OFF&&
445+
top_plan->parallel_safe&&
446+
(top_plan->initPlan==NIL||
447+
debug_parallel_query!=DEBUG_PARALLEL_REGRESS))
437448
{
438449
Gather*gather=makeNode(Gather);
439-
440-
/*
441-
* Top plan must not have any initPlans, else it shouldn't have been
442-
* marked parallel-safe.
443-
*/
444-
Assert(top_plan->initPlan==NIL);
450+
Costinitplan_cost;
451+
boolunsafe_initplans;
445452

446453
gather->plan.targetlist=top_plan->targetlist;
447454
gather->plan.qual=NIL;
@@ -451,6 +458,10 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
451458
gather->single_copy= true;
452459
gather->invisible= (debug_parallel_query==DEBUG_PARALLEL_REGRESS);
453460

461+
/* Transfer any initPlans to the new top node */
462+
gather->plan.initPlan=top_plan->initPlan;
463+
top_plan->initPlan=NIL;
464+
454465
/*
455466
* Since this Gather has no parallel-aware descendants to signal to,
456467
* we don't need a rescan Param.
@@ -470,6 +481,15 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
470481
gather->plan.parallel_aware= false;
471482
gather->plan.parallel_safe= false;
472483

484+
/*
485+
* Delete the initplans' cost from top_plan. We needn't add it to the
486+
* Gather node, since the above coding already included it there.
487+
*/
488+
SS_compute_initplan_cost(gather->plan.initPlan,
489+
&initplan_cost,&unsafe_initplans);
490+
top_plan->startup_cost-=initplan_cost;
491+
top_plan->total_cost-=initplan_cost;
492+
473493
/* use parallel mode for parallel plans. */
474494
root->glob->parallelModeNeeded= true;
475495

‎src/backend/optimizer/plan/setrefs.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include"optimizer/pathnode.h"
2424
#include"optimizer/planmain.h"
2525
#include"optimizer/planner.h"
26+
#include"optimizer/subselect.h"
2627
#include"optimizer/tlist.h"
2728
#include"parser/parse_relation.h"
2829
#include"tcop/utility.h"
@@ -1519,19 +1520,30 @@ clean_up_removed_plan_level(Plan *parent, Plan *child)
15191520
{
15201521
/*
15211522
* We have to be sure we don't lose any initplans, so move any that were
1522-
* attached to the parent plan to the child. If we do move any, the child
1523-
* is no longer parallel-safe.
1523+
* attached to the parent plan to the child. If any are parallel-unsafe,
1524+
* the child is no longer parallel-safe. As a cosmetic matter, also add
1525+
* the initplans' run costs to the child's costs.
15241526
*/
15251527
if (parent->initPlan)
1526-
child->parallel_safe= false;
1528+
{
1529+
Costinitplan_cost;
1530+
boolunsafe_initplans;
15271531

1528-
/*
1529-
* Attach plans this way so that parent's initplans are processed before
1530-
* any pre-existing initplans of the child. Probably doesn't matter, but
1531-
* let's preserve the ordering just in case.
1532-
*/
1533-
child->initPlan=list_concat(parent->initPlan,
1534-
child->initPlan);
1532+
SS_compute_initplan_cost(parent->initPlan,
1533+
&initplan_cost,&unsafe_initplans);
1534+
child->startup_cost+=initplan_cost;
1535+
child->total_cost+=initplan_cost;
1536+
if (unsafe_initplans)
1537+
child->parallel_safe= false;
1538+
1539+
/*
1540+
* Attach plans this way so that parent's initplans are processed
1541+
* before any pre-existing initplans of the child. Probably doesn't
1542+
* matter, but let's preserve the ordering just in case.
1543+
*/
1544+
child->initPlan=list_concat(parent->initPlan,
1545+
child->initPlan);
1546+
}
15351547

15361548
/*
15371549
* We also have to transfer the parent's column labeling info into the

‎src/backend/optimizer/plan/subselect.c

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,8 +1016,7 @@ SS_process_ctes(PlannerInfo *root)
10161016

10171017
/*
10181018
* CTE scans are not considered for parallelism (cf
1019-
* set_rel_consider_parallel), and even if they were, initPlans aren't
1020-
* parallel-safe.
1019+
* set_rel_consider_parallel).
10211020
*/
10221021
splan->parallel_safe= false;
10231022
splan->setParam=NIL;
@@ -2120,8 +2119,8 @@ SS_identify_outer_params(PlannerInfo *root)
21202119
* If any initPlans have been created in the current query level, they will
21212120
* get attached to the Plan tree created from whichever Path we select from
21222121
* the given rel. Increment all that rel's Paths' costs to account for them,
2123-
* andmake surethepaths get marked asparallel-unsafe,since we can't
2124-
*currently transmit initPlans toparallel workers.
2122+
* andif any oftheinitPlans areparallel-unsafe,mark all the rel's Paths
2123+
* parallel-unsafe as well.
21252124
*
21262125
* This is separate from SS_attach_initplans because we might conditionally
21272126
* create more initPlans during create_plan(), depending on which Path we
@@ -2132,6 +2131,7 @@ void
21322131
SS_charge_for_initplans(PlannerInfo*root,RelOptInfo*final_rel)
21332132
{
21342133
Costinitplan_cost;
2134+
boolunsafe_initplans;
21352135
ListCell*lc;
21362136

21372137
/* Nothing to do if no initPlans */
@@ -2140,17 +2140,10 @@ SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
21402140

21412141
/*
21422142
* Compute the cost increment just once, since it will be the same for all
2143-
* Paths. We assume each initPlan gets run once during top plan startup.
2144-
* This is a conservative overestimate, since in fact an initPlan might be
2145-
* executed later than plan startup, or even not at all.
2143+
* Paths. Also check for parallel-unsafe initPlans.
21462144
*/
2147-
initplan_cost=0;
2148-
foreach(lc,root->init_plans)
2149-
{
2150-
SubPlan*initsubplan= (SubPlan*)lfirst(lc);
2151-
2152-
initplan_cost+=initsubplan->startup_cost+initsubplan->per_call_cost;
2153-
}
2145+
SS_compute_initplan_cost(root->init_plans,
2146+
&initplan_cost,&unsafe_initplans);
21542147

21552148
/*
21562149
* Now adjust the costs and parallel_safe flags.
@@ -2161,19 +2154,71 @@ SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
21612154

21622155
path->startup_cost+=initplan_cost;
21632156
path->total_cost+=initplan_cost;
2164-
path->parallel_safe= false;
2157+
if (unsafe_initplans)
2158+
path->parallel_safe= false;
21652159
}
21662160

21672161
/*
2168-
*Forget about anypartial paths and clear consider_parallel, too;
2169-
*they're not usable if we attached an initPlan.
2162+
*Adjustpartial paths' costs too, or forget them entirely if we must
2163+
*consider the rel parallel-unsafe.
21702164
*/
2171-
final_rel->partial_pathlist=NIL;
2172-
final_rel->consider_parallel= false;
2165+
if (unsafe_initplans)
2166+
{
2167+
final_rel->partial_pathlist=NIL;
2168+
final_rel->consider_parallel= false;
2169+
}
2170+
else
2171+
{
2172+
foreach(lc,final_rel->partial_pathlist)
2173+
{
2174+
Path*path= (Path*)lfirst(lc);
2175+
2176+
path->startup_cost+=initplan_cost;
2177+
path->total_cost+=initplan_cost;
2178+
}
2179+
}
21732180

21742181
/* We needn't do set_cheapest() here, caller will do it */
21752182
}
21762183

2184+
/*
2185+
* SS_compute_initplan_cost - count up the cost delta for some initplans
2186+
*
2187+
* The total cost returned in *initplan_cost_p should be added to both the
2188+
* startup and total costs of the plan node the initplans get attached to.
2189+
* We also report whether any of the initplans are not parallel-safe.
2190+
*
2191+
* The primary user of this is SS_charge_for_initplans, but it's also
2192+
* used in adjusting costs when we move initplans to another plan node.
2193+
*/
2194+
void
2195+
SS_compute_initplan_cost(List*init_plans,
2196+
Cost*initplan_cost_p,
2197+
bool*unsafe_initplans_p)
2198+
{
2199+
Costinitplan_cost;
2200+
boolunsafe_initplans;
2201+
ListCell*lc;
2202+
2203+
/*
2204+
* We assume each initPlan gets run once during top plan startup. This is
2205+
* a conservative overestimate, since in fact an initPlan might be
2206+
* executed later than plan startup, or even not at all.
2207+
*/
2208+
initplan_cost=0;
2209+
unsafe_initplans= false;
2210+
foreach(lc,init_plans)
2211+
{
2212+
SubPlan*initsubplan=lfirst_node(SubPlan,lc);
2213+
2214+
initplan_cost+=initsubplan->startup_cost+initsubplan->per_call_cost;
2215+
if (!initsubplan->parallel_safe)
2216+
unsafe_initplans= true;
2217+
}
2218+
*initplan_cost_p=initplan_cost;
2219+
*unsafe_initplans_p=unsafe_initplans;
2220+
}
2221+
21772222
/*
21782223
* SS_attach_initplans - attach initplans to topmost plan node
21792224
*
@@ -2990,6 +3035,7 @@ SS_make_initplan_from_plan(PlannerInfo *root,
29903035
node->plan_id,prm->paramid);
29913036
get_first_col_type(plan,&node->firstColType,&node->firstColTypmod,
29923037
&node->firstColCollation);
3038+
node->parallel_safe=plan->parallel_safe;
29933039
node->setParam=list_make1_int(prm->paramid);
29943040

29953041
root->init_plans=lappend(root->init_plans,node);

‎src/backend/optimizer/util/pathnode.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3348,8 +3348,7 @@ create_minmaxagg_path(PlannerInfo *root,
33483348
/* For now, assume we are above any joins, so no parameterization */
33493349
pathnode->path.param_info=NULL;
33503350
pathnode->path.parallel_aware= false;
3351-
/* A MinMaxAggPath implies use of initplans, so cannot be parallel-safe */
3352-
pathnode->path.parallel_safe= false;
3351+
pathnode->path.parallel_safe= true;/* might change below */
33533352
pathnode->path.parallel_workers=0;
33543353
/* Result is one unordered row */
33553354
pathnode->path.rows=1;
@@ -3358,13 +3357,15 @@ create_minmaxagg_path(PlannerInfo *root,
33583357
pathnode->mmaggregates=mmaggregates;
33593358
pathnode->quals=quals;
33603359

3361-
/* Calculate cost of all the initplans ... */
3360+
/* Calculate cost of all the initplans, and check parallel safety */
33623361
initplan_cost=0;
33633362
foreach(lc,mmaggregates)
33643363
{
33653364
MinMaxAggInfo*mminfo= (MinMaxAggInfo*)lfirst(lc);
33663365

33673366
initplan_cost+=mminfo->pathcost;
3367+
if (!mminfo->path->parallel_safe)
3368+
pathnode->path.parallel_safe= false;
33683369
}
33693370

33703371
/* add tlist eval cost for each output row, plus cpu_tuple_cost */
@@ -3385,6 +3386,17 @@ create_minmaxagg_path(PlannerInfo *root,
33853386
pathnode->path.total_cost+=qual_cost.startup+qual_cost.per_tuple;
33863387
}
33873388

3389+
/*
3390+
* If the initplans were all parallel-safe, also check safety of the
3391+
* target and quals. (The Result node itself isn't parallelizable, but if
3392+
* we are in a subquery then it can be useful for the outer query to know
3393+
* that this one is parallel-safe.)
3394+
*/
3395+
if (pathnode->path.parallel_safe)
3396+
pathnode->path.parallel_safe=
3397+
is_parallel_safe(root, (Node*)target->exprs)&&
3398+
is_parallel_safe(root, (Node*)quals);
3399+
33883400
returnpathnode;
33893401
}
33903402

‎src/include/optimizer/subselect.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr);
2828
externNode*SS_process_sublinks(PlannerInfo*root,Node*expr,boolisQual);
2929
externvoidSS_identify_outer_params(PlannerInfo*root);
3030
externvoidSS_charge_for_initplans(PlannerInfo*root,RelOptInfo*final_rel);
31+
externvoidSS_compute_initplan_cost(List*init_plans,
32+
Cost*initplan_cost_p,
33+
bool*unsafe_initplans_p);
3134
externvoidSS_attach_initplans(PlannerInfo*root,Plan*plan);
3235
externvoidSS_finalize_plan(PlannerInfo*root,Plan*plan);
3336
externParam*SS_make_initplan_output_param(PlannerInfo*root,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp