diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 90dfac2..64bd254 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 470,476 ****
  %type <node>	def_arg columnElem where_clause where_or_current_clause
  				a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
  				columnref in_expr having_clause func_table xmltable array_expr
! 				ExclusionWhereClause operator_def_arg
  %type <list>	rowsfrom_item rowsfrom_list opt_col_def_list
  %type <boolean> opt_ordinality
  %type <list>	ExclusionConstraintList ExclusionConstraintElem
--- 470,476 ----
  %type <node>	def_arg columnElem where_clause where_or_current_clause
  				a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
  				columnref in_expr having_clause func_table xmltable array_expr
! 				ct_row_expr ExclusionWhereClause operator_def_arg
  %type <list>	rowsfrom_item rowsfrom_list opt_col_def_list
  %type <boolean> opt_ordinality
  %type <list>	ExclusionConstraintList ExclusionConstraintElem
*************** set_clause:
*** 11070,11076 ****
  					$1->val = (Node *) $3;
  					$$ = list_make1($1);
  				}
! 			| '(' set_target_list ')' '=' a_expr
  				{
  					int ncolumns = list_length($2);
  					int i = 1;
--- 11070,11076 ----
  					$1->val = (Node *) $3;
  					$$ = list_make1($1);
  				}
! 			| '(' set_target_list ')' '=' ct_row_expr
  				{
  					int ncolumns = list_length($2);
  					int i = 1;
*************** c_expr:		columnref								{ $$ = $1; }
*** 13451,13457 ****
  						n->indirection = check_indirection($4, yyscanner);
  						$$ = (Node *)n;
  					}
