@@ -6553,7 +6553,7 @@ exec_save_simple_expr(PLpgSQL_expr *expr, CachedPlan *cplan)
65536553{
65546554 PlannedStmt * stmt ;
65556555 Plan * plan ;
6556- TargetEntry * tle ;
6556+ Expr * tle_expr ;
65576557
65586558 /*
65596559 * 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)
65636563 /* Extract the single PlannedStmt */
65646564 Assert (list_length (cplan -> stmt_list ) == 1 );
65656565 stmt = linitial_node (PlannedStmt , cplan -> stmt_list );
6566-
6567- /* Should be a trivial Result plan */
65686566 Assert (stmt -> commandType == CMD_SELECT );
6567+
6568+ /*
6569+ * Ordinarily, the plan node should be a simple Result. However, if
6570+ * force_parallel_mode is on, the planner might've stuck a Gather node
6571+ * atop that. The simplest way to deal with this is to look through the
6572+ * Gather node. The Gather node's tlist would normally contain a Var
6573+ * referencing the child node's output ... but setrefs.c might also have
6574+ * copied a Const as-is.
6575+ */
65696576 plan = stmt -> planTree ;
6570- Assert (IsA (plan , Result ));
6571- Assert (plan -> lefttree == NULL &&
6572- plan -> righttree == NULL &&
6573- plan -> initPlan == NULL &&
6574- plan -> qual == NULL &&
6575- ((Result * ) plan )-> resconstantqual == NULL );
6577+ for (;;)
6578+ {
6579+ /* Extract the single tlist expression */
6580+ Assert (list_length (plan -> targetlist ) == 1 );
6581+ tle_expr = castNode (TargetEntry , linitial (plan -> targetlist ))-> expr ;
65766582
6577- /* Extract the single tlist expression */
6578- Assert (list_length (plan -> targetlist ) == 1 );
6579- tle = (TargetEntry * ) linitial (plan -> targetlist );
6583+ if (IsA (plan , Result ))
6584+ {
6585+ Assert (plan -> lefttree == NULL &&
6586+ plan -> righttree == NULL &&
6587+ plan -> initPlan == NULL &&
6588+ plan -> qual == NULL &&
6589+ ((Result * ) plan )-> resconstantqual == NULL );
6590+ break ;
6591+ }
6592+ else if (IsA (plan , Gather ))
6593+ {
6594+ Assert (plan -> lefttree != NULL &&
6595+ plan -> righttree == NULL &&
6596+ plan -> initPlan == NULL &&
6597+ plan -> qual == NULL );
6598+ /* If setrefs.c copied up a Const, no need to look further */
6599+ if (IsA (tle_expr , Const ))
6600+ break ;
6601+ /* Otherwise, it better be an outer Var */
6602+ Assert (IsA (tle_expr , Var ));
6603+ Assert (((Var * ) tle_expr )-> varno == OUTER_VAR );
6604+ /* Descend to the child node */
6605+ plan = plan -> lefttree ;
6606+ }
6607+ else
6608+ elog (ERROR , "unexpected plan node type: %d" ,
6609+ (int ) nodeTag (plan ));
6610+ }
65806611
65816612 /*
65826613 * Save the simple expression, and initialize state to "not valid in
65836614 * current transaction".
65846615 */
6585- expr -> expr_simple_expr = tle -> expr ;
6616+ expr -> expr_simple_expr = tle_expr ;
65866617 expr -> expr_simple_generation = cplan -> generation ;
65876618 expr -> expr_simple_state = NULL ;
65886619 expr -> expr_simple_in_use = false;
65896620 expr -> expr_simple_lxid = InvalidLocalTransactionId ;
65906621 /* Also stash away the expression result type */
6591- expr -> expr_simple_type = exprType ((Node * ) tle -> expr );
6592- expr -> expr_simple_typmod = exprTypmod ((Node * ) tle -> expr );
6622+ expr -> expr_simple_type = exprType ((Node * ) tle_expr );
6623+ expr -> expr_simple_typmod = exprTypmod ((Node * ) tle_expr );
65936624}
65946625
65956626/*
0 commit comments