aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2017-08-15 16:49:47 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2017-08-15 16:49:52 -0400
commitb73f1b5c29e0ace5a281bc13ce09dea30e1b66de (patch)
treee02e37ead2ff82bba9dec8d755b109feadf17f33
parent70b573b2677eb5ef28515e824e5027e26ce21d77 (diff)
downloadpostgresql-b73f1b5c29e0ace5a281bc13ce09dea30e1b66de.tar.gz
postgresql-b73f1b5c29e0ace5a281bc13ce09dea30e1b66de.zip
Make simpler-simple-expressions code cope with a Gather plan.
Commit 00418c612 expected that the plan generated for a simple-expression query would always be a plain Result node. However, if force_parallel_mode is on, the planner might stick a Gather atop that. Cope by looking through the Gather. For safety, assert that the Gather's tlist is trivial. Per buildfarm. Discussion: https://postgr.es/m/23425.1502822098@sss.pgh.pa.us
-rw-r--r--src/pl/plpgsql/src/pl_exec.c61
1 files changed, 46 insertions, 15 deletions
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 4eb2dd284b7..616f5e30f81 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6553,7 +6553,7 @@ exec_save_simple_expr(PLpgSQL_expr *expr, CachedPlan *cplan)
{
PlannedStmt *stmt;
Plan *plan;
- TargetEntry *tle;
+ Expr *tle_expr;
/*
* Given the checks that exec_simple_check_plan did, none of the Asserts
@@ -6563,33 +6563,64 @@ exec_save_simple_expr(PLpgSQL_expr *expr, CachedPlan *cplan)
/* Extract the single PlannedStmt */
Assert(list_length(cplan->stmt_list) == 1);
stmt = linitial_node(PlannedStmt, cplan->stmt_list);
-
- /* Should be a trivial Result plan */
Assert(stmt->commandType == CMD_SELECT);
+
+ /*
+ * Ordinarily, the plan node should be a simple Result. However, if
+ * force_parallel_mode is on, the planner might've stuck a Gather node
+ * atop that. The simplest way to deal with this is to look through the
+ * Gather node. The Gather node's tlist would normally contain a Var
+ * referencing the child node's output ... but setrefs.c might also have
+ * copied a Const as-is.
+ */
plan = stmt->planTree;
- Assert(IsA(plan, Result));
- Assert(plan->lefttree == NULL &&
- plan->righttree == NULL &&
- plan->initPlan == NULL &&
- plan->qual == NULL &&
- ((Result *) plan)->resconstantqual == NULL);
+ for (;;)
+ {
+ /* Extract the single tlist expression */
+ Assert(list_length(plan->targetlist) == 1);
+ tle_expr = castNode(TargetEntry, linitial(plan->targetlist))->expr;
- /* Extract the single tlist expression */
- Assert(list_length(plan->targetlist) == 1);
- tle = (TargetEntry *) linitial(plan->targetlist);
+ if (IsA(plan, Result))
+ {
+ Assert(plan->lefttree == NULL &&
+ plan->righttree == NULL &&
+ plan->initPlan == NULL &&
+ plan->qual == NULL &&
+ ((Result *) plan)->resconstantqual == NULL);
+ break;
+ }
+ else if (IsA(plan, Gather))
+ {
+ Assert(plan->lefttree != NULL &&
+ plan->righttree == NULL &&
+ plan->initPlan == NULL &&
+ plan->qual == NULL);
+ /* If setrefs.c copied up a Const, no need to look further */
+ if (IsA(tle_expr, Const))
+ break;
+ /* Otherwise, it better be an outer Var */
+ Assert(IsA(tle_expr, Var));
+ Assert(((Var *) tle_expr)->varno == OUTER_VAR);
+ /* Descend to the child node */
+ plan = plan->lefttree;
+ }
+ else
+ elog(ERROR, "unexpected plan node type: %d",
+ (int) nodeTag(plan));
+ }
/*
* Save the simple expression, and initialize state to "not valid in
* current transaction".
*/
- expr->expr_simple_expr = tle->expr;
+ expr->expr_simple_expr = tle_expr;
expr->expr_simple_generation = cplan->generation;
expr->expr_simple_state = NULL;
expr->expr_simple_in_use = false;
expr->expr_simple_lxid = InvalidLocalTransactionId;
/* Also stash away the expression result type */
- expr->expr_simple_type = exprType((Node *) tle->expr);
- expr->expr_simple_typmod = exprTypmod((Node *) tle->expr);
+ expr->expr_simple_type = exprType((Node *) tle_expr);
+ expr->expr_simple_typmod = exprTypmod((Node *) tle_expr);
}
/*