diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1fab807..50019f4 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outPlannerGlobal(StringInfo str, const 
*** 2029,2034 ****
--- 2029,2035 ----
  	WRITE_BOOL_FIELD(dependsOnRole);
  	WRITE_BOOL_FIELD(parallelModeOK);
  	WRITE_BOOL_FIELD(parallelModeNeeded);
+ 	WRITE_CHAR_FIELD(maxParallelHazard);
  }
  
  static void
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 88d833a..365161c 100644
*** a/src/backend/optimizer/path/allpaths.c
--- b/src/backend/optimizer/path/allpaths.c
*************** static void set_plain_rel_size(PlannerIn
*** 78,84 ****
  static void create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel);
  static void set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
  						  RangeTblEntry *rte);
! static bool function_rte_parallel_ok(RangeTblEntry *rte);
  static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
  					   RangeTblEntry *rte);
  static void set_tablesample_rel_size(PlannerInfo *root, RelOptInfo *rel,
--- 78,84 ----
  static void create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel);
  static void set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
  						  RangeTblEntry *rte);
! static bool function_rte_parallel_ok(PlannerInfo *root, RangeTblEntry *rte);
  static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
  					   RangeTblEntry *rte);
  static void set_tablesample_rel_size(PlannerInfo *root, RelOptInfo *rel,
*************** set_rel_consider_parallel(PlannerInfo *r
*** 542,549 ****
  
  				if (proparallel != PROPARALLEL_SAFE)
  					return;
! 				if (has_parallel_hazard((Node *) rte->tablesample->args,
! 										false))
  					return;
  			}
  
--- 542,548 ----
  
  				if (proparallel != PROPARALLEL_SAFE)
  					return;
! 				if (!is_parallel_safe(root, (Node *) rte->tablesample->args))
  					return;
  			}
  
*************** set_rel_consider_parallel(PlannerInfo *r
*** 596,602 ****
  
  		case RTE_FUNCTION:
  			/* Check for parallel-restricted functions. */
! 			if (!function_rte_parallel_ok(rte))
  				return;
  			break;
  
--- 595,601 ----
  
  		case RTE_FUNCTION:
  			/* Check for parallel-restricted functions. */
! 			if (!function_rte_parallel_ok(root, rte))
  				return;
  			break;
  
*************** set_rel_consider_parallel(PlannerInfo *r
*** 629,642 ****
  	 * outer join clauses work correctly.  It would likely break equivalence
  	 * classes, too.
  	 */
! 	if (has_parallel_hazard((Node *) rel->baserestrictinfo, false))
  		return;
  
  	/*
  	 * Likewise, if the relation's outputs are not parallel-safe, give up.
  	 * (Usually, they're just Vars, but sometimes they're not.)
  	 */
! 	if (has_parallel_hazard((Node *) rel->reltarget->exprs, false))
  		return;
  
  	/* We have a winner. */
--- 628,641 ----
  	 * outer join clauses work correctly.  It would likely break equivalence
  	 * classes, too.
  	 */
! 	if (!is_parallel_safe(root, (Node *) rel->baserestrictinfo))
  		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))
  		return;
  
  	/* We have a winner. */
