diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 498bcba5816e4b24a07025392a68fb3872110951..11f92adbb0655d56c032b632175b523f6f7b3fab 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1171,6 +1171,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
 		 * This can happen, for example, where the body of the function is
 		 * 'SELECT func2()', where func2 has the same composite return type as
 		 * the function that's calling it.
+		 *
+		 * XXX Note that if rettype is RECORD, the IsBinaryCoercible check
+		 * will succeed for any composite restype.  For the moment we rely on
+		 * runtime type checking to catch any discrepancy, but it'd be nice to
+		 * do better at parse time.
 		 */
 		if (tlistlen == 1)
 		{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index de2e66b0f160ddb1356e545ba09fa28e4abca706..80dfaad736f09b96cce5542a3909d9c71a1c3684 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -3677,6 +3677,10 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
  * We must also beware of changing the volatility or strictness status of
  * functions by inlining them.
  *
+ * Also, at the moment we can't inline functions returning RECORD.  This
+ * doesn't work in the general case because it discards information such
+ * as OUT-parameter declarations.
+ *
  * Returns a simplified expression if successful, or NULL if cannot
  * simplify the function.
  */
@@ -3709,6 +3713,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
 	if (funcform->prolang != SQLlanguageId ||
 		funcform->prosecdef ||
 		funcform->proretset ||
+		funcform->prorettype == RECORDOID ||
 		!heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
 		funcform->pronargs != list_length(args))
 		return NULL;
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index f1466e0d442f484e11b8c4b2404f2783fafc7bea..51d561b28fa19e846c8c67863eef71b9b8d864e5 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -898,3 +898,23 @@ select * from foobar();
 (2 rows)
 
 drop function foobar();
+-- check handling of a SQL function with multiple OUT params (bug #5777)
+create or replace function foobar(out integer, out numeric) as
+$$ select (1, 2.1) $$ language sql;
+select * from foobar();
+ column1 | column2 
+---------+---------
+       1 |     2.1
+(1 row)
+
+create or replace function foobar(out integer, out numeric) as
+$$ select (1, 2) $$ language sql;
+select * from foobar();  -- fail
+ERROR:  function return row and query-specified return row do not match
+DETAIL:  Returned type integer at ordinal position 2, but query expects numeric.
+create or replace function foobar(out integer, out numeric) as
+$$ select (1, 2.1, 3) $$ language sql;
+select * from foobar();  -- fail
+ERROR:  function return row and query-specified return row do not match
+DETAIL:  Returned row contains 3 attributes, but query expects 2.
+drop function foobar();
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index d1b9db7e51396c18056008b87511e066789ca7e8..54cfc178c057c40f82e27cbd1f3a8e0e271c34b3 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -429,3 +429,22 @@ select foobar();
 select * from foobar();
 
 drop function foobar();
+
+-- check handling of a SQL function with multiple OUT params (bug #5777)
+
+create or replace function foobar(out integer, out numeric) as
+$$ select (1, 2.1) $$ language sql;
+
+select * from foobar();
+
+create or replace function foobar(out integer, out numeric) as
+$$ select (1, 2) $$ language sql;
+
+select * from foobar();  -- fail
+
+create or replace function foobar(out integer, out numeric) as
+$$ select (1, 2.1, 3) $$ language sql;
+
+select * from foobar();  -- fail
+
+drop function foobar();