mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Speed up planner's scanning for parallel-query hazards.
We need to scan the whole parse tree for parallel-unsafe functions. If there are none, we'll later need to determine whether particular subtrees contain any parallel-restricted functions. The previous coding retained no knowledge from the first scan, even though this is very wasteful in the common case where the query contains only parallel-safe functions. We can bypass all of the later scans by remembering that fact. This provides a small but measurable speed improvement when the case applies, and shouldn't cost anything when it doesn't. Patch by me, reviewed by Robert Haas Discussion: <3740.1471538387@sss.pgh.pa.us>
This commit is contained in:
parent
6f79ae7fe5
commit
da1c91631e
@ -2029,6 +2029,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
|
|||||||
WRITE_BOOL_FIELD(dependsOnRole);
|
WRITE_BOOL_FIELD(dependsOnRole);
|
||||||
WRITE_BOOL_FIELD(parallelModeOK);
|
WRITE_BOOL_FIELD(parallelModeOK);
|
||||||
WRITE_BOOL_FIELD(parallelModeNeeded);
|
WRITE_BOOL_FIELD(parallelModeNeeded);
|
||||||
|
WRITE_CHAR_FIELD(maxParallelHazard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -78,7 +78,6 @@ static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
static void create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel);
|
static void create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel);
|
||||||
static void set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
|
static void set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
|
||||||
RangeTblEntry *rte);
|
RangeTblEntry *rte);
|
||||||
static bool function_rte_parallel_ok(RangeTblEntry *rte);
|
|
||||||
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
RangeTblEntry *rte);
|
RangeTblEntry *rte);
|
||||||
static void set_tablesample_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
static void set_tablesample_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||||
@ -542,8 +541,7 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
|
|
||||||
if (proparallel != PROPARALLEL_SAFE)
|
if (proparallel != PROPARALLEL_SAFE)
|
||||||
return;
|
return;
|
||||||
if (has_parallel_hazard((Node *) rte->tablesample->args,
|
if (!is_parallel_safe(root, (Node *) rte->tablesample->args))
|
||||||
false))
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,7 +594,7 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
|
|
||||||
case RTE_FUNCTION:
|
case RTE_FUNCTION:
|
||||||
/* Check for parallel-restricted functions. */
|
/* Check for parallel-restricted functions. */
|
||||||
if (!function_rte_parallel_ok(rte))
|
if (!is_parallel_safe(root, (Node *) rte->functions))
|
||||||
return;
|
return;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -629,40 +627,20 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
* outer join clauses work correctly. It would likely break equivalence
|
* outer join clauses work correctly. It would likely break equivalence
|
||||||
* classes, too.
|
* classes, too.
|
||||||
*/
|
*/
|
||||||
if (has_parallel_hazard((Node *) rel->baserestrictinfo, false))
|
if (!is_parallel_safe(root, (Node *) rel->baserestrictinfo))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Likewise, if the relation's outputs are not parallel-safe, give up.
|
* Likewise, if the relation's outputs are not parallel-safe, give up.
|
||||||
* (Usually, they're just Vars, but sometimes they're not.)
|
* (Usually, they're just Vars, but sometimes they're not.)
|
||||||
*/
|
*/
|
||||||
if (has_parallel_hazard((Node *) rel->reltarget->exprs, false))
|
if (!is_parallel_safe(root, (Node *) rel->reltarget->exprs))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* We have a winner. */
|
/* We have a winner. */
|
||||||
rel->consider_parallel = true;
|
rel->consider_parallel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Check whether a function RTE is scanning something parallel-restricted.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
function_rte_parallel_ok(RangeTblEntry *rte)
|
|
||||||
{
|
|
||||||
ListCell *lc;
|
|
||||||
|
|
||||||
foreach(lc, rte->functions)
|
|
||||||
{
|
|
||||||
RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
|
|
||||||
|
|
||||||
Assert(IsA(rtfunc, RangeTblFunction));
|
|
||||||
if (has_parallel_hazard(rtfunc->funcexpr, false))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set_plain_rel_pathlist
|
* set_plain_rel_pathlist
|
||||||
* Build access paths for a plain relation (no subquery, no inheritance)
|
* Build access paths for a plain relation (no subquery, no inheritance)
|
||||||
|
@ -71,14 +71,13 @@ query_planner(PlannerInfo *root, List *tlist,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If query allows parallelism in general, check whether the quals are
|
* If query allows parallelism in general, check whether the quals are
|
||||||
* parallel-restricted. There's currently no real benefit to setting
|
* parallel-restricted. (We need not check final_rel->reltarget
|
||||||
* this flag correctly because we can't yet reference subplans from
|
* because it's empty at this point. Anything parallel-restricted in
|
||||||
* parallel workers. But that might change someday, so set this
|
* the query tlist will be dealt with later.)
|
||||||
* correctly anyway.
|
|
||||||
*/
|
*/
|
||||||
if (root->glob->parallelModeOK)
|
if (root->glob->parallelModeOK)
|
||||||
final_rel->consider_parallel =
|
final_rel->consider_parallel =
|
||||||
!has_parallel_hazard(parse->jointree->quals, false);
|
is_parallel_safe(root, parse->jointree->quals);
|
||||||
|
|
||||||
/* The only path for it is a trivial Result path */
|
/* The only path for it is a trivial Result path */
|
||||||
add_path(final_rel, (Path *)
|
add_path(final_rel, (Path *)
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "access/sysattr.h"
|
#include "access/sysattr.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/pg_constraint_fn.h"
|
#include "catalog/pg_constraint_fn.h"
|
||||||
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/nodeAgg.h"
|
#include "executor/nodeAgg.h"
|
||||||
@ -241,12 +242,26 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
|
|||||||
* time and execution time, so don't generate a parallel plan if we're in
|
* time and execution time, so don't generate a parallel plan if we're in
|
||||||
* serializable mode.
|
* serializable mode.
|
||||||
*/
|
*/
|
||||||
glob->parallelModeOK = (cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
|
if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
|
||||||
IsUnderPostmaster && dynamic_shared_memory_type != DSM_IMPL_NONE &&
|
IsUnderPostmaster &&
|
||||||
parse->commandType == CMD_SELECT && !parse->hasModifyingCTE &&
|
dynamic_shared_memory_type != DSM_IMPL_NONE &&
|
||||||
parse->utilityStmt == NULL && max_parallel_workers_per_gather > 0 &&
|
parse->commandType == CMD_SELECT &&
|
||||||
!IsParallelWorker() && !IsolationIsSerializable() &&
|
parse->utilityStmt == NULL &&
|
||||||
!has_parallel_hazard((Node *) parse, true);
|
!parse->hasModifyingCTE &&
|
||||||
|
max_parallel_workers_per_gather > 0 &&
|
||||||
|
!IsParallelWorker() &&
|
||||||
|
!IsolationIsSerializable())
|
||||||
|
{
|
||||||
|
/* all the cheap tests pass, so scan the query tree */
|
||||||
|
glob->maxParallelHazard = max_parallel_hazard(parse);
|
||||||
|
glob->parallelModeOK = (glob->maxParallelHazard != PROPARALLEL_UNSAFE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* skip the query tree scan, just assume it's unsafe */
|
||||||
|
glob->maxParallelHazard = PROPARALLEL_UNSAFE;
|
||||||
|
glob->parallelModeOK = false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* glob->parallelModeNeeded should tell us whether it's necessary to
|
* glob->parallelModeNeeded should tell us whether it's necessary to
|
||||||
@ -1802,7 +1817,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
|
|||||||
* computed by partial paths.
|
* computed by partial paths.
|
||||||
*/
|
*/
|
||||||
if (current_rel->partial_pathlist &&
|
if (current_rel->partial_pathlist &&
|
||||||
!has_parallel_hazard((Node *) scanjoin_target->exprs, false))
|
is_parallel_safe(root, (Node *) scanjoin_target->exprs))
|
||||||
{
|
{
|
||||||
/* Apply the scan/join target to each partial path */
|
/* Apply the scan/join target to each partial path */
|
||||||
foreach(lc, current_rel->partial_pathlist)
|
foreach(lc, current_rel->partial_pathlist)
|
||||||
@ -1948,8 +1963,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
|
|||||||
* query.
|
* query.
|
||||||
*/
|
*/
|
||||||
if (current_rel->consider_parallel &&
|
if (current_rel->consider_parallel &&
|
||||||
!has_parallel_hazard(parse->limitOffset, false) &&
|
is_parallel_safe(root, parse->limitOffset) &&
|
||||||
!has_parallel_hazard(parse->limitCount, false))
|
is_parallel_safe(root, parse->limitCount))
|
||||||
final_rel->consider_parallel = true;
|
final_rel->consider_parallel = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3326,8 +3341,8 @@ create_grouping_paths(PlannerInfo *root,
|
|||||||
* target list and HAVING quals are parallel-safe.
|
* target list and HAVING quals are parallel-safe.
|
||||||
*/
|
*/
|
||||||
if (input_rel->consider_parallel &&
|
if (input_rel->consider_parallel &&
|
||||||
!has_parallel_hazard((Node *) target->exprs, false) &&
|
is_parallel_safe(root, (Node *) target->exprs) &&
|
||||||
!has_parallel_hazard((Node *) parse->havingQual, false))
|
is_parallel_safe(root, (Node *) parse->havingQual))
|
||||||
grouped_rel->consider_parallel = true;
|
grouped_rel->consider_parallel = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3881,8 +3896,8 @@ create_window_paths(PlannerInfo *root,
|
|||||||
* target list and active windows for non-parallel-safe constructs.
|
* target list and active windows for non-parallel-safe constructs.
|
||||||
*/
|
*/
|
||||||
if (input_rel->consider_parallel &&
|
if (input_rel->consider_parallel &&
|
||||||
!has_parallel_hazard((Node *) output_target->exprs, false) &&
|
is_parallel_safe(root, (Node *) output_target->exprs) &&
|
||||||
!has_parallel_hazard((Node *) activeWindows, false))
|
is_parallel_safe(root, (Node *) activeWindows))
|
||||||
window_rel->consider_parallel = true;
|
window_rel->consider_parallel = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4272,7 +4287,7 @@ create_ordered_paths(PlannerInfo *root,
|
|||||||
* target list is parallel-safe.
|
* target list is parallel-safe.
|
||||||
*/
|
*/
|
||||||
if (input_rel->consider_parallel &&
|
if (input_rel->consider_parallel &&
|
||||||
!has_parallel_hazard((Node *) target->exprs, false))
|
is_parallel_safe(root, (Node *) target->exprs))
|
||||||
ordered_rel->consider_parallel = true;
|
ordered_rel->consider_parallel = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -91,8 +91,9 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
bool allow_restricted;
|
char max_hazard; /* worst proparallel hazard found so far */
|
||||||
} has_parallel_hazard_arg;
|
char max_interesting; /* worst proparallel hazard of interest */
|
||||||
|
} max_parallel_hazard_context;
|
||||||
|
|
||||||
static bool contain_agg_clause_walker(Node *node, void *context);
|
static bool contain_agg_clause_walker(Node *node, void *context);
|
||||||
static bool get_agg_clause_costs_walker(Node *node,
|
static bool get_agg_clause_costs_walker(Node *node,
|
||||||
@ -103,8 +104,8 @@ static bool contain_subplans_walker(Node *node, void *context);
|
|||||||
static bool contain_mutable_functions_walker(Node *node, void *context);
|
static bool contain_mutable_functions_walker(Node *node, void *context);
|
||||||
static bool contain_volatile_functions_walker(Node *node, void *context);
|
static bool contain_volatile_functions_walker(Node *node, void *context);
|
||||||
static bool contain_volatile_functions_not_nextval_walker(Node *node, void *context);
|
static bool contain_volatile_functions_not_nextval_walker(Node *node, void *context);
|
||||||
static bool has_parallel_hazard_walker(Node *node,
|
static bool max_parallel_hazard_walker(Node *node,
|
||||||
has_parallel_hazard_arg *context);
|
max_parallel_hazard_context *context);
|
||||||
static bool contain_nonstrict_functions_walker(Node *node, void *context);
|
static bool contain_nonstrict_functions_walker(Node *node, void *context);
|
||||||
static bool contain_context_dependent_node(Node *clause);
|
static bool contain_context_dependent_node(Node *clause);
|
||||||
static bool contain_context_dependent_node_walker(Node *node, int *flags);
|
static bool contain_context_dependent_node_walker(Node *node, int *flags);
|
||||||
@ -1100,46 +1101,98 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context)
|
|||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Check queries for parallel unsafe and/or restricted constructs
|
* Check queries for parallel unsafe and/or restricted constructs
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check whether a node tree contains parallel hazards. This is used both on
|
* max_parallel_hazard
|
||||||
* the entire query tree, to see whether the query can be parallelized at all
|
* Find the worst parallel-hazard level in the given query
|
||||||
* (with allow_restricted = true), and also to evaluate whether a particular
|
*
|
||||||
* expression is safe to run within a parallel worker (with allow_restricted =
|
* Returns the worst function hazard property (the earliest in this list:
|
||||||
* false). We could separate these concerns into two different functions, but
|
* PROPARALLEL_UNSAFE, PROPARALLEL_RESTRICTED, PROPARALLEL_SAFE) that can
|
||||||
* there's enough overlap that it doesn't seem worthwhile.
|
* be found in the given parsetree. We use this to find out whether the query
|
||||||
|
* can be parallelized at all. The caller will also save the result in
|
||||||
|
* PlannerGlobal so as to short-circuit checks of portions of the querytree
|
||||||
|
* later, in the common case where everything is SAFE.
|
||||||
|
*/
|
||||||
|
char
|
||||||
|
max_parallel_hazard(Query *parse)
|
||||||
|
{
|
||||||
|
max_parallel_hazard_context context;
|
||||||
|
|
||||||
|
context.max_hazard = PROPARALLEL_SAFE;
|
||||||
|
context.max_interesting = PROPARALLEL_UNSAFE;
|
||||||
|
(void) max_parallel_hazard_walker((Node *) parse, &context);
|
||||||
|
return context.max_hazard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* is_parallel_safe
|
||||||
|
* Detect whether the given expr contains only parallel-safe functions
|
||||||
|
*
|
||||||
|
* root->glob->maxParallelHazard must previously have been set to the
|
||||||
|
* result of max_parallel_hazard() on the whole query.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
has_parallel_hazard(Node *node, bool allow_restricted)
|
is_parallel_safe(PlannerInfo *root, Node *node)
|
||||||
{
|
{
|
||||||
has_parallel_hazard_arg context;
|
max_parallel_hazard_context context;
|
||||||
|
|
||||||
context.allow_restricted = allow_restricted;
|
/* If max_parallel_hazard found nothing unsafe, we don't need to look */
|
||||||
return has_parallel_hazard_walker(node, &context);
|
if (root->glob->maxParallelHazard == PROPARALLEL_SAFE)
|
||||||
|
return true;
|
||||||
|
/* Else use max_parallel_hazard's search logic, but stop on RESTRICTED */
|
||||||
|
context.max_hazard = PROPARALLEL_SAFE;
|
||||||
|
context.max_interesting = PROPARALLEL_RESTRICTED;
|
||||||
|
return !max_parallel_hazard_walker(node, &context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* core logic for all parallel-hazard checks */
|
||||||
|
static bool
|
||||||
|
max_parallel_hazard_test(char proparallel, max_parallel_hazard_context *context)
|
||||||
|
{
|
||||||
|
switch (proparallel)
|
||||||
|
{
|
||||||
|
case PROPARALLEL_SAFE:
|
||||||
|
/* nothing to see here, move along */
|
||||||
|
break;
|
||||||
|
case PROPARALLEL_RESTRICTED:
|
||||||
|
/* increase max_hazard to RESTRICTED */
|
||||||
|
Assert(context->max_hazard != PROPARALLEL_UNSAFE);
|
||||||
|
context->max_hazard = proparallel;
|
||||||
|
/* done if we are not expecting any unsafe functions */
|
||||||
|
if (context->max_interesting == proparallel)
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case PROPARALLEL_UNSAFE:
|
||||||
|
context->max_hazard = proparallel;
|
||||||
|
/* we're always done at the first unsafe construct */
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized proparallel value \"%c\"", proparallel);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check_functions_in_node callback */
|
||||||
|
static bool
|
||||||
|
max_parallel_hazard_checker(Oid func_id, void *context)
|
||||||
|
{
|
||||||
|
return max_parallel_hazard_test(func_parallel(func_id),
|
||||||
|
(max_parallel_hazard_context *) context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
has_parallel_hazard_checker(Oid func_id, void *context)
|
max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
|
||||||
{
|
|
||||||
char proparallel = func_parallel(func_id);
|
|
||||||
|
|
||||||
if (((has_parallel_hazard_arg *) context)->allow_restricted)
|
|
||||||
return (proparallel == PROPARALLEL_UNSAFE);
|
|
||||||
else
|
|
||||||
return (proparallel != PROPARALLEL_SAFE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
|
|
||||||
{
|
{
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Check for hazardous functions in node itself */
|
/* Check for hazardous functions in node itself */
|
||||||
if (check_functions_in_node(node, has_parallel_hazard_checker,
|
if (check_functions_in_node(node, max_parallel_hazard_checker,
|
||||||
context))
|
context))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -1156,7 +1209,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
|
|||||||
*/
|
*/
|
||||||
if (IsA(node, CoerceToDomain))
|
if (IsA(node, CoerceToDomain))
|
||||||
{
|
{
|
||||||
if (!context->allow_restricted)
|
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1167,7 +1220,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
|
|||||||
{
|
{
|
||||||
RestrictInfo *rinfo = (RestrictInfo *) node;
|
RestrictInfo *rinfo = (RestrictInfo *) node;
|
||||||
|
|
||||||
return has_parallel_hazard_walker((Node *) rinfo->clause, context);
|
return max_parallel_hazard_walker((Node *) rinfo->clause, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1176,13 +1229,13 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
|
|||||||
* not worry about examining their contents; if they are unsafe, we would
|
* not worry about examining their contents; if they are unsafe, we would
|
||||||
* have found that out while examining the whole tree before reduction of
|
* have found that out while examining the whole tree before reduction of
|
||||||
* sublinks to subplans. (Really we should not see SubLink during a
|
* sublinks to subplans. (Really we should not see SubLink during a
|
||||||
* not-allow_restricted scan, but if we do, return true.)
|
* max_interesting == restricted scan, but if we do, return true.)
|
||||||
*/
|
*/
|
||||||
else if (IsA(node, SubLink) ||
|
else if (IsA(node, SubLink) ||
|
||||||
IsA(node, SubPlan) ||
|
IsA(node, SubPlan) ||
|
||||||
IsA(node, AlternativeSubPlan))
|
IsA(node, AlternativeSubPlan))
|
||||||
{
|
{
|
||||||
if (!context->allow_restricted)
|
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1192,7 +1245,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
|
|||||||
*/
|
*/
|
||||||
else if (IsA(node, Param))
|
else if (IsA(node, Param))
|
||||||
{
|
{
|
||||||
if (!context->allow_restricted)
|
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1207,20 +1260,24 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
|
|||||||
|
|
||||||
/* SELECT FOR UPDATE/SHARE must be treated as unsafe */
|
/* SELECT FOR UPDATE/SHARE must be treated as unsafe */
|
||||||
if (query->rowMarks != NULL)
|
if (query->rowMarks != NULL)
|
||||||
|
{
|
||||||
|
context->max_hazard = PROPARALLEL_UNSAFE;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Recurse into subselects */
|
/* Recurse into subselects */
|
||||||
return query_tree_walker(query,
|
return query_tree_walker(query,
|
||||||
has_parallel_hazard_walker,
|
max_parallel_hazard_walker,
|
||||||
context, 0);
|
context, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Recurse to check arguments */
|
/* Recurse to check arguments */
|
||||||
return expression_tree_walker(node,
|
return expression_tree_walker(node,
|
||||||
has_parallel_hazard_walker,
|
max_parallel_hazard_walker,
|
||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Check clauses for nonstrict functions
|
* Check clauses for nonstrict functions
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
@ -2178,7 +2178,7 @@ create_projection_path(PlannerInfo *root,
|
|||||||
pathnode->path.parallel_aware = false;
|
pathnode->path.parallel_aware = false;
|
||||||
pathnode->path.parallel_safe = rel->consider_parallel &&
|
pathnode->path.parallel_safe = rel->consider_parallel &&
|
||||||
subpath->parallel_safe &&
|
subpath->parallel_safe &&
|
||||||
!has_parallel_hazard((Node *) target->exprs, false);
|
is_parallel_safe(root, (Node *) target->exprs);
|
||||||
pathnode->path.parallel_workers = subpath->parallel_workers;
|
pathnode->path.parallel_workers = subpath->parallel_workers;
|
||||||
/* Projection does not change the sort order */
|
/* Projection does not change the sort order */
|
||||||
pathnode->path.pathkeys = subpath->pathkeys;
|
pathnode->path.pathkeys = subpath->pathkeys;
|
||||||
@ -2285,7 +2285,7 @@ apply_projection_to_path(PlannerInfo *root,
|
|||||||
* target expressions, then we can't.
|
* target expressions, then we can't.
|
||||||
*/
|
*/
|
||||||
if (IsA(path, GatherPath) &&
|
if (IsA(path, GatherPath) &&
|
||||||
!has_parallel_hazard((Node *) target->exprs, false))
|
is_parallel_safe(root, (Node *) target->exprs))
|
||||||
{
|
{
|
||||||
GatherPath *gpath = (GatherPath *) path;
|
GatherPath *gpath = (GatherPath *) path;
|
||||||
|
|
||||||
@ -2306,7 +2306,7 @@ apply_projection_to_path(PlannerInfo *root,
|
|||||||
target);
|
target);
|
||||||
}
|
}
|
||||||
else if (path->parallel_safe &&
|
else if (path->parallel_safe &&
|
||||||
has_parallel_hazard((Node *) target->exprs, false))
|
!is_parallel_safe(root, (Node *) target->exprs))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We're inserting a parallel-restricted target list into a path
|
* We're inserting a parallel-restricted target list into a path
|
||||||
|
@ -513,8 +513,8 @@ build_join_rel(PlannerInfo *root,
|
|||||||
* here.
|
* here.
|
||||||
*/
|
*/
|
||||||
if (inner_rel->consider_parallel && outer_rel->consider_parallel &&
|
if (inner_rel->consider_parallel && outer_rel->consider_parallel &&
|
||||||
!has_parallel_hazard((Node *) restrictlist, false) &&
|
is_parallel_safe(root, (Node *) restrictlist) &&
|
||||||
!has_parallel_hazard((Node *) joinrel->reltarget->exprs, false))
|
is_parallel_safe(root, (Node *) joinrel->reltarget->exprs))
|
||||||
joinrel->consider_parallel = true;
|
joinrel->consider_parallel = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -126,6 +126,8 @@ typedef struct PlannerGlobal
|
|||||||
bool parallelModeOK; /* parallel mode potentially OK? */
|
bool parallelModeOK; /* parallel mode potentially OK? */
|
||||||
|
|
||||||
bool parallelModeNeeded; /* parallel mode actually required? */
|
bool parallelModeNeeded; /* parallel mode actually required? */
|
||||||
|
|
||||||
|
char maxParallelHazard; /* worst PROPARALLEL hazard level */
|
||||||
} PlannerGlobal;
|
} PlannerGlobal;
|
||||||
|
|
||||||
/* macro for fetching the Plan associated with a SubPlan node */
|
/* macro for fetching the Plan associated with a SubPlan node */
|
||||||
|
@ -61,7 +61,8 @@ extern bool contain_subplans(Node *clause);
|
|||||||
extern bool contain_mutable_functions(Node *clause);
|
extern bool contain_mutable_functions(Node *clause);
|
||||||
extern bool contain_volatile_functions(Node *clause);
|
extern bool contain_volatile_functions(Node *clause);
|
||||||
extern bool contain_volatile_functions_not_nextval(Node *clause);
|
extern bool contain_volatile_functions_not_nextval(Node *clause);
|
||||||
extern bool has_parallel_hazard(Node *node, bool allow_restricted);
|
extern char max_parallel_hazard(Query *parse);
|
||||||
|
extern bool is_parallel_safe(PlannerInfo *root, Node *node);
|
||||||
extern bool contain_nonstrict_functions(Node *clause);
|
extern bool contain_nonstrict_functions(Node *clause);
|
||||||
extern bool contain_leaked_vars(Node *clause);
|
extern bool contain_leaked_vars(Node *clause);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user