*************** set_rel_consider_parallel(PlannerInfo *r
*** 647,653 ****
   * Check whether a function RTE is scanning something parallel-restricted.
   */
  static bool
! function_rte_parallel_ok(RangeTblEntry *rte)
  {
  	ListCell   *lc;
  
--- 646,652 ----
   * Check whether a function RTE is scanning something parallel-restricted.
   */
  static bool
! function_rte_parallel_ok(PlannerInfo *root, RangeTblEntry *rte)
  {
  	ListCell   *lc;
  
*************** function_rte_parallel_ok(RangeTblEntry *
*** 656,662 ****
  		RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
  
  		Assert(IsA(rtfunc, RangeTblFunction));
! 		if (has_parallel_hazard(rtfunc->funcexpr, false))
  			return false;
  	}
  
--- 655,661 ----
  		RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
  
  		Assert(IsA(rtfunc, RangeTblFunction));
! 		if (!is_parallel_safe(root, rtfunc->funcexpr))
  			return false;
  	}
  
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 27234ff..e7ae7ae 100644
*** a/src/backend/optimizer/plan/planmain.c
--- b/src/backend/optimizer/plan/planmain.c
*************** query_planner(PlannerInfo *root, List *t
*** 71,84 ****
  
  		/*
  		 * If query allows parallelism in general, check whether the quals are
! 		 * parallel-restricted.  There's currently no real benefit to setting
! 		 * this flag correctly because we can't yet reference subplans from
! 		 * parallel workers.  But that might change someday, so set this
! 		 * correctly anyway.
  		 */
  		if (root->glob->parallelModeOK)
  			final_rel->consider_parallel =
! 				!has_parallel_hazard(parse->jointree->quals, false);
  
  		/* The only path for it is a trivial Result path */
  		add_path(final_rel, (Path *)
--- 71,83 ----
  
  		/*
  		 * If query allows parallelism in general, check whether the quals are
! 		 * parallel-restricted.  (We need not check final_rel->reltarget
! 		 * because it's empty at this point.  Anything parallel-restricted in
! 		 * the query tlist will be dealt with later.)
  		 */
  		if (root->glob->parallelModeOK)
  			final_rel->consider_parallel =
! 				is_parallel_safe(root, parse->jointree->quals);
  
  		/* 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 b265628..174210b 100644
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 23,28 ****
--- 23,29 ----
  #include "access/sysattr.h"
  #include "access/xact.h"
  #include "catalog/pg_constraint_fn.h"
+ #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
  #include "executor/executor.h"
  #include "executor/nodeAgg.h"
*************** standard_planner(Query *parse, int curso
*** 241,252 ****
  	 * time and execution time, so don't generate a parallel plan if we're in
  	 * serializable mode.
  	 */
! 	glob->parallelModeOK = (cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
! 		IsUnderPostmaster && dynamic_shared_memory_type != DSM_IMPL_NONE &&
! 		parse->commandType == CMD_SELECT && !parse->hasModifyingCTE &&
! 		parse->utilityStmt == NULL && max_parallel_workers_per_gather > 0 &&
! 		!IsParallelWorker() && !IsolationIsSerializable() &&
! 		!has_parallel_hazard((Node *) parse, true);
  
  	/*
  	 * glob->parallelModeNeeded should tell us whether it's necessary to
--- 242,267 ----
  	 * time and execution time, so don't generate a parallel plan if we're in
  	 * serializable mode.
  	 */
! 	if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
! 		IsUnderPostmaster &&
! 		dynamic_shared_memory_type != DSM_IMPL_NONE &&
! 		parse->commandType == CMD_SELECT &&
! 		parse->utilityStmt == NULL &&
! 		!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
*************** grouping_planner(PlannerInfo *root, bool
*** 1802,1808 ****
  		 * computed by partial paths.
  		 */
  		if (current_rel->partial_pathlist &&
! 			!has_parallel_hazard((Node *) scanjoin_target->exprs, false))
  		{
  			/* Apply the scan/join target to each partial path */
  			foreach(lc, current_rel->partial_pathlist)
--- 1817,1823 ----
  		 * computed by partial paths.
  		 */
  		if (current_rel->partial_pathlist &&
! 			is_parallel_safe(root, (Node *) scanjoin_target->exprs))
  		{
  			/* Apply the scan/join target to each partial path */
  			foreach(lc, current_rel->partial_pathlist)
*************** grouping_planner(PlannerInfo *root, bool
*** 1948,1955 ****
  	 * query.
  	 */
  	if (current_rel->consider_parallel &&
! 		!has_parallel_hazard(parse->limitOffset, false) &&
! 		!has_parallel_hazard(parse->limitCount, false))
  		final_rel->consider_parallel = true;
  
  	/*
--- 1963,1970 ----
  	 * query.
  	 */
  	if (current_rel->consider_parallel &&
! 		is_parallel_safe(root, parse->limitOffset) &&
! 		is_parallel_safe(root, parse->limitCount))
  		final_rel->consider_parallel = true;
  
  	/*
*************** create_grouping_paths(PlannerInfo *root,
*** 3326,3333 ****
  	 * target list and HAVING quals are parallel-safe.
  	 */
  	if (input_rel->consider_parallel &&
! 		!has_parallel_hazard((Node *) target->exprs, false) &&
! 		!has_parallel_hazard((Node *) parse->havingQual, false))
  		grouped_rel->consider_parallel = true;
  
  	/*
--- 3341,3348 ----
  	 * 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))
  		grouped_rel->consider_parallel = true;
  
  	/*
*************** create_window_paths(PlannerInfo *root,
*** 3881,3888 ****
  	 * target list and active windows for non-parallel-safe constructs.
  	 */
  	if (input_rel->consider_parallel &&
! 		!has_parallel_hazard((Node *) output_target->exprs, false) &&
! 		!has_parallel_hazard((Node *) activeWindows, false))
  		window_rel->consider_parallel = true;
  
  	/*
--- 3896,3903 ----
  	 * 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))
  		window_rel->consider_parallel = true;
  
  	/*
*************** create_ordered_paths(PlannerInfo *root,
*** 4272,4278 ****
  	 * target list is parallel-safe.
  	 */
  	if (input_rel->consider_parallel &&
! 		!has_parallel_hazard((Node *) target->exprs, false))
  		ordered_rel->consider_parallel = true;
  
  	/*
--- 4287,4293 ----
  	 * target list is parallel-safe.
  	 */
  	if (input_rel->consider_parallel &&
! 		is_parallel_safe(root, (Node *) target->exprs))
  		ordered_rel->consider_parallel = true;
  
  	/*
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a40ad40..85220ec 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** typedef struct
*** 91,98 ****
  
  typedef struct
  {
! 	bool		allow_restricted;
! } has_parallel_hazard_arg;
  
  static bool contain_agg_clause_walker(Node *node, void *context);
  static bool get_agg_clause_costs_walker(Node *node,
--- 91,99 ----
  
  typedef struct
  {
! 	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,
*************** static bool contain_subplans_walker(Node
*** 103,110 ****
  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 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);
--- 104,111 ----
  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 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);
*************** contain_volatile_functions_not_nextval_w
*** 1100,1145 ****
  								  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.
   */
  bool
! has_parallel_hazard(Node *node, bool allow_restricted)
  {
! 	has_parallel_hazard_arg context;
  
! 	context.allow_restricted = allow_restricted;
! 	return has_parallel_hazard_walker(node, &context);
  }
  
  static bool
! has_parallel_hazard_checker(Oid func_id, void *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)
  		return false;
  
  	/* Check for hazardous functions in node itself */
! 	if (check_functions_in_node(node, has_parallel_hazard_checker,
  								context))
  		return true;
  
--- 1101,1197 ----
  								  context);
  }
  
+ 
  /*****************************************************************************
   *		Check queries for parallel unsafe and/or restricted constructs
   *****************************************************************************/
  
  /*
!  * max_parallel_hazard
!  *		Find the worst parallel-hazard level in the given query
!  *
!  * Returns the first of PROPARALLEL_UNSAFE, PROPARALLEL_RESTRICTED, or
!  * PROPARALLEL_SAFE that can be found in the given parsetree.  We use this
!  * to find out whether the query can be parallelized at all.  We 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
! is_parallel_safe(PlannerInfo *root, Node *node)
  {
! 	max_parallel_hazard_context 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
! 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
! 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, max_parallel_hazard_checker,
  								context))
  		return true;
  
*************** has_parallel_hazard_walker(Node *node, h
*** 1156,1162 ****
  	 */
  	if (IsA(node, CoerceToDomain))
  	{
! 		if (!context->allow_restricted)
  			return true;
  	}
  
--- 1208,1214 ----
  	 */
  	if (IsA(node, CoerceToDomain))
  	{
! 		if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
  			return true;
  	}
  
*************** has_parallel_hazard_walker(Node *node, h
*** 1167,1173 ****
  	{
  		RestrictInfo *rinfo = (RestrictInfo *) node;
  
! 		return has_parallel_hazard_walker((Node *) rinfo->clause, context);
  	}
  
  	/*
--- 1219,1225 ----
  	{
  		RestrictInfo *rinfo = (RestrictInfo *) node;
  
! 		return max_parallel_hazard_walker((Node *) rinfo->clause, context);
  	}
  
  	/*
*************** has_parallel_hazard_walker(Node *node, h
*** 1176,1188 ****
  	 * 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.)
  	 */
  	else if (IsA(node, SubLink) ||
  			 IsA(node, SubPlan) ||
  			 IsA(node, AlternativeSubPlan))
  	{
! 		if (!context->allow_restricted)
  			return true;
  	}
  
--- 1228,1240 ----
  	 * 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
! 	 * max_interesting == restricted scan, but if we do, return true.)
  	 */
  	else if (IsA(node, SubLink) ||
  			 IsA(node, SubPlan) ||
  			 IsA(node, AlternativeSubPlan))
  	{
! 		if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
  			return true;
  	}
  
*************** has_parallel_hazard_walker(Node *node, h
*** 1192,1198 ****
  	 */
  	else if (IsA(node, Param))
  	{
! 		if (!context->allow_restricted)
  			return true;
  	}
  
--- 1244,1250 ----
  	 */
  	else if (IsA(node, Param))
  	{
! 		if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
  			return true;
  	}
  
*************** has_parallel_hazard_walker(Node *node, h
*** 1207,1226 ****
  
  		/* SELECT FOR UPDATE/SHARE must be treated as unsafe */
  		if (query->rowMarks != NULL)
  			return true;
  
  		/* Recurse into subselects */
  		return query_tree_walker(query,
! 								 has_parallel_hazard_walker,
  								 context, 0);
  	}
  
  	/* Recurse to check arguments */
  	return expression_tree_walker(node,
! 								  has_parallel_hazard_walker,
  								  context);
  }
  
  /*****************************************************************************
   *		Check clauses for nonstrict functions
   *****************************************************************************/
--- 1259,1282 ----
  
  		/* 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,
! 								 max_parallel_hazard_walker,
  								 context, 0);
  	}
  
  	/* Recurse to check arguments */
  	return expression_tree_walker(node,
! 								  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 ce7ad54..abb7507 100644
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
*************** create_projection_path(PlannerInfo *root
*** 2178,2184 ****
  	pathnode->path.parallel_aware = false;
  	pathnode->path.parallel_safe = rel->consider_parallel &&
  		subpath->parallel_safe &&
! 		!has_parallel_hazard((Node *) target->exprs, false);
  	pathnode->path.parallel_workers = subpath->parallel_workers;
  	/* Projection does not change the sort order */
  	pathnode->path.pathkeys = subpath->pathkeys;
--- 2178,2184 ----
  	pathnode->path.parallel_aware = false;
  	pathnode->path.parallel_safe = rel->consider_parallel &&
  		subpath->parallel_safe &&
! 		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;
*************** apply_projection_to_path(PlannerInfo *ro
*** 2285,2291 ****
  	 * target expressions, then we can't.
  	 */
  	if (IsA(path, GatherPath) &&
! 		!has_parallel_hazard((Node *) target->exprs, false))
  	{
  		GatherPath *gpath = (GatherPath *) path;
  
--- 2285,2291 ----
  	 * target expressions, then we can't.
  	 */
  	if (IsA(path, GatherPath) &&
! 		is_parallel_safe(root, (Node *) target->exprs))
  	{
  		GatherPath *gpath = (GatherPath *) path;
  
*************** apply_projection_to_path(PlannerInfo *ro
*** 2306,2312 ****
  								   target);
  	}
  	else if (path->parallel_safe &&
! 			 has_parallel_hazard((Node *) target->exprs, false))
  	{
  		/*
  		 * We're inserting a parallel-restricted target list into a path
--- 2306,2312 ----
  								   target);
  	}
  	else if (path->parallel_safe &&
! 			 !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 806600e..deef560 100644
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
*************** build_join_rel(PlannerInfo *root,
*** 513,520 ****
  	 * here.
  	 */
  	if (inner_rel->consider_parallel && outer_rel->consider_parallel &&
! 		!has_parallel_hazard((Node *) restrictlist, false) &&
! 		!has_parallel_hazard((Node *) joinrel->reltarget->exprs, false))
  		joinrel->consider_parallel = true;
  
  	/*
--- 513,520 ----
  	 * here.
  	 */
  	if (inner_rel->consider_parallel && outer_rel->consider_parallel &&
! 		is_parallel_safe(root, (Node *) restrictlist) &&
! 		is_parallel_safe(root, (Node *) joinrel->reltarget->exprs))
  		joinrel->consider_parallel = true;
  
  	/*
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 2be8908..fcfb0d4 100644
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
*************** typedef struct PlannerGlobal
*** 126,131 ****
--- 126,133 ----
  	bool		parallelModeOK; /* parallel mode potentially OK? */
  
  	bool		parallelModeNeeded;		/* parallel mode actually required? */
+ 
+ 	char		maxParallelHazard;		/* worst PROPARALLEL hazard level */
  } PlannerGlobal;
  
  /* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index be7c639..9abef37 100644
*** a/src/include/optimizer/clauses.h
--- b/src/include/optimizer/clauses.h
*************** extern bool contain_subplans(Node *claus
*** 61,67 ****
  extern bool contain_mutable_functions(Node *clause);
  extern bool contain_volatile_functions(Node *clause);
  extern bool contain_volatile_functions_not_nextval(Node *clause);
! extern bool has_parallel_hazard(Node *node, bool allow_restricted);
  extern bool contain_nonstrict_functions(Node *clause);
  extern bool contain_leaked_vars(Node *clause);
  
--- 61,68 ----
  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 contain_nonstrict_functions(Node *clause);
  extern bool contain_leaked_vars(Node *clause);
  
