diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index b93b4fc..0a3e6d6 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -569,7 +569,7 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel, if (proparallel != PROPARALLEL_SAFE) return; - if (!is_parallel_safe(root, (Node *) rte->tablesample->args)) + if (!is_parallel_safe(root, (Node *) rte->tablesample->args, NIL)) return; } @@ -622,7 +622,7 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel, case RTE_FUNCTION: /* Check for parallel-restricted functions. */ - if (!is_parallel_safe(root, (Node *) rte->functions)) + if (!is_parallel_safe(root, (Node *) rte->functions, NIL)) return; break; @@ -632,7 +632,7 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel, case RTE_VALUES: /* Check for parallel-restricted functions. */ - if (!is_parallel_safe(root, (Node *) rte->values_lists)) + if (!is_parallel_safe(root, (Node *) rte->values_lists, NIL)) return; break; @@ -664,14 +664,14 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel, * outer join clauses work correctly. It would likely break equivalence * classes, too. */ - if (!is_parallel_safe(root, (Node *) rel->baserestrictinfo)) + if (!is_parallel_safe(root, (Node *) rel->baserestrictinfo, NIL)) return; /* * Likewise, if the relation's outputs are not parallel-safe, give up. * (Usually, they're just Vars, but sometimes they're not.) */ - if (!is_parallel_safe(root, (Node *) rel->reltarget->exprs)) + if (!is_parallel_safe(root, (Node *) rel->reltarget->exprs, NIL)) return; /* We have a winner. */ diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index ef0de3f..a8d10e0 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -77,7 +77,7 @@ query_planner(PlannerInfo *root, List *tlist, */ if (root->glob->parallelModeOK) final_rel->consider_parallel = - is_parallel_safe(root, parse->jointree->quals); + is_parallel_safe(root, parse->jointree->quals, NIL); /* The only path for it is a trivial Result path */ add_path(final_rel, (Path *) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 649a233..9c31aa3 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1809,7 +1809,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, * computed by partial paths. */ if (current_rel->partial_pathlist && - is_parallel_safe(root, (Node *) scanjoin_target->exprs)) + is_parallel_safe(root, (Node *) scanjoin_target->exprs, NIL)) { /* Apply the scan/join target to each partial path */ foreach(lc, current_rel->partial_pathlist) @@ -1944,8 +1944,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, * query. */ if (current_rel->consider_parallel && - is_parallel_safe(root, parse->limitOffset) && - is_parallel_safe(root, parse->limitCount)) + is_parallel_safe(root, parse->limitOffset, NIL) && + is_parallel_safe(root, parse->limitCount, NIL)) final_rel->consider_parallel = true; /* @@ -3530,8 +3530,8 @@ create_grouping_paths(PlannerInfo *root, * target list and HAVING quals are parallel-safe. */ if (input_rel->consider_parallel && - is_parallel_safe(root, (Node *) target->exprs) && - is_parallel_safe(root, (Node *) parse->havingQual)) + is_parallel_safe(root, (Node *) target->exprs, NIL) && + is_parallel_safe(root, (Node *) parse->havingQual, NIL)) grouped_rel->consider_parallel = true; /* @@ -4502,8 +4502,8 @@ create_window_paths(PlannerInfo *root, * target list and active windows for non-parallel-safe constructs. */ if (input_rel->consider_parallel && - is_parallel_safe(root, (Node *) output_target->exprs) && - is_parallel_safe(root, (Node *) activeWindows)) + is_parallel_safe(root, (Node *) output_target->exprs, NIL) && + is_parallel_safe(root, (Node *) activeWindows, NIL)) window_rel->consider_parallel = true; /* @@ -4892,7 +4892,7 @@ create_ordered_paths(PlannerInfo *root, * target list is parallel-safe. */ if (input_rel->consider_parallel && - is_parallel_safe(root, (Node *) target->exprs)) + is_parallel_safe(root, (Node *) target->exprs, NIL)) ordered_rel->consider_parallel = true; /* diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index c1be34d..959a660 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -57,7 +57,7 @@ typedef struct finalize_primnode_context static Node *build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, List *plan_params, SubLinkType subLinkType, int subLinkId, - Node *testexpr, bool adjust_testexpr, + Node *testexpr, List *paramIds, bool adjust_testexpr, bool unknownEqFalse); static List *generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds); @@ -550,7 +550,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, /* And convert to SubPlan or InitPlan format. */ result = build_subplan(root, plan, subroot, plan_params, subLinkType, subLinkId, - testexpr, true, isTopQual); + testexpr, NIL, true, isTopQual); /* * If it's a correlated EXISTS with an unimportant targetlist, we might be @@ -604,12 +604,11 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, plan_params, ANY_SUBLINK, 0, newtestexpr, + paramIds, false, true)); /* Check we got what we expected */ Assert(hashplan->parParam == NIL); Assert(hashplan->useHashTable); - /* build_subplan won't have filled in paramIds */ - hashplan->paramIds = paramIds; /* Leave it to the executor to decide which plan to use */ asplan = makeNode(AlternativeSubPlan); @@ -632,7 +631,7 @@ static Node * build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, List *plan_params, SubLinkType subLinkType, int subLinkId, - Node *testexpr, bool adjust_testexpr, + Node *testexpr, List *paramIds, bool adjust_testexpr, bool unknownEqFalse) { Node *result; @@ -647,12 +646,12 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, splan = makeNode(SubPlan); splan->subLinkType = subLinkType; splan->testexpr = NULL; - splan->paramIds = NIL; + splan->paramIds = paramIds; get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation); splan->useHashTable = false; splan->unknownEqFalse = unknownEqFalse; - splan->parallel_safe = plan->parallel_safe; + splan->parallel_safe = false; splan->setParam = NIL; splan->parParam = NIL; splan->args = NIL; @@ -815,6 +814,13 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, splan->testexpr = testexpr; /* + * mark the subplan as parallel safe, iff the child plan and the + * contained testexpr are parallel safe. + */ + splan->parallel_safe = plan->parallel_safe && + is_parallel_safe(root, splan->testexpr, splan->paramIds); + + /* * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types to * initPlans, even when they are uncorrelated or undirect correlated, * because we need to scan the output of the subplan for each outer diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index e196c5e..8584e04 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -93,6 +93,7 @@ typedef struct { char max_hazard; /* worst proparallel hazard found so far */ char max_interesting; /* worst proparallel hazard of interest */ + List *safe_param_ids; /* list of safe param ids */ } max_parallel_hazard_context; static bool contain_agg_clause_walker(Node *node, void *context); @@ -1056,6 +1057,7 @@ max_parallel_hazard(Query *parse) context.max_hazard = PROPARALLEL_SAFE; context.max_interesting = PROPARALLEL_UNSAFE; + context.safe_param_ids = NIL; (void) max_parallel_hazard_walker((Node *) parse, &context); return context.max_hazard; } @@ -1064,11 +1066,15 @@ max_parallel_hazard(Query *parse) * is_parallel_safe * Detect whether the given expr contains only parallel-safe functions * + * Any reference to safeParamIds in an expression is considered parallel-safe. + * Note that we must consider only those params as safe params which can be + * available in parallel part of the plan. + * * root->glob->maxParallelHazard must previously have been set to the * result of max_parallel_hazard() on the whole query. */ bool -is_parallel_safe(PlannerInfo *root, Node *node) +is_parallel_safe(PlannerInfo *root, Node *node, List *safeParamIds) { max_parallel_hazard_context context; @@ -1084,6 +1090,7 @@ is_parallel_safe(PlannerInfo *root, Node *node) /* Else use max_parallel_hazard's search logic, but stop on RESTRICTED */ context.max_hazard = PROPARALLEL_SAFE; context.max_interesting = PROPARALLEL_RESTRICTED; + context.safe_param_ids = safeParamIds; return !max_parallel_hazard_walker(node, &context); } @@ -1176,11 +1183,19 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) return !((SubPlan *) node)->parallel_safe; /* - * We can't pass Params to workers at the moment either, so they are also - * parallel-restricted. + * We can't pass Params to workers at the moment, so except for reference + * to safe_param_ids, they are considered parallel-restricted. */ else if (IsA(node, Param)) { + ListCell *lc; + + foreach(lc, context->safe_param_ids) + { + if (lfirst_int(lc) == ((Param *) node)->paramid) + return false; + } + if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context)) return true; } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 2d5caae..e198d68 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -2292,7 +2292,7 @@ create_projection_path(PlannerInfo *root, pathnode->path.parallel_aware = false; pathnode->path.parallel_safe = rel->consider_parallel && subpath->parallel_safe && - is_parallel_safe(root, (Node *) target->exprs); + is_parallel_safe(root, (Node *) target->exprs, NIL); pathnode->path.parallel_workers = subpath->parallel_workers; /* Projection does not change the sort order */ pathnode->path.pathkeys = subpath->pathkeys; @@ -2399,7 +2399,7 @@ apply_projection_to_path(PlannerInfo *root, * target expressions, then we can't. */ if (IsA(path, GatherPath) && - is_parallel_safe(root, (Node *) target->exprs)) + is_parallel_safe(root, (Node *) target->exprs, NIL)) { GatherPath *gpath = (GatherPath *) path; @@ -2420,7 +2420,7 @@ apply_projection_to_path(PlannerInfo *root, target); } else if (path->parallel_safe && - !is_parallel_safe(root, (Node *) target->exprs)) + !is_parallel_safe(root, (Node *) target->exprs, NIL)) { /* * We're inserting a parallel-restricted target list into a path @@ -2460,7 +2460,7 @@ create_set_projection_path(PlannerInfo *root, pathnode->path.parallel_aware = false; pathnode->path.parallel_safe = rel->consider_parallel && subpath->parallel_safe && - is_parallel_safe(root, (Node *) target->exprs); + is_parallel_safe(root, (Node *) target->exprs, NIL); pathnode->path.parallel_workers = subpath->parallel_workers; /* Projection does not change the sort order XXX? */ pathnode->path.pathkeys = subpath->pathkeys; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 342d884..1980cf5 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -593,8 +593,8 @@ build_join_rel(PlannerInfo *root, * here. */ if (inner_rel->consider_parallel && outer_rel->consider_parallel && - is_parallel_safe(root, (Node *) restrictlist) && - is_parallel_safe(root, (Node *) joinrel->reltarget->exprs)) + is_parallel_safe(root, (Node *) restrictlist, NIL) && + is_parallel_safe(root, (Node *) joinrel->reltarget->exprs, NIL)) joinrel->consider_parallel = true; /* Add the joinrel to the PlannerInfo. */ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index cc0d7b0..17533f3 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -61,7 +61,8 @@ extern bool contain_mutable_functions(Node *clause); extern bool contain_volatile_functions(Node *clause); extern bool contain_volatile_functions_not_nextval(Node *clause); extern char max_parallel_hazard(Query *parse); -extern bool is_parallel_safe(PlannerInfo *root, Node *node); +extern bool is_parallel_safe(PlannerInfo *root, Node *node, + List *safeParamIds); extern bool contain_nonstrict_functions(Node *clause); extern bool contain_leaked_vars(Node *clause);