8000 Fix parallel-safety marking when moving initplans to another node. · postgres/postgres@60c8aea · GitHub
[go: up one dir, main page]

Skip to content

Commit 60c8aea

Browse files
committed
Fix parallel-safety marking when moving initplans to another node.
Our policy since commit ab77a5a has been that a plan node having any initplans is automatically not parallel-safe. (This could be relaxed, but not today.) clean_up_removed_plan_level neglected this, and could attach initplans to a parallel-safe child plan node without clearing the plan's parallel-safe flag. That could lead to "subplan was not initialized" errors at runtime, in case an initplan referenced another one and only the referencing one got transmitted to parallel workers. The fix in clean_up_removed_plan_level is trivial enough. materialize_finished_plan also moves initplans from one node to another, but it's okay because it already copies the source node's parallel_safe flag. The other place that does this kind of thing is standard_planner's hack to inject a top-level Gather when debug_parallel_query is active. But that's actually dead code given that we're correctly enforcing the "initplans aren't parallel safe" rule, so just replace it with an Assert that there are no initplans. Also improve some related comments. Normally we'd add a regression test case for this sort of bug. The mistake itself is already reached by existing tests, but there is accidentally no visible problem. The only known test case that creates an actual failure seems too indirect and fragile to justify keeping it as a regression test (not least because it fails to fail in v11, though the bug is clearly present there too). Per report from Justin Pryzby. Back-patch to all supported branches. Discussion: https://postgr.es/m/ZDVt6MaNWkRDO1LQ@telsasoft.com
1 parent 52d83e9 commit 60c8aea

File tree

4 files changed

+17
-10
lines changed

4 files changed

+17
-10
lines changed

src/backend/optimizer/plan/planner.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,10 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
430430
Gather *gather = makeNode(Gather);
431431

432432
/*
433-
* If there are any initPlans attached to the formerly-top plan node,
434-
* move them up to the Gather node; same as we do for Material node in
435-
* materialize_finished_plan.
433+
* Top plan must not have any initPlans, else it shouldn't have been
434+
* marked parallel-safe.
436435
*/
437-
gather->plan.initPlan = top_plan->initPlan;
438-
top_plan->initPlan = NIL;
436+
Assert(top_plan->initPlan == NIL);
439437

440438
gather->plan.targetlist = top_plan->targetlist;
441439
gather->plan.qual = NIL;

src/backend/optimizer/plan/setrefs.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,14 @@ set_subqueryscan_references(PlannerInfo *root,
11021102

11031103
result = plan->subplan;
11041104

1105-
/* We have to be sure we don't lose any initplans */
1105+
/*
1106+
* We have to be sure we don't lose any initplans, so move any that
1107+
* were attached to the parent plan to the child. If we do move any,
1108+
* the child is no longer parallel-safe.
1109+
*/
1110+
if (plan->scan.plan.initPlan)
1111+
result->parallel_safe = false;
1112+
11061113
result->initPlan = list_concat(plan->scan.plan.initPlan,
11071114
result->initPlan);
11081115

src/backend/optimizer/plan/subselect.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,7 +1879,7 @@ SS_identify_outer_params(PlannerInfo *root)
18791879
* This is separate from SS_attach_initplans because we might conditionally
18801880
* create more initPlans during create_plan(), depending on which Path we
18811881
* select. However, Paths that would generate such initPlans are expected
1882-
* to have included their cost already.
1882+
* to have included their cost and parallel-safety effects already.
18831883
*/
18841884
void
18851885
SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
@@ -1935,8 +1935,10 @@ SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
19351935
* (In principle the initPlans could go in any node at or above where they're
19361936
* referenced; but there seems no reason to put them any lower than the
19371937
* topmost node, so we don't bother to track exactly where they came from.)
1938-
* We do not touch the plan node's cost; the initplans should have been
1939-
* accounted for in path costing.
1938+
*
1939+
* We do not touch the plan node's cost or parallel_safe flag. The initplans
1940+
* must have been accounted for in SS_charge_for_initplans, or by any later
1941+
* code that adds initplans via SS_make_initplan_from_plan.
19401942
*/
19411943
void
19421944
SS_attach_initplans(PlannerInfo *root, Plan *plan)

src/backend/optimizer/util/pathnode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3093,7 +3093,7 @@ create_minmaxagg_path(PlannerInfo *root,
30933093
/* For now, assume we are above any joins, so no parameterization */
30943094
pathnode->path.param_info = NULL;
30953095
pathnode->path.parallel_aware = false;
3096-
/* A MinMaxAggPath implies use of subplans, so cannot be parallel-safe */
3096+
/* A MinMaxAggPath implies use of initplans, so cannot be parallel-safe */
30973097
pathnode->path.parallel_safe = false;
30983098
pathnode->path.parallel_workers = 0;
30993099
/* Result is one unordered row */

0 commit comments

Comments
 (0)
0