! 					else if (operator_precedence_warning)
  					{
  						/*
  						 * If precedence warnings are enabled, insert
--- 13451,13458 ----
  						n->indirection = check_indirection($4, yyscanner);
  						$$ = (Node *)n;
  					}
! 					else if (operator_precedence_warning ||
! 							 pg_yyget_extra(yyscanner)->in_ctre)
  					{
  						/*
  						 * If precedence warnings are enabled, insert
*************** c_expr:		columnref								{ $$ = $1; }
*** 13469,13474 ****
--- 13470,13479 ----
  						 * suppress warnings, it's probably safe enough; and
  						 * we'd just as soon not waste cycles on dummy parse
  						 * nodes if we don't have to.
+ 						 *
+ 						 * We also insert AEXPR_PAREN nodes if we're inside a
+ 						 * ct_row_expr, so that parse analysis can distinguish
+ 						 * "(x)" from "x" later.
  						 */
  						$$ = (Node *) makeA_Expr(AEXPR_PAREN, NIL, $2, NULL,
  												 exprLocation($2));
*************** opt_asymmetric: ASYMMETRIC
*** 14599,14604 ****
--- 14604,14623 ----
  			| /*EMPTY*/
  		;
  
+ /*
+  * Deal with the spec's poorly-thought-through "contextually typed row value
+  * expression" syntax.  For that, we need to detect whether the expression has
+  * outer parentheses.  We can't define it as "a_expr | (a_expr)" without
+  * causing reduce/reduce conflicts, so instead force AEXPR_PAREN nodes to be
+  * inserted for parens throughout the subexpression, and we'll sort it out
+  * during parse analysis.
+  */
+ ct_row_expr:
+ 			{ pg_yyget_extra(yyscanner)->in_ctre++; }
+ 			a_expr
+ 			{ pg_yyget_extra(yyscanner)->in_ctre--; $$ = $2; }
+ 		;
+ 
  
  /*****************************************************************************
   *
*************** void
*** 16328,16331 ****
--- 16347,16351 ----
  parser_init(base_yy_extra_type *yyext)
  {
  	yyext->parsetree = NIL;		/* in case grammar forgets to set it */
+ 	yyext->in_ctre = 0;			/* not handling ct_row_expr */
  }
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a..0d09d7b 100644
*** a/src/backend/parser/parse_expr.c
--- b/src/backend/parser/parse_expr.c
*************** transformMultiAssignRef(ParseState *psta
*** 1499,1515 ****
  	/* We only need to transform the source if this is the first column */
  	if (maref->colno == 1)
  	{
  		/*
  		 * For now, we only allow EXPR SubLinks and RowExprs as the source of
  		 * an UPDATE multiassignment.  This is sufficient to cover interesting
  		 * cases; at worst, someone would have to write (SELECT * FROM expr)
  		 * to expand a composite-returning expression of another form.
  		 */
! 		if (IsA(maref->source, SubLink) &&
! 			((SubLink *) maref->source)->subLinkType == EXPR_SUBLINK)
  		{
  			/* Relabel it as a MULTIEXPR_SUBLINK */
! 			sublink = (SubLink *) maref->source;
  			sublink->subLinkType = MULTIEXPR_SUBLINK;
  			/* And transform it */
  			sublink = (SubLink *) transformExprRecurse(pstate,
--- 1499,1548 ----
  	/* We only need to transform the source if this is the first column */
  	if (maref->colno == 1)
  	{
+ 		Node	   *source = maref->source;
+ 		bool		have_parens = false;
+ 
+ 		/* Drill through any AEXPR_PAREN nodes */
+ 		while (source && IsA(source, A_Expr) &&
+ 			   ((A_Expr *) source)->kind == AEXPR_PAREN)
+ 		{
+ 			have_parens = true;
+ 			source = ((A_Expr *) source)->lexpr;
+ 		}
+ 
+ 		/*
+ 		 * If we have a single-column assignment, and the RHS had parens but
+ 		 * isn't a SubLink or RowExpr, pretend it's a single-column RowExpr.
+ 		 * This is quite broken, as it will cause odd inconsistencies whenever
+ 		 * we get around to allowing other syntactic cases for the RHS, but
+ 		 * some people insisted.
+ 		 */
+ 		if (have_parens && maref->ncolumns == 1 &&
+ 			!(IsA(source, SubLink) &&
+ 			  ((SubLink *) source)->subLinkType == EXPR_SUBLINK) &&
+ 			!IsA(source, RowExpr))
+ 		{
+ 			RowExpr    *r = makeNode(RowExpr);
+ 
+ 			r->args = list_make1(source);
+ 			r->row_typeid = InvalidOid;
+ 			r->colnames = NIL;
+ 			r->row_format = COERCE_IMPLICIT_CAST;	/* abuse */
+ 			r->location = -1;
+ 			source = (Node *) r;
+ 		}
+ 
  		/*
  		 * For now, we only allow EXPR SubLinks and RowExprs as the source of
  		 * an UPDATE multiassignment.  This is sufficient to cover interesting
  		 * cases; at worst, someone would have to write (SELECT * FROM expr)
  		 * to expand a composite-returning expression of another form.
  		 */
! 		if (IsA(source, SubLink) &&
! 			((SubLink *) source)->subLinkType == EXPR_SUBLINK)
  		{
  			/* Relabel it as a MULTIEXPR_SUBLINK */
! 			sublink = (SubLink *) source;
  			sublink->subLinkType = MULTIEXPR_SUBLINK;
  			/* And transform it */
  			sublink = (SubLink *) transformExprRecurse(pstate,
*************** transformMultiAssignRef(ParseState *psta
*** 1542,1552 ****
  			 */
  			sublink->subLinkId = list_length(pstate->p_multiassign_exprs);
  		}
! 		else if (IsA(maref->source, RowExpr))
  		{
  			/* Transform the RowExpr, allowing SetToDefault items */
  			rexpr = (RowExpr *) transformRowExpr(pstate,
! 												 (RowExpr *) maref->source,
  												 true);
  
  			/* Check it returns required number of columns */
--- 1575,1585 ----
  			 */
  			sublink->subLinkId = list_length(pstate->p_multiassign_exprs);
  		}
! 		else if (IsA(source, RowExpr))
  		{
  			/* Transform the RowExpr, allowing SetToDefault items */
  			rexpr = (RowExpr *) transformRowExpr(pstate,
! 												 (RowExpr *) source,
  												 true);
  
  			/* Check it returns required number of columns */
*************** transformMultiAssignRef(ParseState *psta
*** 1568,1574 ****
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  					 errmsg("source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression"),
! 					 parser_errposition(pstate, exprLocation(maref->source))));
  	}
  	else
  	{
--- 1601,1607 ----
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  					 errmsg("source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression"),
! 					 parser_errposition(pstate, exprLocation(source))));
  	}
  	else
  	{
diff --git a/src/include/parser/gramparse.h b/src/include/parser/gramparse.h
index 42e7ede..6e51081 100644
*** a/src/include/parser/gramparse.h
--- b/src/include/parser/gramparse.h
*************** typedef struct base_yy_extra_type
*** 53,58 ****
--- 53,59 ----
  	 * State variables that belong to the grammar.
  	 */
  	List	   *parsetree;		/* final parse result is delivered here */
+ 	int			in_ctre;		/* handling contextually-typed row expr? */
  } base_yy_extra_type;
  
  /*
