Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a87c0c7

Browse files
committed
Allow parallel query for prepared statements with generic plans.
This was always intended to work, but due to an oversight in max_parallel_hazard_walker, it didn't. In testing, we missed the fact that it was only working for custom plans, where the parameter value has been substituted for the parameter itself early enough that everything worked. In a generic plan, the Param node survives and must be treated as parallel-safe. SerializeParamList provides for the transmission of parameter values to workers. Amit Kapila with help from Kuntal Ghosh. Some changes by me. Discussion: http://postgr.es/m/CAA4eK1+_BuZrmVCeua5Eqnm4Co9DAXdM5HPAOE2J19ePbR912Q@mail.gmail.com
1 parent 69125c8 commit a87c0c7

File tree

4 files changed

+76
-21
lines changed

4 files changed

+76
-21
lines changed

src/backend/optimizer/util/clauses.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,13 +1223,17 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
12231223

12241224
/*
12251225
* We can't pass Params to workers at the moment either, so they are also
1226-
* parallel-restricted, unless they are PARAM_EXEC Params listed in
1227-
* safe_param_ids, meaning they could be generated within the worker.
1226+
* parallel-restricted, unless they are PARAM_EXTERN Params or are
1227+
* PARAM_EXEC Params listed in safe_param_ids, meaning they could be
1228+
* generated within the worker.
12281229
*/
12291230
else if (IsA(node, Param))
12301231
{
12311232
Param *param = (Param *) node;
12321233

1234+
if (param->paramkind == PARAM_EXTERN)
1235+
return false;
1236+
12331237
if (param->paramkind != PARAM_EXEC ||
12341238
!list_member_int(context->safe_param_ids, param->paramid))
12351239
{

src/pl/plpgsql/src/pl_exec.c

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6821,7 +6821,7 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
68216821
{
68226822
PlannedStmt *stmt;
68236823
Plan *plan;
6824-
TargetEntry *tle;
6824+
Expr *tle_expr;
68256825

68266826
/*
68276827
* Initialize to "not simple", and remember the plan generation number we
@@ -6836,18 +6836,51 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
68366836
if (list_length(cplan->stmt_list) != 1)
68376837
return;
68386838
stmt = linitial_node(PlannedStmt, cplan->stmt_list);
6839+
if (stmt->commandType != CMD_SELECT)
6840+
return;
68396841

68406842
/*
6841-
* 2. It must be a RESULT plan --> no scan's required
6843+
* 2. Ordinarily, the plan node should be a simple Result. However, if
6844+
* force_parallel_mode is on, the planner might've stuck a Gather node
6845+
* atop that. The simplest way to deal with this is to look through the
6846+
* Gather node. The Gather node's tlist would normally contain a Var
6847+
* referencing the child node's output, but it could also be a Param, or
6848+
* it could be a Const that setrefs.c copied as-is.
68426849
*/
6843-
if (stmt->commandType != CMD_SELECT)
6844-
return;
68456850
plan = stmt->planTree;
6846-
if (!IsA(plan, Result))
6847-
return;
6851+
for (;;)
6852+
{
6853+
/*
6854+
* 3. The plan must have a single attribute as result
6855+
*/
6856+
if (list_length(plan->targetlist) != 1)
6857+
return;
6858+
tle_expr = castNode(TargetEntry, linitial(plan->targetlist))->expr;
6859+
6860+
if (IsA(plan, Gather))
6861+
{
6862+
if (plan->righttree != NULL ||
6863+
plan->initPlan != NULL ||
6864+
plan->qual != NULL)
6865+
return;
6866+
/* If setrefs.c copied up a Const, no need to look further */
6867+
if (IsA(tle_expr, Const))
6868+
break;
6869+
/* Otherwise, it had better be a Param or an outer Var */
6870+
if (!IsA(tle_expr, Param) && !(IsA(tle_expr, Var) &&
6871+
((Var *) tle_expr)->varno == OUTER_VAR))
6872+
return;
6873+
/* Descend to the child node */
6874+
plan = plan->lefttree;
6875+
continue;
6876+
}
6877+
else if (!IsA(plan, Result))
6878+
return;
6879+
break;
6880+
}
68486881

68496882
/*
6850-
* 3. Can't have any subplan or qual clause, either
6883+
* 4. Can't have any subplan or qual clause, either
68516884
*/
68526885
if (plan->lefttree != NULL ||
68536886
plan->righttree != NULL ||
@@ -6856,31 +6889,23 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
68566889
((Result *) plan)->resconstantqual != NULL)
68576890
return;
68586891

6859-
/*
6860-
* 4. The plan must have a single attribute as result
6861-
*/
6862-
if (list_length(plan->targetlist) != 1)
6863-
return;
6864-
6865-
tle = (TargetEntry *) linitial(plan->targetlist);
6866-
68676892
/*
68686893
* 5. Check that all the nodes in the expression are non-scary.
68696894
*/
6870-
if (!exec_simple_check_node((Node *) tle->expr))
6895+
if (!exec_simple_check_node((Node *) tle_expr))
68716896
return;
68726897

68736898
/*
68746899
* Yes - this is a simple expression. Mark it as such, and initialize
68756900
* state to "not valid in current transaction".
68766901
*/
6877-
expr->expr_simple_expr = tle->expr;
6902+
expr->expr_simple_expr = tle_expr;
68786903
expr->expr_simple_state = NULL;
68796904
expr->expr_simple_in_use = false;
68806905
expr->expr_simple_lxid = InvalidLocalTransactionId;
68816906
/* Also stash away the expression result type */
6882-
expr->expr_simple_type = exprType((Node *) tle->expr);
6883-
expr->expr_simple_typmod = exprTypmod((Node *) tle->expr);
6907+
expr->expr_simple_type = exprType((Node *) tle_expr);
6908+
expr->expr_simple_typmod = exprTypmod((Node *) tle_expr);
68846909
}
68856910

68866911
/*

src/test/regress/expected/select_parallel.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,26 @@ explain (costs off)
101101
-> Parallel Index Only Scan using tenk1_unique1 on tenk1
102102
(5 rows)
103103

104+
-- test prepared statement
105+
prepare tenk1_count(integer) As select count((unique1)) from tenk1 where hundred > $1;
106+
explain (costs off) execute tenk1_count(1);
107+
QUERY PLAN
108+
----------------------------------------------
109+
Finalize Aggregate
110+
-> Gather
111+
Workers Planned: 4
112+
-> Partial Aggregate
113+
-> Parallel Seq Scan on tenk1
114+
Filter: (hundred > 1)
115+
(6 rows)
116+
117+
execute tenk1_count(1);
118+
count
119+
-------
120+
9800
121+
(1 row)
122+
123+
deallocate tenk1_count;
104124
-- test parallel plans for queries containing un-correlated subplans.
105125
alter table tenk2 set (parallel_workers = 0);
106126
explain (costs off)

src/test/regress/sql/select_parallel.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ explain (costs off)
3939
select sum(parallel_restricted(unique1)) from tenk1
4040
group by(parallel_restricted(unique1));
4141

42+
-- test prepared statement
43+
prepare tenk1_count(integer) As select count((unique1)) from tenk1 where hundred > $1;
44+
explain (costs off) execute tenk1_count(1);
45+
execute tenk1_count(1);
46+
deallocate tenk1_count;
47+
4248
-- test parallel plans for queries containing un-correlated subplans.
4349
alter table tenk2 set (parallel_workers = 0);
4450
explain (costs off)

0 commit comments

Comments
 (0)