diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out
index 70a4571..1576588 100644
*** a/src/pl/plpython/expected/plpython_composite.out
--- b/src/pl/plpython/expected/plpython_composite.out
*************** SELECT * FROM composite_types_table();
*** 273,275 ****
--- 273,309 ----
  ERROR:  PL/Python functions cannot return type table_record[]
  DETAIL:  PL/Python does not support conversion to arrays of row types.
  CONTEXT:  PL/Python function "composite_types_table"
+ -- check what happens if the output record descriptor changes
+ CREATE FUNCTION return_record(t text) RETURNS record AS $$
+ return {'t': t, 'val': 10}
+ $$ LANGUAGE plpythonu;
+ SELECT * FROM return_record('abc') AS r(t text, val integer);
+   t  | val 
+ -----+-----
+  abc |  10
+ (1 row)
+ 
+ SELECT * FROM return_record('abc') AS r(t text, val bigint);
+   t  | val 
+ -----+-----
+  abc |  10
+ (1 row)
+ 
+ SELECT * FROM return_record('abc') AS r(t text, val integer);
+   t  | val 
+ -----+-----
+  abc |  10
+ (1 row)
+ 
+ SELECT * FROM return_record('abc') AS r(t varchar(30), val integer);
+   t  | val 
+ -----+-----
+  abc |  10
+ (1 row)
+ 
+ SELECT * FROM return_record('abc') AS r(t varchar(100), val integer);
+   t  | val 
+ -----+-----
+  abc |  10
+ (1 row)
+ 
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index cc7a007..1f93c7b 100644
*** a/src/pl/plpython/plpython.c
--- b/src/pl/plpython/plpython.c
*************** typedef int Py_ssize_t;
*** 100,105 ****
--- 100,106 ----
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "parser/parse_type.h"
+ #include "parser/parse_coerce.h"
  #include "tcop/tcopprot.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
*************** static void PLy_input_datum_func(PLyType
*** 334,339 ****
--- 335,341 ----
  static void PLy_input_datum_func2(PLyDatumToOb *, Oid, HeapTuple);
  static void PLy_output_tuple_funcs(PLyTypeInfo *, TupleDesc);
  static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);
+ static void PLy_output_record_funcs(PLyTypeInfo *, TupleDesc);
  
  /* conversion functions */
  static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
*************** PLy_function_build_args(FunctionCallInfo
*** 1262,1269 ****
  		}
  
  		/* Set up output conversion for functions returning RECORD */
! 		if (proc->result.out.d.typoid == RECORDOID &&
! 			proc->result.is_rowtype > 1)
  		{
  			TupleDesc	desc;
  
--- 1264,1270 ----
  		}
  
  		/* Set up output conversion for functions returning RECORD */
! 		if (proc->result.out.d.typoid == RECORDOID)
  		{
  			TupleDesc	desc;
  
*************** PLy_function_build_args(FunctionCallInfo
*** 1275,1288 ****
  						 errmsg("function returning record called in context "
  								"that cannot accept type record")));
  			}
! 			/* bless the record to make it known to the typcache lookup code */
! 			BlessTupleDesc(desc);
! 			/* save the freshly generated typmod */
! 			proc->result.out.d.typmod = desc->tdtypmod;
! 			/* proceed with normal I/O function caching */
! 			PLy_output_tuple_funcs(&(proc->result), desc);
! 			/* it should change is_rowtype to 1, so we won't go through this again */
! 			Assert(proc->result.is_rowtype == 1);
  		}
  	}
  	PG_CATCH();
--- 1276,1284 ----
  						 errmsg("function returning record called in context "
  								"that cannot accept type record")));
  			}
! 
! 			/* cache the output conversion functions */
! 			PLy_output_record_funcs(&(proc->result), desc);
  		}
  	}
  	PG_CATCH();
*************** PLy_input_tuple_funcs(PLyTypeInfo *arg,
*** 1769,1774 ****
--- 1765,1809 ----
  }
  
  static void
+ PLy_output_record_funcs(PLyTypeInfo *arg, TupleDesc desc)
+ {
+ 	/*
+ 	 * If the output record functions are already set, we just have to check
+ 	 * if the record descriptor has not changed
+ 	 */
+ 	bool	can_skip = false;
+ 
+ 	if (arg->is_rowtype == 1)
+ 	{
+ 		int	i;
+ 
+ 		/* the functions are already set, check the attributes */
+ 		Assert(arg->out.r.natts == desc->natts);
+ 		can_skip = true;
+ 
+ 		for (i = 0; i < arg->out.r.natts; i++)
+ 		{
+ 			if (!IsBinaryCoercible(arg->out.r.atts[i].typoid,
+ 								   desc->attrs[i]->atttypid))
+ 				can_skip = false;
+ 		}
+ 	}
+ 
+ 	if (can_skip)
+ 		return;
+ 
+ 	/* bless the record to make it known to the typcache lookup code */
+ 	BlessTupleDesc(desc);
+ 	/* save the freshly generated typmod */
+ 	arg->out.d.typmod = desc->tdtypmod;
+ 	/* proceed with normal I/O function caching */
+ 	PLy_output_tuple_funcs(arg, desc);
+ 	/* it should change is_rowtype to 1, so we won't go through this again
+ 	 * unless the the output record description changes */
+ 	Assert(arg->is_rowtype == 1);
+ }
+ 
+ static void
  PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
  {
  	int			i;
diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql
index 1330e26..db4bd73 100644
*** a/src/pl/plpython/sql/plpython_composite.sql
--- b/src/pl/plpython/sql/plpython_composite.sql
*************** yield {'tab': [['first', 1], ['second',
*** 140,142 ****
--- 140,153 ----
  $$ LANGUAGE plpythonu;
  
  SELECT * FROM composite_types_table();
+ 
+ -- check what happens if the output record descriptor changes
+ CREATE FUNCTION return_record(t text) RETURNS record AS $$
+ return {'t': t, 'val': 10}
+ $$ LANGUAGE plpythonu;
+ 
+ SELECT * FROM return_record('abc') AS r(t text, val integer);
+ SELECT * FROM return_record('abc') AS r(t text, val bigint);
+ SELECT * FROM return_record('abc') AS r(t text, val integer);
+ SELECT * FROM return_record('abc') AS r(t varchar(30), val integer);
+ SELECT * FROM return_record('abc') AS r(t varchar(100), val integer);
