On Fri, Oct 23, 2015 at 9:22 PM, Amit Kapila <amit.kapil...@gmail.com> wrote: > The base rel's consider_parallel flag won't be percolated to childrels, so > even > if we mark base rel as parallel capable, while generating the path it won't > be considered. I think we need to find a way to pass on that information if > we want to follow this way.
Fixed in the attached version. I added a max_parallel_degree check, too, per your suggestion. > True, we can do that way. What I was trying to convey by above is > that we anyway need checks during path creation atleast in some > of the cases, so why not do all the checks at that time only as I > think all the information will be available at that time. > > I think if we store parallelism related info in RelOptInfo, that can also > be made to work, but the only worry I have with that approach is we > need to have checks at two levels one at RelOptInfo formation time > and other at Path formation time. I don't really see that as a problem. What I'm thinking about doing (but it's not implemented in the attached patch) is additionally adding a ppi_consider_parallel flag to the ParamPathInfo. This would be meaningful only for baserels, and would indicate whether the ParamPathInfo's ppi_clauses are parallel-safe. If we're thinking about adding a parallel path to a baserel, we need the RelOptInfo to have consider_parallel set and, if there is a ParamPathInfo, we need the ParamPathInfo's ppi_consider_parallel flag to be set also. That shows that both the rel's baserestrictinfo and the paramaterized join clauses are parallel-safe. For a joinrel, we can add a path if (1) the joinrel has consider_parallel set and (2) the paths being joined are parallel-safe. Testing condition (2) will require a per-Path flag, so we'll end up with one flag in the RelOptInfo, a second in the ParamPathInfo, and a third in the Path. That doesn't seem like a problem, though: it's a sign that we're doing this in a way that fits into the existing infrastructure, and it should be pretty efficient. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
From e31d5f3f4c53d80e87a74925db88bcaf2e6fa564 Mon Sep 17 00:00:00 2001 From: Robert Haas <rh...@postgresql.org> Date: Fri, 2 Oct 2015 23:57:46 -0400 Subject: [PATCH 2/7] Strengthen planner infrastructure for parallelism. Add a new flag, consider_parallel, to each RelOptInfo, indicating whether a plan for that relation could conceivably be run inside of a parallel worker. Right now, we're pretty conservative: for example, it might be possible to defer applying a parallel-restricted qual in a worker, and later do it in the leader, but right now we just don't try to parallelize access to that relation. That's probably the right decision in most cases, anyway. --- src/backend/nodes/outfuncs.c | 1 + src/backend/optimizer/path/allpaths.c | 155 +++++++++++++++++++++++++++- src/backend/optimizer/plan/planmain.c | 12 +++ src/backend/optimizer/plan/planner.c | 9 +- src/backend/optimizer/util/clauses.c | 183 +++++++++++++++++++++++++++------- src/backend/optimizer/util/relnode.c | 21 ++++ src/backend/utils/cache/lsyscache.c | 22 ++++ src/include/nodes/relation.h | 1 + src/include/optimizer/clauses.h | 2 +- src/include/utils/lsyscache.h | 1 + 10 files changed, 364 insertions(+), 43 deletions(-) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 3e75cd1..0030a9b 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1868,6 +1868,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node) WRITE_INT_FIELD(width); WRITE_BOOL_FIELD(consider_startup); WRITE_BOOL_FIELD(consider_param_startup); + WRITE_BOOL_FIELD(consider_parallel); WRITE_NODE_FIELD(reltargetlist); WRITE_NODE_FIELD(pathlist); WRITE_NODE_FIELD(ppilist); diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 8fc1cfd..105e544 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -21,6 +21,7 @@ #include "access/tsmapi.h" #include "catalog/pg_class.h" #include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" #include "foreign/fdwapi.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -71,6 +72,9 @@ static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); +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, @@ -158,7 +162,8 @@ make_one_rel(PlannerInfo *root, List *joinlist) set_base_rel_consider_startup(root); /* - * Generate access paths for the base rels. + * Generate access paths for the base rels. set_base_rel_sizes also + * sets the consider_parallel flag for each baserel, if appropriate. */ set_base_rel_sizes(root); set_base_rel_pathlists(root); @@ -222,9 +227,12 @@ set_base_rel_consider_startup(PlannerInfo *root) /* * set_base_rel_sizes * Set the size estimates (rows and widths) for each base-relation entry. + * Also determine whether to consider parallel paths for base relations. * * We do this in a separate pass over the base rels so that rowcount - * estimates are available for parameterized path generation. + * estimates are available for parameterized path generation, and also so + * that the consider_parallel flag is set correctly before we begin to + * generate paths. */ static void set_base_rel_sizes(PlannerInfo *root) @@ -234,6 +242,7 @@ set_base_rel_sizes(PlannerInfo *root) for (rti = 1; rti < root->simple_rel_array_size; rti++) { RelOptInfo *rel = root->simple_rel_array[rti]; + RangeTblEntry *rte; /* there may be empty slots corresponding to non-baserel RTEs */ if (rel == NULL) @@ -245,7 +254,19 @@ set_base_rel_sizes(PlannerInfo *root) if (rel->reloptkind != RELOPT_BASEREL) continue; - set_rel_size(root, rel, rti, root->simple_rte_array[rti]); + rte = root->simple_rte_array[rti]; + + /* + * If parallelism is allowable for this query in general, see whether + * it's allowable for this rel in particular. We have to do this + * before set_rel_size, because that if this is an inheritance parent, + * set_append_rel_size will pass the consider_parallel flag down to + * inheritance children. + */ + if (root->glob->parallelModeOK) + set_rel_consider_parallel(root, rel, rte); + + set_rel_size(root, rel, rti, rte); } } @@ -459,6 +480,131 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) } /* + * If this relation could possibly be scanned from within a worker, then set + * the consider_parallel flag. The flag has previously been initialized to + * false, so we just bail out if it becomes clear that we can't safely set it. + */ +static void +set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel, + RangeTblEntry *rte) +{ + /* Don't call this if parallelism is disallowed for the entire query. */ + Assert(root->glob->parallelModeOK); + + /* Don't call this for non-baserels. */ + Assert(rel->reloptkind == RELOPT_BASEREL); + + /* Assorted checks based on rtekind. */ + switch (rte->rtekind) + { + case RTE_RELATION: + /* + * Currently, parallel workers can't access the leader's temporary + * tables. We could possibly relax this if the wrote all of its + * local buffers at the start of the query and made no changes + * thereafter (maybe we could allow hint bit changes), and if we + * taught the workers to read them. Writing a large number of + * temporary buffers could be expensive, though, and we don't have + * the rest of the necessary infrastructure right now anyway. So + * for now, bail out if we see a temporary table. + */ + if (get_rel_persistence(rte->relid) == RELPERSISTENCE_TEMP) + return; + + /* + * Table sampling can be pushed down to workers if the sample + * function and its arguments are safe. + */ + if (rte->tablesample != NULL) + { + Oid proparallel = func_parallel(rte->tablesample->tsmhandler); + + if (proparallel != PROPARALLEL_SAFE) + return; + if (has_parallel_hazard((Node *) rte->tablesample->args, + false)) + return; + return; + } + break; + + case RTE_SUBQUERY: + /* + * Subplans currently aren't passed to workers. Even if they + * were, the subplan might be using parallelism internally, and + * we can't support nested Gather nodes at present. Finally, + * we don't have a good way of knowing whether the subplan + * involves any parallel-restricted operations. It would be + * nice to relax this restriction some day, but it's going to + * take a fair amount of work. + */ + return; + + case RTE_JOIN: + /* Shouldn't happen; we're only considering baserels here. */ + Assert(false); + return; + + case RTE_FUNCTION: + /* Check for parallel-restricted functions. */ + if (!function_rte_parallel_ok(rte)) + return; + break; + + case RTE_VALUES: + /* + * The data for a VALUES clause is stored in the plan tree itself, + * so scanning it in a worker is fine. + */ + break; + + case RTE_CTE: + /* + * CTE tuplestores aren't shared among parallel workers, so we + * force all CTE scans to happen in the leader. Also, populating + * the CTE would require executing a subplan that's not available + * in the worker, might be parallel-restricted, and must get + * executed only once. + */ + return; + } + + /* + * If there's anything in baserestrictinfo that's parallel-restricted, + * we give up on parallelizing access to this relation. We could consider + * instead postponing application of the restricted quals until we're + * above all the parallelism in the plan tree, but it's not clear that + * this would be a win in very many cases, and it might be tricky to make + * outer join clauses work correctly. + */ + if (has_parallel_hazard((Node *) rel->baserestrictinfo, false)) + return; + + /* We have a winner. */ + 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 * Build access paths for a plain relation (no subquery, no inheritance) */ @@ -714,6 +860,9 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, continue; } + /* Copy consider_parallel flag from parent. */ + childrel->consider_parallel = rel->consider_parallel; + /* * CE failed, so finish copying/modifying targetlist and join quals. * diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 848df97..d73e7c0 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -20,6 +20,7 @@ */ #include "postgres.h" +#include "optimizer/clauses.h" #include "optimizer/orclauses.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -70,6 +71,17 @@ query_planner(PlannerInfo *root, List *tlist, /* We need a dummy joinrel to describe the empty set of baserels */ final_rel = build_empty_join_rel(root); + /* + * 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 *) create_result_path((List *) parse->jointree->quals)); diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 569fe55..bddd7ba 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -239,7 +239,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) /* * Assess whether it's feasible to use parallel mode for this query. * We can't do this in a standalone backend, or if the command will - * try to modify any data, or if this is a cursor operation, or if any + * try to modify any data, or if this is a cursor operation, or if + * GUCs are set to values that don't permit parallelism, or if * parallel-unsafe functions are present in the query tree. * * For now, we don't try to use parallel mode if we're running inside @@ -258,9 +259,9 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) 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 && !IsParallelWorker() && - !IsolationIsSerializable() && - !contain_parallel_unsafe((Node *) parse); + parse->utilityStmt == NULL && max_parallel_degree > 0 && + !IsParallelWorker() && !IsolationIsSerializable() && + !has_parallel_hazard((Node *) parse, true); /* * glob->parallelModeOK should tell us whether it's necessary to impose diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f2c8551..915c8a4 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -21,6 +21,7 @@ #include "access/htup_details.h" #include "catalog/pg_aggregate.h" +#include "catalog/pg_class.h" #include "catalog/pg_language.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" @@ -87,6 +88,11 @@ typedef struct char *prosrc; } inline_error_callback_arg; +typedef struct +{ + bool allow_restricted; +} has_parallel_hazard_arg; + static bool contain_agg_clause_walker(Node *node, void *context); static bool count_agg_clauses_walker(Node *node, count_agg_clauses_context *context); @@ -96,7 +102,11 @@ 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 contain_parallel_unsafe_walker(Node *node, void *context); +static bool has_parallel_hazard_walker(Node *node, + has_parallel_hazard_arg *context); +static bool parallel_too_dangerous(char proparallel, + has_parallel_hazard_arg *context); +static bool typeid_is_temp(Oid typeid); static bool contain_nonstrict_functions_walker(Node *node, void *context); static bool contain_leaked_vars_walker(Node *node, void *context); static Relids find_nonnullable_rels_walker(Node *node, bool top_level); @@ -1200,63 +1210,159 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context) } /***************************************************************************** - * Check queries for parallel-unsafe constructs + * 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, and also to evaluate whether a particular expression is safe to + * run in a parallel worker. We could separate these concerns into two + * different functions, but there's enough overlap that it doesn't seem + * worthwhile. + */ bool -contain_parallel_unsafe(Node *node) +has_parallel_hazard(Node *node, bool allow_restricted) { - return contain_parallel_unsafe_walker(node, NULL); + has_parallel_hazard_arg context; + + context.allow_restricted = allow_restricted; + return has_parallel_hazard_walker(node, &context); } static bool -contain_parallel_unsafe_walker(Node *node, void *context) +has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context) { if (node == NULL) return false; + + /* + * When we're first invoked on a completely unplanned tree, we must + * recurse through Query objects to as to locate parallel-unsafe + * constructs anywhere in the tree. + * + * Later, we'll be called again for specific quals, possibly after + * some planning has been done, we may encounter SubPlan, SubLink, + * or AlternativeSubLink nodes. Currently, there's no need to recurse + * through these; they can't be unsafe, since we've already cleared + * the entire query of unsafe operations, and they're definitely + * parallel-restricted. + */ + if (IsA(node, Query)) + { + Query *query = (Query *) node; + + if (query->rowMarks != NULL) + return true; + + /* Recurse into subselects */ + return query_tree_walker(query, + has_parallel_hazard_walker, + context, 0); + } + else if (IsA(node, SubPlan) || IsA(node, SubLink) || + IsA(node, AlternativeSubPlan) || IsA(node, Param)) + { + /* + * Since we don't have the ability to push subplans down to workers + * at present, we treat subplan references as parallel-restricted. + */ + if (!context->allow_restricted) + return true; + } + + /* This is just a notational convenience for callers. */ + if (IsA(node, RestrictInfo)) + { + RestrictInfo *rinfo = (RestrictInfo *) node; + return has_parallel_hazard_walker((Node *) rinfo->clause, context); + } + + /* + * It is an error for a parallel worker to touch a temporary table in any + * way, so we can't handle nodes whose type is the rowtype of such a table. + */ + if (!context->allow_restricted) + { + switch (nodeTag(node)) + { + case T_Var: + case T_Const: + case T_Param: + case T_Aggref: + case T_WindowFunc: + case T_ArrayRef: + case T_FuncExpr: + case T_NamedArgExpr: + case T_OpExpr: + case T_DistinctExpr: + case T_NullIfExpr: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_CaseTestExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_CoerceToDomain: + case T_CoerceToDomainValue: + case T_SetToDefault: + if (typeid_is_temp(exprType(node))) + return true; + break; + default: + break; + } + } + + /* + * For each node that might potentially call a function, we need to + * examine the pg_proc.proparallel marking for that function to see + * whether it's safe enough for the current value of allow_restricted. + */ if (IsA(node, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; - if (func_parallel(expr->funcid) == PROPARALLEL_UNSAFE) + if (parallel_too_dangerous(func_parallel(expr->funcid), context)) return true; - /* else fall through to check args */ } else if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; set_opfuncid(expr); - if (func_parallel(expr->opfuncid) == PROPARALLEL_UNSAFE) + if (parallel_too_dangerous(func_parallel(expr->opfuncid), context)) return true; - /* else fall through to check args */ } else if (IsA(node, DistinctExpr)) { DistinctExpr *expr = (DistinctExpr *) node; set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ - if (func_parallel(expr->opfuncid) == PROPARALLEL_UNSAFE) + if (parallel_too_dangerous(func_parallel(expr->opfuncid), context)) return true; - /* else fall through to check args */ } else if (IsA(node, NullIfExpr)) { NullIfExpr *expr = (NullIfExpr *) node; set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ - if (func_parallel(expr->opfuncid) == PROPARALLEL_UNSAFE) + if (parallel_too_dangerous(func_parallel(expr->opfuncid), context)) return true; - /* else fall through to check args */ } else if (IsA(node, ScalarArrayOpExpr)) { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; set_sa_opfuncid(expr); - if (func_parallel(expr->opfuncid) == PROPARALLEL_UNSAFE) + if (parallel_too_dangerous(func_parallel(expr->opfuncid), context)) return true; - /* else fall through to check args */ } else if (IsA(node, CoerceViaIO)) { @@ -1268,54 +1374,61 @@ contain_parallel_unsafe_walker(Node *node, void *context) /* check the result type's input function */ getTypeInputInfo(expr->resulttype, &iofunc, &typioparam); - if (func_parallel(iofunc) == PROPARALLEL_UNSAFE) + if (parallel_too_dangerous(func_parallel(iofunc), context)) return true; /* check the input type's output function */ getTypeOutputInfo(exprType((Node *) expr->arg), &iofunc, &typisvarlena); - if (func_parallel(iofunc) == PROPARALLEL_UNSAFE) + if (parallel_too_dangerous(func_parallel(iofunc), context)) return true; - /* else fall through to check args */ } else if (IsA(node, ArrayCoerceExpr)) { ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; if (OidIsValid(expr->elemfuncid) && - func_parallel(expr->elemfuncid) == PROPARALLEL_UNSAFE) + parallel_too_dangerous(func_parallel(expr->elemfuncid), context)) return true; - /* else fall through to check args */ } else if (IsA(node, RowCompareExpr)) { - /* RowCompare probably can't have volatile ops, but check anyway */ RowCompareExpr *rcexpr = (RowCompareExpr *) node; ListCell *opid; foreach(opid, rcexpr->opnos) { - if (op_volatile(lfirst_oid(opid)) == PROPARALLEL_UNSAFE) + Oid opfuncid = get_opcode(lfirst_oid(opid)); + if (parallel_too_dangerous(func_parallel(opfuncid), context)) return true; } - /* else fall through to check args */ } - else if (IsA(node, Query)) - { - Query *query = (Query *) node; - if (query->rowMarks != NULL) - return true; - - /* Recurse into subselects */ - return query_tree_walker(query, - contain_parallel_unsafe_walker, - context, 0); - } + /* ... and recurse to check substructure */ return expression_tree_walker(node, - contain_parallel_unsafe_walker, + has_parallel_hazard_walker, context); } +static bool +parallel_too_dangerous(char proparallel, has_parallel_hazard_arg *context) +{ + if (context->allow_restricted) + return proparallel == PROPARALLEL_UNSAFE; + else + return proparallel != PROPARALLEL_SAFE; +} + +static bool +typeid_is_temp(Oid typeid) +{ + Oid relid = get_typ_typrelid(typeid); + + if (!OidIsValid(relid)) + return false; + + return (get_rel_persistence(relid) == RELPERSISTENCE_TEMP); +} + /***************************************************************************** * Check clauses for nonstrict functions *****************************************************************************/ diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 68a93a1..996b7fe 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -102,6 +103,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) /* cheap startup cost is interesting iff not all tuples to be retrieved */ rel->consider_startup = (root->tuple_fraction > 0); rel->consider_param_startup = false; /* might get changed later */ + rel->consider_parallel = false; /* might get changed later */ rel->reltargetlist = NIL; rel->pathlist = NIL; rel->ppilist = NIL; @@ -363,6 +365,7 @@ build_join_rel(PlannerInfo *root, /* cheap startup cost is interesting iff not all tuples to be retrieved */ joinrel->consider_startup = (root->tuple_fraction > 0); joinrel->consider_param_startup = false; + joinrel->consider_parallel = false; joinrel->reltargetlist = NIL; joinrel->pathlist = NIL; joinrel->ppilist = NIL; @@ -442,6 +445,24 @@ build_join_rel(PlannerInfo *root, sjinfo, restrictlist); /* + * Set the consider_parallel flag if this joinrel could potentially be + * scanned within a parallel worker. If this flag is false for either + * inner_rel or outer_rel, then it must be false for the joinrel also. + * Even if both are true, there might be parallel-restricted quals at + * our level. + * + * Note that if there are more than two rels in this relation, they + * could be divided between inner_rel and outer_rel in any arbitary + * way. We assume this doesn't matter, because we should hit all the + * same baserels and joinclauses while building up to this joinrel no + * matter which we take; therefore, we should make the same decision + * here however we get here. + */ + if (inner_rel->consider_parallel && outer_rel->consider_parallel && + !has_parallel_hazard((Node *) restrictlist, false)) + joinrel->consider_parallel = true; + + /* * Add the joinrel to the query's joinrel list, and store it into the * auxiliary hashtable if there is one. NB: GEQO requires us to append * the new joinrel to the end of the list! diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 8d1cdf1..093da76 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -1787,6 +1787,28 @@ get_rel_tablespace(Oid relid) return InvalidOid; } +/* + * get_rel_persistence + * + * Returns the relpersistence associated with a given relation. + */ +char +get_rel_persistence(Oid relid) +{ + HeapTuple tp; + Form_pg_class reltup; + char result; + + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for relation %u", relid); + reltup = (Form_pg_class) GETSTRUCT(tp); + result = reltup->relpersistence; + ReleaseSysCache(tp); + + return result; +} + /* ---------- TRANSFORM CACHE ---------- */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 6cf2e24..41be9b1 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -452,6 +452,7 @@ typedef struct RelOptInfo /* per-relation planner control flags */ bool consider_startup; /* keep cheap-startup-cost paths? */ bool consider_param_startup; /* ditto, for parameterized paths? */ + bool consider_parallel; /* consider parallel paths? */ /* materialization information */ List *reltargetlist; /* Vars to be output by scan of relation */ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 5ac79b1..323f093 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -62,7 +62,7 @@ extern bool contain_subplans(Node *clause); 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 contain_parallel_unsafe(Node *node); +extern bool has_parallel_hazard(Node *node, bool allow_restricted); extern bool contain_nonstrict_functions(Node *clause); extern bool contain_leaked_vars(Node *clause); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 450d9fe..dcc421f 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -103,6 +103,7 @@ extern Oid get_rel_namespace(Oid relid); extern Oid get_rel_type_id(Oid relid); extern char get_rel_relkind(Oid relid); extern Oid get_rel_tablespace(Oid relid); +extern char get_rel_persistence(Oid relid); extern Oid get_transform_fromsql(Oid typid, Oid langid, List *trftypes); extern Oid get_transform_tosql(Oid typid, Oid langid, List *trftypes); extern bool get_typisdefined(Oid typid); -- 2.3.8 (Apple Git-58)
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers