diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 117c9c0e451568c6cc1302cb45129867cd684101..344ebb79891e685bc589ee9bcd91ae0d0e153491 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -109,16 +109,13 @@ static List *simplify_and_arguments(List *args,
 static Node *simplify_boolean_equality(Oid opno, List *args);
 static Expr *simplify_function(Oid funcid,
 				  Oid result_type, int32 result_typmod,
-				  Oid result_collid, Oid input_collid, List **args,
-				  bool has_named_args,
-				  bool allow_non_const,
+				  Oid result_collid, Oid input_collid, List **args_p,
+				  bool process_args, bool allow_non_const,
 				  eval_const_expressions_context *context);
-static List *reorder_function_arguments(List *args, Oid result_type,
-						   HeapTuple func_tuple,
-						   eval_const_expressions_context *context);
-static List *add_function_defaults(List *args, Oid result_type,
-					  HeapTuple func_tuple,
-					  eval_const_expressions_context *context);
+static List *expand_function_arguments(List *args, Oid result_type,
+									   HeapTuple func_tuple);
+static List *reorder_function_arguments(List *args, HeapTuple func_tuple);
+static List *add_function_defaults(List *args, HeapTuple func_tuple);
 static List *fetch_function_defaults(HeapTuple func_tuple);
 static void recheck_cast_function_args(List *args, Oid result_type,
 						   HeapTuple func_tuple);
@@ -2303,27 +2300,9 @@ eval_const_expressions_mutator(Node *node,
 		case T_FuncExpr:
 			{
 				FuncExpr   *expr = (FuncExpr *) node;
-				List	   *args;
-				bool		has_named_args;
+				List	   *args = expr->args;
 				Expr	   *simple;
 				FuncExpr   *newexpr;
-				ListCell   *lc;
-
-				/*
-				 * Reduce constants in the FuncExpr's arguments, and check to
-				 * see if there are any named args.
-				 */
-				args = NIL;
-				has_named_args = false;
-				foreach(lc, expr->args)
-				{
-					Node	   *arg = (Node *) lfirst(lc);
-
-					arg = eval_const_expressions_mutator(arg, context);
-					if (IsA(arg, NamedArgExpr))
-						has_named_args = true;
-					args = lappend(args, arg);
-				}
 
 				/*
 				 * Code for op/func reduction is pretty bulky, so split it out
@@ -2338,7 +2317,7 @@ eval_const_expressions_mutator(Node *node,
 										   expr->funccollid,
 										   expr->inputcollid,
 										   &args,
-										   has_named_args,
+										   true,
 										   true,
 										   context);
 				if (simple)		/* successfully simplified it */
@@ -2364,20 +2343,10 @@ eval_const_expressions_mutator(Node *node,
 		case T_OpExpr:
 			{
 				OpExpr	   *expr = (OpExpr *) node;
-				List	   *args;
+				List	   *args = expr->args;
 				Expr	   *simple;
 				OpExpr	   *newexpr;
 
-				/*
-				 * Reduce constants in the OpExpr's arguments.  We know args
-				 * is either NIL or a List node, so we can call
-				 * expression_tree_mutator directly rather than recursing to
-				 * self.
-				 */
-				args = (List *) expression_tree_mutator((Node *) expr->args,
-											  eval_const_expressions_mutator,
-														(void *) context);
-
 				/*
 				 * Need to get OID of underlying function.	Okay to scribble
 				 * on input to this extent.
@@ -2393,7 +2362,9 @@ eval_const_expressions_mutator(Node *node,
 										   expr->opcollid,
 										   expr->inputcollid,
 										   &args,
-										   false, true, context);
+										   true,
+										   true,
+										   context);
 				if (simple)		/* successfully simplified it */
 					return (Node *) simple;
 
@@ -2494,7 +2465,9 @@ eval_const_expressions_mutator(Node *node,
 											   expr->opcollid,
 											   expr->inputcollid,
 											   &args,
-											   false, false, context);
+											   false,
+											   false,
+											   context);
 					if (simple) /* successfully simplified it */
 					{
 						/*
@@ -2665,7 +2638,6 @@ eval_const_expressions_mutator(Node *node,
 		case T_CoerceViaIO:
 			{
 				CoerceViaIO *expr = (CoerceViaIO *) node;
-				Expr	   *arg;
 				List	   *args;
 				Oid			outfunc;
 				bool		outtypisvarlena;
@@ -2674,12 +2646,8 @@ eval_const_expressions_mutator(Node *node,
 				Expr	   *simple;
 				CoerceViaIO *newexpr;
 
-				/*
-				 * Reduce constants in the CoerceViaIO's argument.
-				 */
-				arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
-															  context);
-				args = list_make1(arg);
+				/* Make a List so we can use simplify_function */
+				args = list_make1(expr->arg);
 
 				/*
 				 * CoerceViaIO represents calling the source type's output
@@ -2690,7 +2658,7 @@ eval_const_expressions_mutator(Node *node,
 				 * Note that the coercion functions are assumed not to care
 				 * about input collation, so we just pass InvalidOid for that.
 				 */
-				getTypeOutputInfo(exprType((Node *) arg),
+				getTypeOutputInfo(exprType((Node *) expr->arg),
 								  &outfunc, &outtypisvarlena);
 				getTypeInputInfo(expr->resulttype,
 								 &infunc, &intypioparam);
@@ -2700,7 +2668,9 @@ eval_const_expressions_mutator(Node *node,
 										   InvalidOid,
 										   InvalidOid,
 										   &args,
-										   false, true, context);
+										   true,
+										   true,
+										   context);
 				if (simple)		/* successfully simplified output fn */
 				{
 					/*
@@ -2729,7 +2699,9 @@ eval_const_expressions_mutator(Node *node,
 											   expr->resultcollid,
 											   InvalidOid,
 											   &args,
-											   false, true, context);
+											   false,
+											   true,
+											   context);
 					if (simple) /* successfully simplified input fn */
 						return (Node *) simple;
 				}
@@ -2740,7 +2712,7 @@ eval_const_expressions_mutator(Node *node,
 				 * possibly-simplified argument.
 				 */
 				newexpr = makeNode(CoerceViaIO);
-				newexpr->arg = arg;
+				newexpr->arg = (Expr *) linitial(args);
 				newexpr->resulttype = expr->resulttype;
 				newexpr->resultcollid = expr->resultcollid;
 				newexpr->coerceformat = expr->coerceformat;
@@ -3577,9 +3549,9 @@ simplify_boolean_equality(Oid opno, List *args)
  * (which might originally have been an operator; we don't care)
  *
  * Inputs are the function OID, actual result type OID (which is needed for
- * polymorphic functions), result typmod, result collation,
- * the input collation to use for the function,
- * the pre-simplified argument list, and some flags;
+ * polymorphic functions), result typmod, result collation, the input
+ * collation to use for the function, the original argument list (not
+ * const-simplified yet, unless process_args is false), and some flags;
  * also the context data for eval_const_expressions.
  *
  * Returns a simplified expression if successful, or NULL if cannot
@@ -3587,17 +3559,19 @@ simplify_boolean_equality(Oid opno, List *args)
  *
  * This function is also responsible for converting named-notation argument
  * lists into positional notation and/or adding any needed default argument
- * expressions; which is a bit grotty, but it avoids an extra fetch of the
+ * expressions; which is a bit grotty, but it avoids extra fetches of the
  * function's pg_proc tuple.  For this reason, the args list is
- * pass-by-reference, and it may get modified even if simplification fails.
+ * pass-by-reference.  Conversion and const-simplification of the args list
+ * will be done even if simplification of the function call itself is not
+ * possible.
  */
 static Expr *
 simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
-				  Oid result_collid, Oid input_collid, List **args,
-				  bool has_named_args,
-				  bool allow_non_const,
+				  Oid result_collid, Oid input_collid, List **args_p,
+				  bool process_args, bool allow_non_const,
 				  eval_const_expressions_context *context)
 {
+	List	   *args = *args_p;
 	HeapTuple	func_tuple;
 	Form_pg_proc func_form;
 	Expr	   *newexpr;
@@ -3620,17 +3594,25 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 	func_form = (Form_pg_proc) GETSTRUCT(func_tuple);
 
 	/*
-	 * While we have the tuple, reorder named arguments and add default
-	 * arguments if needed.
+	 * Process the function arguments, unless the caller did it already.
+	 *
+	 * Here we must deal with named or defaulted arguments, and then
+	 * recursively apply eval_const_expressions to the whole argument list.
 	 */
-	if (has_named_args)
-		*args = reorder_function_arguments(*args, result_type, func_tuple,
-										   context);
-	else if (func_form->pronargs > list_length(*args))
-		*args = add_function_defaults(*args, result_type, func_tuple, context);
+	if (process_args)
+	{
+		args = expand_function_arguments(args, result_type, func_tuple);
+		args = (List *) expression_tree_mutator((Node *) args,
+												eval_const_expressions_mutator,
+												(void *) context);
+		/* Argument processing done, give it back to the caller */
+		*args_p = args;
+	}
+
+	/* Now attempt simplification of the function call proper. */
 
 	newexpr = evaluate_function(funcid, result_type, result_typmod,
-								result_collid, input_collid, *args,
+								result_collid, input_collid, args,
 								func_tuple, context);
 
 	if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
@@ -3649,7 +3631,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 		fexpr.funcformat = COERCE_DONTCARE;
 		fexpr.funccollid = result_collid;
 		fexpr.inputcollid = input_collid;
-		fexpr.args = *args;
+		fexpr.args = args;
 		fexpr.location = -1;
 
 		newexpr = (Expr *)
@@ -3659,7 +3641,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 
 	if (!newexpr && allow_non_const)
 		newexpr = inline_function(funcid, result_type, result_collid,
-								  input_collid, *args,
+								  input_collid, args,
 								  func_tuple, context);
 
 	ReleaseSysCache(func_tuple);
@@ -3667,6 +3649,54 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 	return newexpr;
 }
 
+/*
+ * expand_function_arguments: convert named-notation args to positional args
+ * and/or insert default args, as needed
+ *
+ * If we need to change anything, the input argument list is copied, not
+ * modified.
+ *
+ * Note: this gets applied to operator argument lists too, even though the
+ * cases it handles should never occur there.  This should be OK since it
+ * will fall through very quickly if there's nothing to do.
+ */
+static List *
+expand_function_arguments(List *args, Oid result_type, HeapTuple func_tuple)
+{
+	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+	bool		has_named_args = false;
+	ListCell   *lc;
+
+	/* Do we have any named arguments? */
+	foreach(lc, args)
+	{
+		Node	   *arg = (Node *) lfirst(lc);
+
+		if (IsA(arg, NamedArgExpr))
+		{
+			has_named_args = true;
+			break;
+		}
+	}
+
+	/* If so, we must apply reorder_function_arguments */
+	if (has_named_args)
+	{
+		args = reorder_function_arguments(args, func_tuple);
+		/* Recheck argument types and add casts if needed */
+		recheck_cast_function_args(args, result_type, func_tuple);
+	}
+	else if (list_length(args) < funcform->pronargs)
+	{
+		/* No named args, but we seem to be short some defaults */
+		args = add_function_defaults(args, func_tuple);
+		/* Recheck argument types and add casts if needed */
+		recheck_cast_function_args(args, result_type, func_tuple);
+	}
+
+	return args;
+}
+
 /*
  * reorder_function_arguments: convert named-notation args to positional args
  *
@@ -3674,14 +3704,12 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
  * impossible to form a truly valid positional call without that.
  */
 static List *
-reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
-						   eval_const_expressions_context *context)
+reorder_function_arguments(List *args, HeapTuple func_tuple)
 {
 	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
 	int			pronargs = funcform->pronargs;
 	int			nargsprovided = list_length(args);
 	Node	   *argarray[FUNC_MAX_ARGS];
-	Bitmapset  *defargnumbers;
 	ListCell   *lc;
 	int			i;
 
@@ -3715,7 +3743,6 @@ reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
 	 * Fetch default expressions, if needed, and insert into array at proper
 	 * locations (they aren't necessarily consecutive or all used)
 	 */
-	defargnumbers = NULL;
 	if (nargsprovided < pronargs)
 	{
 		List	   *defaults = fetch_function_defaults(func_tuple);
@@ -3724,10 +3751,7 @@ reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
 		foreach(lc, defaults)
 		{
 			if (argarray[i] == NULL)
-			{
 				argarray[i] = (Node *) lfirst(lc);
-				defargnumbers = bms_add_member(defargnumbers, i);
-			}
 			i++;
 		}
 	}
@@ -3740,32 +3764,6 @@ reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
 		args = lappend(args, argarray[i]);
 	}
 
-	/* Recheck argument types and add casts if needed */
-	recheck_cast_function_args(args, result_type, func_tuple);
-
-	/*
-	 * Lastly, we have to recursively simplify the defaults we just added (but
-	 * don't recurse on the args passed in, as we already did those). This
-	 * isn't merely an optimization, it's *necessary* since there could be
-	 * functions with named or defaulted arguments down in there.
-	 *
-	 * Note that we do this last in hopes of simplifying any typecasts that
-	 * were added by recheck_cast_function_args --- there shouldn't be any new
-	 * casts added to the explicit arguments, but casts on the defaults are
-	 * possible.
-	 */
-	if (defargnumbers != NULL)
-	{
-		i = 0;
-		foreach(lc, args)
-		{
-			if (bms_is_member(i, defargnumbers))
-				lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
-															context);
-			i++;
-		}
-	}
-
 	return args;
 }
 
@@ -3776,14 +3774,12 @@ reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
  * and so we know we just need to add defaults at the end.
  */
 static List *
-add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
-					  eval_const_expressions_context *context)
+add_function_defaults(List *args, HeapTuple func_tuple)
 {
 	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
 	int			nargsprovided = list_length(args);
 	List	   *defaults;
 	int			ndelete;
-	ListCell   *lc;
 
 	/* Get all the default expressions from the pg_proc tuple */
 	defaults = fetch_function_defaults(func_tuple);
@@ -3795,32 +3791,8 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
 	while (ndelete-- > 0)
 		defaults = list_delete_first(defaults);
 
-	/* And form the combined argument list */
-	args = list_concat(args, defaults);
-
-	/* Recheck argument types and add casts if needed */
-	recheck_cast_function_args(args, result_type, func_tuple);
-
-	/*
-	 * Lastly, we have to recursively simplify the defaults we just added (but
-	 * don't recurse on the args passed in, as we already did those). This
-	 * isn't merely an optimization, it's *necessary* since there could be
-	 * functions with named or defaulted arguments down in there.
-	 *
-	 * Note that we do this last in hopes of simplifying any typecasts that
-	 * were added by recheck_cast_function_args --- there shouldn't be any new
-	 * casts added to the explicit arguments, but casts on the defaults are
-	 * possible.
-	 */
-	foreach(lc, args)
-	{
-		if (nargsprovided-- > 0)
-			continue;			/* skip original arg positions */
-		lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
-													context);
-	}
-
-	return args;
+	/* And form the combined argument list, not modifying the input list */
+	return list_concat(list_copy(args), defaults);
 }
 
 /*
@@ -3859,7 +3831,8 @@ fetch_function_defaults(HeapTuple func_tuple)
  * This should be a no-op if there are no polymorphic arguments,
  * but we do it anyway to be sure.
  *
- * Note: if any casts are needed, the args list is modified in-place.
+ * Note: if any casts are needed, the args list is modified in-place;
+ * caller should have already copied the list structure.
  */
 static void
 recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)