diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index a10bb89dcb..a9d3c22d7c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -430,12 +430,10 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) Gather *gather = makeNode(Gather); /* - * If there are any initPlans attached to the formerly-top plan node, - * move them up to the Gather node; same as we do for Material node in - * materialize_finished_plan. + * Top plan must not have any initPlans, else it shouldn't have been + * marked parallel-safe. */ - gather->plan.initPlan = top_plan->initPlan; - top_plan->initPlan = NIL; + Assert(top_plan->initPlan == NIL); gather->plan.targetlist = top_plan->targetlist; gather->plan.qual = NIL; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index a6ad28570a..15ffaa3707 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -1102,7 +1102,14 @@ set_subqueryscan_references(PlannerInfo *root, result = plan->subplan; - /* We have to be sure we don't lose any initplans */ + /* + * We have to be sure we don't lose any initplans, so move any that + * were attached to the parent plan to the child. If we do move any, + * the child is no longer parallel-safe. + */ + if (plan->scan.plan.initPlan) + result->parallel_safe = false; + result->initPlan = list_concat(plan->scan.plan.initPlan, result->initPlan); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index b22519e69e..adada2a1b4 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1879,7 +1879,7 @@ SS_identify_outer_params(PlannerInfo *root) * This is separate from SS_attach_initplans because we might conditionally * create more initPlans during create_plan(), depending on which Path we * select. However, Paths that would generate such initPlans are expected - * to have included their cost already. + * to have included their cost and parallel-safety effects already. */ void SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel) @@ -1935,8 +1935,10 @@ SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel) * (In principle the initPlans could go in any node at or above where they're * referenced; but there seems no reason to put them any lower than the * topmost node, so we don't bother to track exactly where they came from.) - * We do not touch the plan node's cost; the initplans should have been - * accounted for in path costing. + * + * We do not touch the plan node's cost or parallel_safe flag. The initplans + * must have been accounted for in SS_charge_for_initplans, or by any later + * code that adds initplans via SS_make_initplan_from_plan. */ void SS_attach_initplans(PlannerInfo *root, Plan *plan) diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index aa8f2de72a..24a216e754 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3093,7 +3093,7 @@ create_minmaxagg_path(PlannerInfo *root, /* For now, assume we are above any joins, so no parameterization */ pathnode->path.param_info = NULL; pathnode->path.parallel_aware = false; - /* A MinMaxAggPath implies use of subplans, so cannot be parallel-safe */ + /* A MinMaxAggPath implies use of initplans, so cannot be parallel-safe */ pathnode->path.parallel_safe = false; pathnode->path.parallel_workers = 0; /* Result is one unordered row */