Skip to content

Commit 3915178

Browse files
committed
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
1 parent 539f670 commit 3915178

File tree

4 files changed

+57
-6
lines changed

4 files changed

+57
-6
lines changed

src/backend/optimizer/util/clauses.c

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ typedef struct
9393
{
9494
char max_hazard; /* worst proparallel hazard found so far */
9595
char max_interesting; /* worst proparallel hazard of interest */
96+
List *safe_param_ids; /* PARAM_EXEC Param IDs to treat as safe */
9697
} max_parallel_hazard_context;
9798

9899
static bool contain_agg_clause_walker(Node *node, void *context);
@@ -1056,6 +1057,7 @@ max_parallel_hazard(Query *parse)
10561057

10571058
context.max_hazard = PROPARALLEL_SAFE;
10581059
context.max_interesting = PROPARALLEL_UNSAFE;
1060+
context.safe_param_ids = NIL;
10591061
(void) max_parallel_hazard_walker((Node *) parse, &context);
10601062
return context.max_hazard;
10611063
}
@@ -1084,6 +1086,7 @@ is_parallel_safe(PlannerInfo *root, Node *node)
10841086
/* Else use max_parallel_hazard's search logic, but stop on RESTRICTED */
10851087
context.max_hazard = PROPARALLEL_SAFE;
10861088
context.max_interesting = PROPARALLEL_RESTRICTED;
1089+
context.safe_param_ids = NIL;
10871090
return !max_parallel_hazard_walker(node, &context);
10881091
}
10891092

@@ -1171,18 +1174,49 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
11711174
return true;
11721175
}
11731176

1174-
/* We can push the subplans only if they are parallel-safe. */
1177+
/*
1178+
* Only parallel-safe SubPlans can be sent to workers. Within the
1179+
* testexpr of the SubPlan, Params representing the output columns of the
1180+
* subplan can be treated as parallel-safe, so temporarily add their IDs
1181+
* to the safe_param_ids list while examining the testexpr.
1182+
*/
11751183
else if (IsA(node, SubPlan))
1176-
return !((SubPlan *) node)->parallel_safe;
1184+
{
1185+
SubPlan *subplan = (SubPlan *) node;
1186+
List *save_safe_param_ids;
1187+
1188+
if (!subplan->parallel_safe &&
1189+
max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
1190+
return true;
1191+
save_safe_param_ids = context->safe_param_ids;
1192+
context->safe_param_ids = list_concat(list_copy(subplan->paramIds),
1193+
context->safe_param_ids);
1194+
if (max_parallel_hazard_walker(subplan->testexpr, context))
1195+
return true; /* no need to restore safe_param_ids */
1196+
context->safe_param_ids = save_safe_param_ids;
1197+
/* we must also check args, but no special Param treatment there */
1198+
if (max_parallel_hazard_walker((Node *) subplan->args, context))
1199+
return true;
1200+
/* don't want to recurse normally, so we're done */
1201+
return false;
1202+
}
11771203

11781204
/*
11791205
* We can't pass Params to workers at the moment either, so they are also
1180-
* parallel-restricted.
1206+
* parallel-restricted, unless they are PARAM_EXEC Params listed in
1207+
* safe_param_ids, meaning they could be generated within the worker.
11811208
*/
11821209
else if (IsA(node, Param))
11831210
{
1184-
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
1185-
return true;
1211+
Param *param = (Param *) node;
1212+
1213+
if (param->paramkind != PARAM_EXEC ||
1214+
!list_member_int(context->safe_param_ids, param->paramid))
1215+
{
1216+
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
1217+
return true;
1218+
}
1219+
return false; /* nothing to recurse to */
11861220
}
11871221

11881222
/*

src/include/nodes/primnodes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,8 @@ typedef struct SubPlan
699699
bool unknownEqFalse; /* TRUE if it's okay to return FALSE when the
700700
* spec result is UNKNOWN; this allows much
701701
* simpler handling of null values */
702-
bool parallel_safe; /* OK to use as part of parallel plan? */
702+
bool parallel_safe; /* is the subplan parallel-safe? */
703+
/* Note: parallel_safe does not consider contents of testexpr or args */
703704
/* Information for passing params into and out of the subselect: */
704705
/* setParam and parParam are lists of integers (param IDs) */
705706
List *setParam; /* initplan subqueries have to set these

src/test/regress/expected/select_parallel.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,18 @@ select count(*) from tenk1 where (two, four) not in
126126
10000
127127
(1 row)
128128

129+
-- this is not parallel-safe due to use of random() within SubLink's testexpr:
130+
explain (costs off)
131+
select * from tenk1 where (unique1 + random())::integer not in
132+
(select ten from tenk2);
133+
QUERY PLAN
134+
------------------------------------
135+
Seq Scan on tenk1
136+
Filter: (NOT (hashed SubPlan 1))
137+
SubPlan 1
138+
-> Seq Scan on tenk2
139+
(4 rows)
140+
129141
alter table tenk2 reset (parallel_workers);
130142
-- test parallel index scans.
131143
set enable_seqscan to off;

src/test/regress/sql/select_parallel.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ explain (costs off)
4646
(select hundred, thousand from tenk2 where thousand > 100);
4747
select count(*) from tenk1 where (two, four) not in
4848
(select hundred, thousand from tenk2 where thousand > 100);
49+
-- this is not parallel-safe due to use of random() within SubLink's testexpr:
50+
explain (costs off)
51+
select * from tenk1 where (unique1 + random())::integer not in
52+
(select ten from tenk2);
4953
alter table tenk2 reset (parallel_workers);
5054

5155
-- test parallel index scans.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy