diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8852bf934638daf8666e918f1bcbe489ec6e910b..e5d5b68084c74a63fd08279cf28d9d3cb677ee3b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -207,9 +207,9 @@ static char *get_variable(Var *var, int levelsup, bool showstar,
 			 deparse_context *context);
 static RangeTblEntry *find_rte_by_refname(const char *refname,
 					deparse_context *context);
+static Node *find_param_referent(Param *param, deparse_context *context,
+					deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
 static void get_parameter(Param *param, deparse_context *context);
-static void print_parameter_expr(Node *expr, ListCell *ancestor_cell,
-					 deparse_namespace *dpns, deparse_context *context);
 static const char *get_simple_binary_op_name(OpExpr *expr);
 static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
 static void appendContextKeyword(deparse_context *context, const char *str,
@@ -3892,15 +3892,21 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
 
 
 /*
- * Get the name of a field of an expression of composite type.
- *
- * This is fairly straightforward except for the case of a Var of type RECORD.
- * Since no actual table or view column is allowed to have type RECORD, such
- * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output.	We
- * drill down to find the ultimate defining expression and attempt to infer
- * the field name from it.	We ereport if we can't determine the name.
+ * Get the name of a field of an expression of composite type.  The
+ * expression is usually a Var, but we handle other cases too.
  *
  * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
+ *
+ * This is fairly straightforward when the expression has a named composite
+ * type; we need only look up the type in the catalogs.  However, the type
+ * could also be RECORD.  Since no actual table or view column is allowed to
+ * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
+ * or to a subquery output.  We drill down to find the ultimate defining
+ * expression and attempt to infer the field name from it.  We ereport if we
+ * can't determine the name.
+ *
+ * Similarly, a PARAM of type RECORD has to refer to some expression of
+ * a determinable composite type.
  */
 static const char *
 get_name_for_var_field(Var *var, int fieldno,
@@ -3925,6 +3931,29 @@ get_name_for_var_field(Var *var, int fieldno,
 			return strVal(list_nth(r->colnames, fieldno - 1));
 	}
 
+	/*
+	 * If it's a Param of type RECORD, try to find what the Param refers to.
+	 */
+	if (IsA(var, Param))
+	{
+		Param	   *param = (Param *) var;
+		ListCell   *ancestor_cell;
+
+		expr = find_param_referent(param, context, &dpns, &ancestor_cell);
+		if (expr)
+		{
+			/* Found a match, so recurse to decipher the field name */
+			deparse_namespace save_dpns;
+			const char *result;
+
+			push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+			result = get_name_for_var_field((Var *) expr, fieldno,
+											0, context);
+			pop_ancestor_plan(dpns, &save_dpns);
+			return result;
+		}
+	}
+
 	/*
 	 * If it's a Var of type RECORD, we have to find what the Var refers to;
 	 * if not, we can use get_expr_result_type. If that fails, we try
@@ -4279,17 +4308,25 @@ find_rte_by_refname(const char *refname, deparse_context *context)
 }
 
 /*
- * Display a Param appropriately.
+ * Try to find the referenced expression for a PARAM_EXEC Param that might
+ * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
+ *
+ * If successful, return the expression and set *dpns_p and *ancestor_cell_p
+ * appropriately for calling push_ancestor_plan().  If no referent can be
+ * found, return NULL.
  */
-static void
-get_parameter(Param *param, deparse_context *context)
+static Node *
+find_param_referent(Param *param, deparse_context *context,
+					deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
 {
+	/* Initialize output parameters to prevent compiler warnings */
+	*dpns_p = NULL;
+	*ancestor_cell_p = NULL;
+
 	/*
-	 * If it's a PARAM_EXEC parameter, try to locate the expression from which
-	 * the parameter was computed.	This will necessarily be in some ancestor
-	 * of the current expression's PlanState.  Note that failing to find a
-	 * referent isn't an error, since the Param might well be a subplan output
-	 * rather than an input.
+	 * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
+	 * SubPlan argument.  This will necessarily be in some ancestor of the
+	 * current expression's PlanState.
 	 */
 	if (param->paramkind == PARAM_EXEC)
 	{
@@ -4324,10 +4361,10 @@ get_parameter(Param *param, deparse_context *context)
 
 					if (nlp->paramno == param->paramid)
 					{
-						/* Found a match, so print it */
-						print_parameter_expr((Node *) nlp->paramval, lc,
-											 dpns, context);
-						return;
+						/* Found a match, so return it */
+						*dpns_p = dpns;
+						*ancestor_cell_p = lc;
+						return (Node *) nlp->paramval;
 					}
 				}
 			}
@@ -4353,9 +4390,10 @@ get_parameter(Param *param, deparse_context *context)
 
 					if (paramid == param->paramid)
 					{
-						/* Found a match, so print it */
-						print_parameter_expr(arg, lc, dpns, context);
-						return;
+						/* Found a match, so return it */
+						*dpns_p = dpns;
+						*ancestor_cell_p = lc;
+						return arg;
 					}
 				}
 
@@ -4390,50 +4428,71 @@ get_parameter(Param *param, deparse_context *context)
 		}
 	}
 
-	/*
-	 * Not PARAM_EXEC, or couldn't find referent: just print $N.
-	 */
-	appendStringInfo(context->buf, "$%d", param->paramid);
+	/* No referent found */
+	return NULL;
 }
 
-/* Print a parameter reference expression found by get_parameter */
+/*
+ * Display a Param appropriately.
+ */
 static void
-print_parameter_expr(Node *expr, ListCell *ancestor_cell,
-					 deparse_namespace *dpns, deparse_context *context)
+get_parameter(Param *param, deparse_context *context)
 {
-	deparse_namespace save_dpns;
-	bool		save_varprefix;
-	bool		need_paren;
-
-	/* Switch attention to the ancestor plan node */
-	push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+	Node	   *expr;
+	deparse_namespace *dpns;
+	ListCell   *ancestor_cell;
 
 	/*
-	 * Force prefixing of Vars, since they won't belong to the relation being
-	 * scanned in the original plan node.
+	 * If it's a PARAM_EXEC parameter, try to locate the expression from which
+	 * the parameter was computed.  Note that failing to find a referent isn't
+	 * an error, since the Param might well be a subplan output rather than an
+	 * input.
 	 */
-	save_varprefix = context->varprefix;
-	context->varprefix = true;
+	expr = find_param_referent(param, context, &dpns, &ancestor_cell);
+	if (expr)
+	{
+		/* Found a match, so print it */
+		deparse_namespace save_dpns;
+		bool		save_varprefix;
+		bool		need_paren;
 
-	/*
-	 * A Param's expansion is typically a Var, Aggref, or upper-level Param,
-	 * which wouldn't need extra parentheses.  Otherwise, insert parens to
-	 * ensure the expression looks atomic.
-	 */
-	need_paren = !(IsA(expr, Var) ||
-				   IsA(expr, Aggref) ||
-				   IsA(expr, Param));
-	if (need_paren)
-		appendStringInfoChar(context->buf, '(');
+		/* Switch attention to the ancestor plan node */
+		push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
 
-	get_rule_expr(expr, context, false);
+		/*
+		 * Force prefixing of Vars, since they won't belong to the relation
+		 * being scanned in the original plan node.
+		 */
+		save_varprefix = context->varprefix;
+		context->varprefix = true;
 
-	if (need_paren)
-		appendStringInfoChar(context->buf, ')');
+		/*
+		 * A Param's expansion is typically a Var, Aggref, or upper-level
+		 * Param, which wouldn't need extra parentheses.  Otherwise, insert
+		 * parens to ensure the expression looks atomic.
+		 */
+		need_paren = !(IsA(expr, Var) ||
+					   IsA(expr, Aggref) ||
+					   IsA(expr, Param));
+		if (need_paren)
+			appendStringInfoChar(context->buf, '(');
+
+		get_rule_expr(expr, context, false);
+
+		if (need_paren)
+			appendStringInfoChar(context->buf, ')');
+
+		context->varprefix = save_varprefix;
 
-	context->varprefix = save_varprefix;
+		pop_ancestor_plan(dpns, &save_dpns);
 
-	pop_ancestor_plan(dpns, &save_dpns);
+		return;
+	}
+
+	/*
+	 * Not PARAM_EXEC, or couldn't find referent: just print $N.
+	 */
+	appendStringInfo(context->buf, "$%d", param->paramid);
 }
 
 /*