From: Tom Lane Date: Tue, 15 Aug 2017 20:49:47 +0000 (-0400) Subject: Make simpler-simple-expressions code cope with a Gather plan. X-Git-Tag: REL_11_BETA1~1791 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b73f1b5c29e0ace5a281bc13ce09dea30e1b66de;p=postgresql 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 --- diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 4eb2dd284b..616f5e30f8 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); } /*