aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-08-19 14:03:07 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2016-08-19 14:03:13 -0400
commitda1c91631e3577ea5818f855ebb5bd206d559006 (patch)
tree90bdd7e1f5929851e2cb948cd08db891c0a24479 /src/backend/optimizer/util
parent6f79ae7fe549bed8bbd1f54ddd9b98f8f9a315f5 (diff)
downloadpostgresql-da1c91631e3577ea5818f855ebb5bd206d559006.tar.gz
postgresql-da1c91631e3577ea5818f855ebb5bd206d559006.zip
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>
Diffstat (limited to 'src/backend/optimizer/util')
-rw-r--r--src/backend/optimizer/util/clauses.c115
-rw-r--r--src/backend/optimizer/util/pathnode.c6
-rw-r--r--src/backend/optimizer/util/relnode.c4
3 files changed, 91 insertions, 34 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a40ad406065..4496fde056d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -91,8 +91,9 @@ typedef struct
typedef struct
{
- bool allow_restricted;
-} has_parallel_hazard_arg;
+ char max_hazard; /* worst proparallel hazard found so far */
+ char max_interesting; /* worst proparallel hazard of interest */
+} max_parallel_hazard_context;
static bool contain_agg_clause_walker(Node *node, void *context);
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_volatile_functions_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,
- has_parallel_hazard_arg *context);
+static bool max_parallel_hazard_walker(Node *node,
+ max_parallel_hazard_context *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_walker(Node *node, int *flags);
@@ -1100,46 +1101,98 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context)
context);
}
+
/*****************************************************************************
* Check queries for parallel unsafe and/or restricted constructs
*****************************************************************************/
/*
- * Check whether a node tree contains parallel hazards. This is used both on
- * the entire query tree, to see whether the query can be parallelized at all
- * (with allow_restricted = true), and also to evaluate whether a particular
- * expression is safe to run within a parallel worker (with allow_restricted =
- * false). We could separate these concerns into two different functions, but
- * there's enough overlap that it doesn't seem worthwhile.
+ * max_parallel_hazard
+ * Find the worst parallel-hazard level in the given query
+ *
+ * Returns the worst function hazard property (the earliest in this list:
+ * PROPARALLEL_UNSAFE, PROPARALLEL_RESTRICTED, PROPARALLEL_SAFE) that can
+ * 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
-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;
- return has_parallel_hazard_walker(node, &context);
+ /* If max_parallel_hazard found nothing unsafe, we don't need to look */
+ 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
-has_parallel_hazard_checker(Oid func_id, void *context)
+max_parallel_hazard_test(char proparallel, max_parallel_hazard_context *context)
{
- char proparallel = func_parallel(func_id);
+ 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;
+}
- if (((has_parallel_hazard_arg *) context)->allow_restricted)
- return (proparallel == PROPARALLEL_UNSAFE);
- else
- return (proparallel != PROPARALLEL_SAFE);
+/* 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
-has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
+max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
{
if (node == NULL)
return false;
/* 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))
return true;
@@ -1156,7 +1209,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
*/
if (IsA(node, CoerceToDomain))
{
- if (!context->allow_restricted)
+ if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
return true;
}
@@ -1167,7 +1220,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
{
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
* have found that out while examining the whole tree before reduction of
* 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) ||
IsA(node, SubPlan) ||
IsA(node, AlternativeSubPlan))
{
- if (!context->allow_restricted)
+ if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
return true;
}
@@ -1192,7 +1245,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
*/
else if (IsA(node, Param))
{
- if (!context->allow_restricted)
+ if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
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 */
if (query->rowMarks != NULL)
+ {
+ context->max_hazard = PROPARALLEL_UNSAFE;
return true;
+ }
/* Recurse into subselects */
return query_tree_walker(query,
- has_parallel_hazard_walker,
+ max_parallel_hazard_walker,
context, 0);
}
/* Recurse to check arguments */
return expression_tree_walker(node,
- has_parallel_hazard_walker,
+ max_parallel_hazard_walker,
context);
}
+
/*****************************************************************************
* Check clauses for nonstrict functions
*****************************************************************************/
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index ce7ad545a95..abb7507d8e4 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -2178,7 +2178,7 @@ create_projection_path(PlannerInfo *root,
pathnode->path.parallel_aware = false;
pathnode->path.parallel_safe = rel->consider_parallel &&
subpath->parallel_safe &&
- !has_parallel_hazard((Node *) target->exprs, false);
+ is_parallel_safe(root, (Node *) target->exprs);
pathnode->path.parallel_workers = subpath->parallel_workers;
/* Projection does not change the sort order */
pathnode->path.pathkeys = subpath->pathkeys;
@@ -2285,7 +2285,7 @@ apply_projection_to_path(PlannerInfo *root,
* target expressions, then we can't.
*/
if (IsA(path, GatherPath) &&
- !has_parallel_hazard((Node *) target->exprs, false))
+ is_parallel_safe(root, (Node *) target->exprs))
{
GatherPath *gpath = (GatherPath *) path;
@@ -2306,7 +2306,7 @@ apply_projection_to_path(PlannerInfo *root,
target);
}
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
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 806600ed107..deef5605b7d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -513,8 +513,8 @@ build_join_rel(PlannerInfo *root,
* here.
*/
if (inner_rel->consider_parallel && outer_rel->consider_parallel &&
- !has_parallel_hazard((Node *) restrictlist, false) &&
- !has_parallel_hazard((Node *) joinrel->reltarget->exprs, false))
+ is_parallel_safe(root, (Node *) restrictlist) &&
+ is_parallel_safe(root, (Node *) joinrel->reltarget->exprs))
joinrel->consider_parallel = true;
/*