diff options
| author | Robert Haas | 2015-09-16 19:38:47 +0000 |
|---|---|---|
| committer | Robert Haas | 2015-09-16 19:38:47 +0000 |
| commit | 7aea8e4f2daa4b39ca9d1309a0c4aadb0f7ed81b (patch) | |
| tree | f92032ada7ea070c55e6173bdb70550027a4dc46 /src/backend | |
| parent | b44d92b67b65a76f92448b5a282aae72820ac676 (diff) | |
Determine whether it's safe to attempt a parallel plan for a query.
Commit 924bcf4f16d54c55310b28f77686608684734f42 introduced a framework
for parallel computation in PostgreSQL that makes most but not all
built-in functions safe to execute in parallel mode. In order to have
parallel query, we'll need to be able to determine whether that query
contains functions (either built-in or user-defined) that cannot be
safely executed in parallel mode. This requires those functions to be
labeled, so this patch introduces an infrastructure for that. Some
functions currently labeled as safe may need to be revised depending on
how pending issues related to heavyweight locking under paralllelism
are resolved.
Parallel plans can't be used except for the case where the query will
run to completion. If portal execution were suspended, the parallel
mode restrictions would need to remain in effect during that time, but
that might make other queries fail. Therefore, this patch introduces
a framework that enables consideration of parallel plans only when it
is known that the plan will be run to completion. This probably needs
some refinement; for example, at bind time, we do not know whether a
query run via the extended protocol will be execution to completion or
run with a limited fetch count. Having the client indicate its
intentions at bind time would constitute a wire protocol break. Some
contexts in which parallel mode would be safe are not adjusted by this
patch; the default is not to try parallel plans except from call sites
that have been updated to say that such plans are OK.
This commit doesn't introduce any parallel paths or plans; it just
provides a way to determine whether they could potentially be used.
I'm committing it on the theory that the remaining parallel sequential
scan patches will also get committed to this release, hopefully in the
not-too-distant future.
Robert Haas and Amit Kapila. Reviewed (in earlier versions) by Noah
Misch.
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/catalog/pg_aggregate.c | 1 | ||||
| -rw-r--r-- | src/backend/catalog/pg_proc.c | 2 | ||||
| -rw-r--r-- | src/backend/commands/explain.c | 2 | ||||
| -rw-r--r-- | src/backend/commands/extension.c | 2 | ||||
| -rw-r--r-- | src/backend/commands/functioncmds.c | 51 | ||||
| -rw-r--r-- | src/backend/commands/proclang.c | 3 | ||||
| -rw-r--r-- | src/backend/commands/typecmds.c | 1 | ||||
| -rw-r--r-- | src/backend/executor/execMain.c | 10 | ||||
| -rw-r--r-- | src/backend/executor/functions.c | 4 | ||||
| -rw-r--r-- | src/backend/nodes/copyfuncs.c | 1 | ||||
| -rw-r--r-- | src/backend/nodes/outfuncs.c | 3 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/planner.c | 45 | ||||
| -rw-r--r-- | src/backend/optimizer/util/clauses.c | 118 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 9 | ||||
| -rw-r--r-- | src/backend/tcop/postgres.c | 3 | ||||
| -rw-r--r-- | src/backend/utils/cache/lsyscache.c | 19 |
16 files changed, 263 insertions, 11 deletions
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 009ac398ee4..121c27f105d 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -540,6 +540,7 @@ AggregateCreate(const char *aggName, false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ + PROPARALLEL_UNSAFE, parameterTypes, /* paramTypes */ allParameterTypes, /* allParamTypes */ parameterModes, /* parameterModes */ diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 7765be4be43..9567423d2fb 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -83,6 +83,7 @@ ProcedureCreate(const char *procedureName, bool isLeakProof, bool isStrict, char volatility, + char parallel, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, @@ -344,6 +345,7 @@ ProcedureCreate(const char *procedureName, values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); + values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults)); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 5d06fa4ea65..4f32400011f 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -348,7 +348,7 @@ ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es, INSTR_TIME_SET_CURRENT(planstart); /* plan the query */ - plan = pg_plan_query(query, 0, params); + plan = pg_plan_query(query, CURSOR_OPT_PARALLEL_OK, params); INSTR_TIME_SET_CURRENT(planduration); INSTR_TIME_SUBTRACT(planduration, planstart); diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 2b1dcd0d19c..6b92bdc5e0e 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -707,7 +707,7 @@ execute_sql_string(const char *sql, const char *filename) sql, NULL, 0); - stmt_list = pg_plan_queries(stmt_list, 0, NULL); + stmt_list = pg_plan_queries(stmt_list, CURSOR_OPT_PARALLEL_OK, NULL); foreach(lc2, stmt_list) { diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index dbbb2d3f88a..efc3aa4b9cc 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -465,7 +465,8 @@ compute_common_attribute(DefElem *defel, DefElem **leakproof_item, List **set_items, DefElem **cost_item, - DefElem **rows_item) + DefElem **rows_item, + DefElem **parallel_item) { if (strcmp(defel->defname, "volatility") == 0) { @@ -513,6 +514,13 @@ compute_common_attribute(DefElem *defel, *rows_item = defel; } + else if (strcmp(defel->defname, "parallel") == 0) + { + if (*parallel_item) + goto duplicate_error; + + *parallel_item = defel; + } else return false; @@ -544,6 +552,27 @@ interpret_func_volatility(DefElem *defel) } } +static char +interpret_func_parallel(DefElem *defel) +{ + char *str = strVal(defel->arg); + + if (strcmp(str, "safe") == 0) + return PROPARALLEL_SAFE; + else if (strcmp(str, "unsafe") == 0) + return PROPARALLEL_UNSAFE; + else if (strcmp(str, "restricted") == 0) + return PROPARALLEL_RESTRICTED; + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parallel option \"%s\" not recognized", + str))); + return PROPARALLEL_UNSAFE; /* keep compiler quiet */ + } +} + /* * Update a proconfig value according to a list of VariableSetStmt items. * @@ -592,7 +621,8 @@ compute_attributes_sql_style(List *options, bool *leakproof_p, ArrayType **proconfig, float4 *procost, - float4 *prorows) + float4 *prorows, + char *parallel_p) { ListCell *option; DefElem *as_item = NULL; @@ -606,6 +636,7 @@ compute_attributes_sql_style(List *options, List *set_items = NIL; DefElem *cost_item = NULL; DefElem *rows_item = NULL; + DefElem *parallel_item = NULL; foreach(option, options) { @@ -650,7 +681,8 @@ compute_attributes_sql_style(List *options, &leakproof_item, &set_items, &cost_item, - &rows_item)) + &rows_item, + ¶llel_item)) { /* recognized common option */ continue; @@ -712,6 +744,8 @@ compute_attributes_sql_style(List *options, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ROWS must be positive"))); } + if (parallel_item) + *parallel_p = interpret_func_parallel(parallel_item); } @@ -858,6 +892,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) HeapTuple languageTuple; Form_pg_language languageStruct; List *as_clause; + char parallel; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, @@ -878,13 +913,14 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) proconfig = NULL; procost = -1; /* indicates not set */ prorows = -1; /* indicates not set */ + parallel = PROPARALLEL_UNSAFE; /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, &as_clause, &language, &transformDefElem, &isWindowFunc, &volatility, &isStrict, &security, &isLeakProof, - &proconfig, &procost, &prorows); + &proconfig, &procost, &prorows, ¶llel); /* Look up the language and validate permissions */ languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language)); @@ -1061,6 +1097,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) isLeakProof, isStrict, volatility, + parallel, parameterTypes, PointerGetDatum(allParameterTypes), PointerGetDatum(parameterModes), @@ -1141,6 +1178,7 @@ AlterFunction(AlterFunctionStmt *stmt) List *set_items = NIL; DefElem *cost_item = NULL; DefElem *rows_item = NULL; + DefElem *parallel_item = NULL; ObjectAddress address; rel = heap_open(ProcedureRelationId, RowExclusiveLock); @@ -1178,7 +1216,8 @@ AlterFunction(AlterFunctionStmt *stmt) &leakproof_item, &set_items, &cost_item, - &rows_item) == false) + &rows_item, + ¶llel_item) == false) elog(ERROR, "option \"%s\" not recognized", defel->defname); } @@ -1250,6 +1289,8 @@ AlterFunction(AlterFunctionStmt *stmt) tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); } + if (parallel_item) + procForm->proparallel = interpret_func_parallel(parallel_item); /* Do the update */ simple_heap_update(rel, &tup->t_self, tup); diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 3995a575e4e..939b147c1d6 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -135,6 +135,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) false, /* isLeakProof */ false, /* isStrict */ PROVOLATILE_VOLATILE, + PROPARALLEL_UNSAFE, buildoidvector(funcargtypes, 0), PointerGetDatum(NULL), PointerGetDatum(NULL), @@ -174,6 +175,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) false, /* isLeakProof */ true, /* isStrict */ PROVOLATILE_VOLATILE, + PROPARALLEL_UNSAFE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), @@ -216,6 +218,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) false, /* isLeakProof */ true, /* isStrict */ PROVOLATILE_VOLATILE, + PROPARALLEL_UNSAFE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index de913538910..d2b3f2297b2 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1611,6 +1611,7 @@ makeRangeConstructors(const char *name, Oid namespace, false, /* leakproof */ false, /* isStrict */ PROVOLATILE_IMMUTABLE, /* volatility */ + PROPARALLEL_SAFE, /* parallel safety */ constructorArgTypesVector, /* parameterTypes */ PointerGetDatum(NULL), /* allParameterTypes */ PointerGetDatum(NULL), /* parameterModes */ diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index c28eb2bca36..85ff46b8026 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -243,6 +243,11 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags) if (!(eflags & (EXEC_FLAG_SKIP_TRIGGERS | EXEC_FLAG_EXPLAIN_ONLY))) AfterTriggerBeginQuery(); + /* Enter parallel mode, if required by the query. */ + if (queryDesc->plannedstmt->parallelModeNeeded && + !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) + EnterParallelMode(); + MemoryContextSwitchTo(oldcontext); } @@ -474,6 +479,11 @@ standard_ExecutorEnd(QueryDesc *queryDesc) */ MemoryContextSwitchTo(oldcontext); + /* Exit parallel mode, if it was required by the query. */ + if (queryDesc->plannedstmt->parallelModeNeeded && + !(estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY)) + ExitParallelMode(); + /* * Release EState and per-query memory context. This should release * everything the executor has allocated. diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index ce49c471d49..812a610d029 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -496,7 +496,9 @@ init_execution_state(List *queryTree_list, if (queryTree->commandType == CMD_UTILITY) stmt = queryTree->utilityStmt; else - stmt = (Node *) pg_plan_query(queryTree, 0, NULL); + stmt = (Node *) pg_plan_query(queryTree, + fcache->readonly_func ? CURSOR_OPT_PARALLEL_OK : 0, + NULL); /* Precheck all commands for validity in a function */ if (IsA(stmt, TransactionStmt)) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 1c801f54ea0..62355aae519 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -94,6 +94,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_NODE_FIELD(invalItems); COPY_SCALAR_FIELD(nParamExec); COPY_SCALAR_FIELD(hasRowSecurity); + COPY_SCALAR_FIELD(parallelModeNeeded); return newnode; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 79b71793ef7..e1b49d57faf 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -256,6 +256,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_NODE_FIELD(invalItems); WRITE_INT_FIELD(nParamExec); WRITE_BOOL_FIELD(hasRowSecurity); + WRITE_BOOL_FIELD(parallelModeNeeded); } /* @@ -1787,6 +1788,8 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_UINT_FIELD(lastRowMarkId); WRITE_BOOL_FIELD(transientPlan); WRITE_BOOL_FIELD(hasRowSecurity); + WRITE_BOOL_FIELD(parallelModeOK); + WRITE_BOOL_FIELD(parallelModeNeeded); } static void diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index d598c1bd5c0..06be9229298 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -19,6 +19,7 @@ #include <math.h> #include "access/htup_details.h" +#include "access/parallel.h" #include "executor/executor.h" #include "executor/nodeAgg.h" #include "foreign/fdwapi.h" @@ -43,6 +44,7 @@ #include "parser/parsetree.h" #include "parser/parse_agg.h" #include "rewrite/rewriteManip.h" +#include "storage/dsm_impl.h" #include "utils/rel.h" #include "utils/selfuncs.h" @@ -197,6 +199,48 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) glob->transientPlan = false; glob->hasRowSecurity = false; + /* + * 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 + * parallel-unsafe functions are present in the query tree. + * + * For now, we don't try to use parallel mode if we're running inside + * a parallel worker. We might eventually be able to relax this + * restriction, but for now it seems best not to have parallel workers + * trying to create their own parallel workers. + */ + 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() && + !contain_parallel_unsafe((Node *) parse); + + /* + * glob->parallelModeOK should tell us whether it's necessary to impose + * the parallel mode restrictions, but we don't actually want to impose + * them unless we choose a parallel plan, so that people who mislabel + * their functions but don't use parallelism anyway aren't harmed. + * However, it's useful for testing purposes to be able to force the + * restrictions to be imposed whenever a parallel plan is actually chosen + * or not. + * + * (It's been suggested that we should always impose these restrictions + * whenever glob->parallelModeOK is true, so that it's easier to notice + * incorrectly-labeled functions sooner. That might be the right thing + * to do, but for now I've taken this approach. We could also control + * this with a GUC.) + * + * FIXME: It's assumed that code further down will set parallelModeNeeded + * to true if a parallel path is actually chosen. Since the core + * parallelism code isn't committed yet, this currently never happens. + */ +#ifdef FORCE_PARALLEL_MODE + glob->parallelModeNeeded = glob->parallelModeOK; +#else + glob->parallelModeNeeded = false; +#endif + /* Determine what fraction of the plan is likely to be scanned */ if (cursorOptions & CURSOR_OPT_FAST_PLAN) { @@ -293,6 +337,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) result->invalItems = glob->invalItems; result->nParamExec = glob->nParamExec; result->hasRowSecurity = glob->hasRowSecurity; + result->parallelModeNeeded = glob->parallelModeNeeded; return result; } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index c72dbef1c83..f2c85514225 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -96,6 +96,7 @@ 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 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); @@ -1199,6 +1200,123 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context) } /***************************************************************************** + * Check queries for parallel-unsafe constructs + *****************************************************************************/ + +bool +contain_parallel_unsafe(Node *node) +{ + return contain_parallel_unsafe_walker(node, NULL); +} + +static bool +contain_parallel_unsafe_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, FuncExpr)) + { + FuncExpr *expr = (FuncExpr *) node; + + if (func_parallel(expr->funcid) == PROPARALLEL_UNSAFE) + 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) + 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) + 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) + 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) + return true; + /* else fall through to check args */ + } + else if (IsA(node, CoerceViaIO)) + { + CoerceViaIO *expr = (CoerceViaIO *) node; + Oid iofunc; + Oid typioparam; + bool typisvarlena; + + /* check the result type's input function */ + getTypeInputInfo(expr->resulttype, + &iofunc, &typioparam); + if (func_parallel(iofunc) == PROPARALLEL_UNSAFE) + return true; + /* check the input type's output function */ + getTypeOutputInfo(exprType((Node *) expr->arg), + &iofunc, &typisvarlena); + if (func_parallel(iofunc) == PROPARALLEL_UNSAFE) + 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) + 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) + 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); + } + return expression_tree_walker(node, + contain_parallel_unsafe_walker, + context); +} + +/***************************************************************************** * Check clauses for nonstrict functions *****************************************************************************/ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1efc6d66d70..fb84937b626 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -613,8 +613,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER - PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY POSITION - PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY + PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY + POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM QUOTE @@ -7065,6 +7065,10 @@ common_func_opt_item: /* we abuse the normal content of a DefElem here */ $$ = makeDefElem("set", (Node *)$1); } + | PARALLEL ColId + { + $$ = makeDefElem("parallel", (Node *)makeString($2)); + } ; createfunc_opt_item: @@ -13778,6 +13782,7 @@ unreserved_keyword: | OVER | OWNED | OWNER + | PARALLEL | PARSER | PARTIAL | PARTITION diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index d917af36749..d1f43c5c8a2 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -1030,7 +1030,8 @@ exec_simple_query(const char *query_string) querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0); - plantree_list = pg_plan_queries(querytree_list, 0, NULL); + plantree_list = pg_plan_queries(querytree_list, + CURSOR_OPT_PARALLEL_OK, NULL); /* Done with the snapshot used for parsing/planning */ if (snapshot_set) diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 1dc293297d9..8d1cdf1f1aa 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -1540,6 +1540,25 @@ func_volatile(Oid funcid) } /* + * func_parallel + * Given procedure id, return the function's proparallel flag. + */ +char +func_parallel(Oid funcid) +{ + HeapTuple tp; + char result; + + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + result = ((Form_pg_proc) GETSTRUCT(tp))->proparallel; + ReleaseSysCache(tp); + return result; +} + +/* * get_func_leakproof * Given procedure id, return the function's leakproof field. */ |
