mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
Fix testing of parallel-safety of SubPlans.
is_parallel_safe() supposed that the only relevant property of a SubPlan was the parallel safety of the referenced subplan tree. This is wrong: the testexpr or args subtrees might contain parallel-unsafe stuff, as demonstrated by the test case added here. However, just recursing into the subtrees fails in a different way: we'll typically find PARAM_EXEC Params representing the subplan's output columns in the testexpr. The previous coding supposed that any Param must be treated as parallel-restricted, so that a naive attempt at fixing this disabled parallel pushdown of SubPlans altogether. We must instead determine, for any visited Param, whether it is one that would be computed by a surrounding SubPlan node; if so, it's safe to push down along with the SubPlan node. We might later be able to extend this logic to cope with Params used for correlated subplans and other cases; but that's a task for v11 or beyond. Tom Lane and Amit Kapila Discussion: https://postgr.es/m/7064.1492022469@sss.pgh.pa.us
This commit is contained in:
@ -93,6 +93,7 @@ typedef struct
|
|||||||
{
|
{
|
||||||
char max_hazard; /* worst proparallel hazard found so far */
|
char max_hazard; /* worst proparallel hazard found so far */
|
||||||
char max_interesting; /* worst proparallel hazard of interest */
|
char max_interesting; /* worst proparallel hazard of interest */
|
||||||
|
List *safe_param_ids; /* PARAM_EXEC Param IDs to treat as safe */
|
||||||
} max_parallel_hazard_context;
|
} max_parallel_hazard_context;
|
||||||
|
|
||||||
static bool contain_agg_clause_walker(Node *node, void *context);
|
static bool contain_agg_clause_walker(Node *node, void *context);
|
||||||
@ -1056,6 +1057,7 @@ max_parallel_hazard(Query *parse)
|
|||||||
|
|
||||||
context.max_hazard = PROPARALLEL_SAFE;
|
context.max_hazard = PROPARALLEL_SAFE;
|
||||||
context.max_interesting = PROPARALLEL_UNSAFE;
|
context.max_interesting = PROPARALLEL_UNSAFE;
|
||||||
|
context.safe_param_ids = NIL;
|
||||||
(void) max_parallel_hazard_walker((Node *) parse, &context);
|
(void) max_parallel_hazard_walker((Node *) parse, &context);
|
||||||
return context.max_hazard;
|
return context.max_hazard;
|
||||||
}
|
}
|
||||||
@ -1084,6 +1086,7 @@ is_parallel_safe(PlannerInfo *root, Node *node)
|
|||||||
/* Else use max_parallel_hazard's search logic, but stop on RESTRICTED */
|
/* Else use max_parallel_hazard's search logic, but stop on RESTRICTED */
|
||||||
context.max_hazard = PROPARALLEL_SAFE;
|
context.max_hazard = PROPARALLEL_SAFE;
|
||||||
context.max_interesting = PROPARALLEL_RESTRICTED;
|
context.max_interesting = PROPARALLEL_RESTRICTED;
|
||||||
|
context.safe_param_ids = NIL;
|
||||||
return !max_parallel_hazard_walker(node, &context);
|
return !max_parallel_hazard_walker(node, &context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1171,19 +1174,50 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We can push the subplans only if they are parallel-safe. */
|
/*
|
||||||
|
* Only parallel-safe SubPlans can be sent to workers. Within the
|
||||||
|
* testexpr of the SubPlan, Params representing the output columns of the
|
||||||
|
* subplan can be treated as parallel-safe, so temporarily add their IDs
|
||||||
|
* to the safe_param_ids list while examining the testexpr.
|
||||||
|
*/
|
||||||
else if (IsA(node, SubPlan))
|
else if (IsA(node, SubPlan))
|
||||||
return !((SubPlan *) node)->parallel_safe;
|
{
|
||||||
|
SubPlan *subplan = (SubPlan *) node;
|
||||||
|
List *save_safe_param_ids;
|
||||||
|
|
||||||
|
if (!subplan->parallel_safe &&
|
||||||
|
max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
|
||||||
|
return true;
|
||||||
|
save_safe_param_ids = context->safe_param_ids;
|
||||||
|
context->safe_param_ids = list_concat(list_copy(subplan->paramIds),
|
||||||
|
context->safe_param_ids);
|
||||||
|
if (max_parallel_hazard_walker(subplan->testexpr, context))
|
||||||
|
return true; /* no need to restore safe_param_ids */
|
||||||
|
context->safe_param_ids = save_safe_param_ids;
|
||||||
|
/* we must also check args, but no special Param treatment there */
|
||||||
|
if (max_parallel_hazard_walker((Node *) subplan->args, context))
|
||||||
|
return true;
|
||||||
|
/* don't want to recurse normally, so we're done */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can't pass Params to workers at the moment either, so they are also
|
* We can't pass Params to workers at the moment either, so they are also
|
||||||
* parallel-restricted.
|
* parallel-restricted, unless they are PARAM_EXEC Params listed in
|
||||||
|
* safe_param_ids, meaning they could be generated within the worker.
|
||||||
*/
|
*/
|
||||||
else if (IsA(node, Param))
|
else if (IsA(node, Param))
|
||||||
|
{
|
||||||
|
Param *param = (Param *) node;
|
||||||
|
|
||||||
|
if (param->paramkind != PARAM_EXEC ||
|
||||||
|
!list_member_int(context->safe_param_ids, param->paramid))
|
||||||
{
|
{
|
||||||
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
|
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false; /* nothing to recurse to */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When we're first invoked on a completely unplanned tree, we must
|
* When we're first invoked on a completely unplanned tree, we must
|
||||||
|
@ -699,7 +699,8 @@ typedef struct SubPlan
|
|||||||
bool unknownEqFalse; /* TRUE if it's okay to return FALSE when the
|
bool unknownEqFalse; /* TRUE if it's okay to return FALSE when the
|
||||||
* spec result is UNKNOWN; this allows much
|
* spec result is UNKNOWN; this allows much
|
||||||
* simpler handling of null values */
|
* simpler handling of null values */
|
||||||
bool parallel_safe; /* OK to use as part of parallel plan? */
|
bool parallel_safe; /* is the subplan parallel-safe? */
|
||||||
|
/* Note: parallel_safe does not consider contents of testexpr or args */
|
||||||
/* Information for passing params into and out of the subselect: */
|
/* Information for passing params into and out of the subselect: */
|
||||||
/* setParam and parParam are lists of integers (param IDs) */
|
/* setParam and parParam are lists of integers (param IDs) */
|
||||||
List *setParam; /* initplan subqueries have to set these
|
List *setParam; /* initplan subqueries have to set these
|
||||||
|
@ -126,6 +126,18 @@ select count(*) from tenk1 where (two, four) not in
|
|||||||
10000
|
10000
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- this is not parallel-safe due to use of random() within SubLink's testexpr:
|
||||||
|
explain (costs off)
|
||||||
|
select * from tenk1 where (unique1 + random())::integer not in
|
||||||
|
(select ten from tenk2);
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------
|
||||||
|
Seq Scan on tenk1
|
||||||
|
Filter: (NOT (hashed SubPlan 1))
|
||||||
|
SubPlan 1
|
||||||
|
-> Seq Scan on tenk2
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
alter table tenk2 reset (parallel_workers);
|
alter table tenk2 reset (parallel_workers);
|
||||||
-- test parallel index scans.
|
-- test parallel index scans.
|
||||||
set enable_seqscan to off;
|
set enable_seqscan to off;
|
||||||
|
@ -46,6 +46,10 @@ explain (costs off)
|
|||||||
(select hundred, thousand from tenk2 where thousand > 100);
|
(select hundred, thousand from tenk2 where thousand > 100);
|
||||||
select count(*) from tenk1 where (two, four) not in
|
select count(*) from tenk1 where (two, four) not in
|
||||||
(select hundred, thousand from tenk2 where thousand > 100);
|
(select hundred, thousand from tenk2 where thousand > 100);
|
||||||
|
-- this is not parallel-safe due to use of random() within SubLink's testexpr:
|
||||||
|
explain (costs off)
|
||||||
|
select * from tenk1 where (unique1 + random())::integer not in
|
||||||
|
(select ten from tenk2);
|
||||||
alter table tenk2 reset (parallel_workers);
|
alter table tenk2 reset (parallel_workers);
|
||||||
|
|
||||||
-- test parallel index scans.
|
-- test parallel index scans.
|
||||||
|
Reference in New Issue
Block a user