diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 53c43db011..ab0fa77ec4 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -98,6 +98,8 @@ typedef struct char max_hazard; /* worst proparallel hazard found so far */ char max_interesting; /* worst proparallel hazard of interest */ List *safe_param_ids; /* PARAM_EXEC Param IDs to treat as safe */ + RangeTblEntry *targetRTE; /* query's target relation if any */ + CmdType commandType; } max_parallel_hazard_context; static bool contain_agg_clause_walker(Node *node, void *context); @@ -108,6 +110,15 @@ 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 target_rel_max_parallel_hazard(max_parallel_hazard_context *context); +static bool target_rel_max_parallel_hazard_recurse(Relation relation, + CmdType command_type, + max_parallel_hazard_context *context); +static bool target_rel_trigger_max_parallel_hazard(TriggerDesc *trigdesc, + max_parallel_hazard_context *context); +static bool target_rel_index_expr_max_parallel_hazard(Relation rel, + max_parallel_hazard_context *context); +static bool target_rel_domain_max_parallel_hazard(Oid typid, max_parallel_hazard_context *context); static bool contain_nonstrict_functions_walker(Node *node, void *context); static bool contain_exec_param_walker(Node *node, List *param_ids); static bool contain_context_dependent_node(Node *clause); @@ -158,15 +169,6 @@ static Query *substitute_actual_srf_parameters(Query *expr, static Node *substitute_actual_srf_parameters_mutator(Node *node, substitute_actual_srf_parameters_context *context); -static bool trigger_max_parallel_hazard_for_modify(TriggerDesc *trigdesc, - max_parallel_hazard_context *context); -static bool index_expr_max_parallel_hazard_for_modify(Relation rel, - max_parallel_hazard_context *context); -static bool domain_max_parallel_hazard_for_modify(Oid typid, max_parallel_hazard_context *context); -static bool rel_max_parallel_hazard_for_modify(Relation rel, - CmdType command_type, - max_parallel_hazard_context *context); - /***************************************************************************** * Aggregate-function clause manipulation *****************************************************************************/ @@ -570,19 +572,11 @@ max_parallel_hazard(Query *parse) context.max_hazard = PROPARALLEL_SAFE; context.max_interesting = PROPARALLEL_UNSAFE; context.safe_param_ids = NIL; + context.targetRTE = parse->resultRelation > 0 ? + rt_fetch(parse->resultRelation, parse->rtable) : NULL; + context.commandType = parse->commandType; (void) max_parallel_hazard_walker((Node *) parse, &context); - /* - * Additional parallel-mode safety checks are required in order to - * allow an underlying parallel query to be used for a - * table-modification command that is supported in parallel-mode. - */ - - if (context.max_hazard != PROPARALLEL_UNSAFE && - IsModifySupportedInParallelMode(parse->commandType)) - { - context.max_hazard = max_parallel_hazard_for_modify(parse, context.max_hazard); - } return context.max_hazard; } @@ -787,6 +781,19 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) } return false; /* nothing to recurse to */ } + else if (IsA(node, RangeTblEntry)) + { + RangeTblEntry *rte = (RangeTblEntry *) node; + + /* Nothing interesting to check for SELECTs */ + if (context->targetRTE == NULL) + return false; + + if (rte == context->targetRTE) + return target_rel_max_parallel_hazard(context); + + return false; + } /* * When we're first invoked on a completely unplanned tree, we must @@ -804,10 +811,28 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) return true; } + /* + * UPDATE is not currently supported in parallel-mode, so prohibit + * INSERT...ON CONFLICT...DO UPDATE... + * In order to support update, even if only in the leader, some + * further work would need to be done. A mechanism would be needed + * for sharing combo-cids between leader and workers during + * parallel-mode, since for example, the leader might generate a + * combo-cid and it needs to be propagated to the workers. + */ + if (query->onConflict != NULL && + query->onConflict->action == ONCONFLICT_UPDATE) + { + context->max_hazard = PROPARALLEL_UNSAFE; + return true; + } + /* Recurse into subselects */ return query_tree_walker(query, max_parallel_hazard_walker, - context, 0); + context, + context->targetRTE != NULL ? + QTW_EXAMINE_RTES_BEFORE: 0); } /* Recurse to check arguments */ @@ -817,12 +842,12 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) } /* - * trigger_max_parallel_hazard_for_modify + * target_rel_trigger_max_parallel_hazard * * Finds the maximum parallel-mode hazard level for the specified trigger data. */ static bool -trigger_max_parallel_hazard_for_modify(TriggerDesc *trigdesc, +target_rel_trigger_max_parallel_hazard(TriggerDesc *trigdesc, max_parallel_hazard_context *context) { int i; @@ -853,13 +878,13 @@ trigger_max_parallel_hazard_for_modify(TriggerDesc *trigdesc, } /* - * index_expr_max_parallel_hazard_for_modify + * target_rel_index_expr_max_parallel_hazard * * Finds the maximum parallel-mode hazard level for any existing index * expressions of a specified relation. */ static bool -index_expr_max_parallel_hazard_for_modify(Relation rel, +target_rel_index_expr_max_parallel_hazard(Relation rel, max_parallel_hazard_context *context) { List *index_oid_list; @@ -924,7 +949,7 @@ index_expr_max_parallel_hazard_for_modify(Relation rel, } /* - * domain_max_parallel_hazard_for_modify + * target_rel_domain_max_parallel_hazard * * Finds the maximum parallel-mode hazard level for the specified DOMAIN type. * Only any CHECK expressions are examined for parallel safety. @@ -934,7 +959,7 @@ index_expr_max_parallel_hazard_for_modify(Relation rel, * */ static bool -domain_max_parallel_hazard_for_modify(Oid typid, max_parallel_hazard_context *context) +target_rel_domain_max_parallel_hazard(Oid typid, max_parallel_hazard_context *context) { Relation con_rel; ScanKeyData key[1]; @@ -994,15 +1019,31 @@ domain_max_parallel_hazard_for_modify(Oid typid, max_parallel_hazard_context *co } /* - * rel_max_parallel_hazard_for_modify + * target_rel_max_parallel_hazard * * Determines the maximum parallel-mode hazard level for modification * of a specified relation. */ static bool -rel_max_parallel_hazard_for_modify(Relation rel, - CmdType command_type, - max_parallel_hazard_context *context) +target_rel_max_parallel_hazard(max_parallel_hazard_context *context) +{ + Relation targetRel = table_open(context->targetRTE->relid, + context->targetRTE->rellockmode); + bool max_hazard_found; + + max_hazard_found = target_rel_max_parallel_hazard_recurse(targetRel, + context->commandType, + context); + + table_close(targetRel, NoLock); + + return max_hazard_found; +} + +static bool +target_rel_max_parallel_hazard_recurse(Relation rel, + CmdType command_type, + max_parallel_hazard_context *context) { TupleDesc tupdesc; int attnum; @@ -1076,9 +1117,9 @@ rel_max_parallel_hazard_for_modify(Relation rel, Relation part_rel; part_rel = table_open(pdesc->oids[i], AccessShareLock); - max_hazard_found = rel_max_parallel_hazard_for_modify(part_rel, - command_type, - context); + max_hazard_found = target_rel_max_parallel_hazard_recurse(part_rel, + command_type, + context); table_close(part_rel, AccessShareLock); if (max_hazard_found) { @@ -1091,7 +1132,7 @@ rel_max_parallel_hazard_for_modify(Relation rel, * If there are any index expressions, check that they are parallel-mode * safe. */ - if (index_expr_max_parallel_hazard_for_modify(rel, context)) + if (target_rel_index_expr_max_parallel_hazard(rel, context)) { return true; } @@ -1101,7 +1142,7 @@ rel_max_parallel_hazard_for_modify(Relation rel, */ if (rel->trigdesc != NULL) { - if (trigger_max_parallel_hazard_for_modify(rel->trigdesc, context)) + if (target_rel_trigger_max_parallel_hazard(rel->trigdesc, context)) { return true; } @@ -1136,7 +1177,7 @@ rel_max_parallel_hazard_for_modify(Relation rel, */ if (get_typtype(att->atttypid) == TYPTYPE_DOMAIN) { - if (domain_max_parallel_hazard_for_modify(att->atttypid, context)) + if (target_rel_domain_max_parallel_hazard(att->atttypid, context)) { return true; } @@ -1168,77 +1209,6 @@ rel_max_parallel_hazard_for_modify(Relation rel, return false; } -/* - * max_parallel_hazard_for_modify - * - * Determines the worst parallel-mode hazard level for the specified - * table-modification statement, based on the statement attributes and - * target table. An initial max parallel hazard level may optionally be - * supplied. The search returns the earliest in the following list: - * PROPARALLEL_UNSAFE, PROPARALLEL_RESTRICTED, PROPARALLEL_SAFE - */ -char -max_parallel_hazard_for_modify(Query *parse, char initial_max_parallel_hazard) -{ - RangeTblEntry *rte; - ListCell *lc; - bool hasSubQuery; - max_parallel_hazard_context context; - Relation rel; - - - /* - * UPDATE is not currently supported in parallel-mode, so prohibit - * INSERT...ON CONFLICT...DO UPDATE... - * In order to support update, even if only in the leader, some - * further work would need to be done. A mechanism would be needed - * for sharing combo-cids between leader and workers during - * parallel-mode, since for example, the leader might generate a - * combo-cid and it needs to be propagated to the workers. - */ - if (parse->onConflict != NULL && parse->onConflict->action == ONCONFLICT_UPDATE) - return PROPARALLEL_UNSAFE; - - /* - * If there is no underlying SELECT, a parallel table-modification - * operation is not possible (nor desirable). - */ - hasSubQuery = false; - foreach(lc, parse->rtable) - { - rte = lfirst_node(RangeTblEntry, lc); - if (rte->rtekind == RTE_SUBQUERY) - { - hasSubQuery = true; - break; - } - } - if (!hasSubQuery) - return PROPARALLEL_UNSAFE; - - /* - * Setup the context used in finding the max parallel-mode hazard. - */ - Assert(initial_max_parallel_hazard == 0 || - initial_max_parallel_hazard == PROPARALLEL_SAFE || - initial_max_parallel_hazard == PROPARALLEL_RESTRICTED); - context.max_hazard = initial_max_parallel_hazard == 0 ? - PROPARALLEL_SAFE : initial_max_parallel_hazard; - context.max_interesting = PROPARALLEL_UNSAFE; - context.safe_param_ids = NIL; - - rte = rt_fetch(parse->resultRelation, parse->rtable); - - /* - * The target table is already locked by the caller (this is done in the - * parse/analyze phase). - */ - rel = table_open(rte->relid, NoLock); - (void) rel_max_parallel_hazard_for_modify(rel, parse->commandType, &context); - table_close(rel, NoLock); - return context.max_hazard; -} - /***************************************************************************** * Check clauses for nonstrict functions *****************************************************************************/