diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index 5c1cd8cebfdc744de01ef700284e2f561afa275b..906dc15e0ca097ec962c6dce9a08b29cb31d35b5 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -101,6 +101,16 @@ SELECT * FROM perl_row();
   1 | hello | world | ({{1}})
 (1 row)
 
+-- test returning a composite literal
+CREATE OR REPLACE FUNCTION perl_row_lit() RETURNS testrowperl AS $$
+    return '(1,hello,world,"({{1}})")';
+$$ LANGUAGE plperl;
+SELECT perl_row_lit();
+       perl_row_lit        
+---------------------------
+ (1,hello,world,"({{1}})")
+(1 row)
+
 CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$
     return undef;
 $$  LANGUAGE plperl;
@@ -336,7 +346,8 @@ CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$
 return 42;
 $$ LANGUAGE plperl;
 SELECT * FROM foo_bad();
-ERROR:  composite-returning PL/Perl function must return reference to hash
+ERROR:  malformed record literal: "42"
+DETAIL:  Missing left parenthesis.
 CONTEXT:  PL/Perl function "foo_bad"
 CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$
 return [
@@ -345,7 +356,7 @@ return [
 ];
 $$ LANGUAGE plperl;
 SELECT * FROM foo_bad();
-ERROR:  composite-returning PL/Perl function must return reference to hash
+ERROR:  cannot convert Perl array to non-array type footype
 CONTEXT:  PL/Perl function "foo_bad"
 CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$
     return 42;
@@ -639,3 +650,46 @@ CONTEXT:  PL/Perl anonymous code block
 DO $do$ use warnings FATAL => qw(void) ; my @y; my $x = sort @y; 1; $do$ LANGUAGE plperl;
 ERROR:  Useless use of sort in scalar context at line 1.
 CONTEXT:  PL/Perl anonymous code block
+-- make sure functions marked as VOID without an explicit return work
+CREATE OR REPLACE FUNCTION myfuncs() RETURNS void AS $$
+   $_SHARED{myquote} = sub {
+       my $arg = shift;
+       $arg =~ s/(['\\])/\\$1/g;
+       return "'$arg'";
+   };
+$$ LANGUAGE plperl;
+SELECT myfuncs();
+ myfuncs 
+---------
+ 
+(1 row)
+
+-- make sure we can't return an array as a scalar
+CREATE OR REPLACE FUNCTION text_arrayref() RETURNS text AS $$
+	return ['array'];
+$$ LANGUAGE plperl;
+SELECT text_arrayref();
+ERROR:  cannot convert Perl array to non-array type text
+CONTEXT:  PL/Perl function "text_arrayref"
+--- make sure we can't return a hash as a scalar
+CREATE OR REPLACE FUNCTION text_hashref() RETURNS text AS $$
+	return {'hash'=>1};
+$$ LANGUAGE plperl;
+SELECT text_hashref();
+ERROR:  cannot convert Perl hash to non-composite type text
+CONTEXT:  PL/Perl function "text_hashref"
+---- make sure we can't return a blessed object as a scalar
+CREATE OR REPLACE FUNCTION text_obj() RETURNS text AS $$
+	return bless({}, 'Fake::Object');
+$$ LANGUAGE plperl;
+SELECT text_obj();
+ERROR:  cannot convert Perl hash to non-composite type text
+CONTEXT:  PL/Perl function "text_obj"
+----- make sure we can't return a scalar ref
+CREATE OR REPLACE FUNCTION text_scalarref() RETURNS text AS $$
+	my $str = 'str';
+	return \$str;
+$$ LANGUAGE plperl;
+SELECT text_scalarref();
+ERROR:  PL/Perl function must return reference to hash or array
+CONTEXT:  PL/Perl function "text_scalarref"
diff --git a/src/pl/plperl/expected/plperl_array.out b/src/pl/plperl/expected/plperl_array.out
index be76f6cc240b7b4983c3d08c8524b6af3e433b21..829733fef4f8e389ee8da13873eb79c8f67aa8ba 100644
--- a/src/pl/plperl/expected/plperl_array.out
+++ b/src/pl/plperl/expected/plperl_array.out
@@ -204,6 +204,16 @@ select plperl_arrays_inout('{{1}, {2}, {3}}');
  {{1},{2},{3}}
 (1 row)
 
+-- check that we can return an array literal
+CREATE OR REPLACE FUNCTION plperl_arrays_inout_l(INTEGER[]) returns INTEGER[] AS $$
+	return shift.''; # stringify it
+$$ LANGUAGE plperl;
+select plperl_arrays_inout_l('{{1}, {2}, {3}}');
+ plperl_arrays_inout_l 
+-----------------------
+ {{1},{2},{3}}
+(1 row)
+
 -- make sure setof works
 create or replace function perl_setof_array(integer[]) returns setof integer[] language plperl as $$
 	my $arr = shift;
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 784e137976c9e557f75b7fdabdf6d16cee3a148f..fb23ae2d935fb04bc16f78e7af999deaadffd799 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -155,7 +155,6 @@ typedef struct plperl_call_data
 	FunctionCallInfo fcinfo;
 	Tuplestorestate *tuple_store;
 	TupleDesc	ret_tdesc;
-	AttInMetadata *attinmeta;
 	MemoryContext tmp_cxt;
 } plperl_call_data;
 
@@ -244,12 +243,16 @@ static SV  *plperl_ref_from_pg_array(Datum arg, Oid typid);
 static SV  *split_array(plperl_array_info *info, int first, int last, int nest);
 static SV  *make_array_ref(plperl_array_info *info, int first, int last);
 static SV  *get_perl_array_ref(SV *sv);
-static Datum plperl_sv_to_datum(SV *sv, FmgrInfo *func, Oid typid,
-				   Oid typioparam, int32 typmod, bool *isnull);
-static void _sv_to_datum_finfo(FmgrInfo *fcinfo, Oid typid, Oid *typioparam);
-static Datum plperl_array_to_datum(SV *src, Oid typid);
-static ArrayBuildState *_array_to_datum(AV *av, int *ndims, int *dims,
-			  int cur_depth, ArrayBuildState *astate, Oid typid, Oid atypid);
+static Datum plperl_sv_to_datum(SV *sv, Oid typid, int32 typmod,
+				   FunctionCallInfo fcinfo,
+				   FmgrInfo *finfo, Oid typioparam,
+				   bool *isnull);
+static void _sv_to_datum_finfo(Oid typid, FmgrInfo *finfo, Oid *typioparam);
+static Datum plperl_array_to_datum(SV *src, Oid typid, int32 typmod);
+static ArrayBuildState *array_to_datum_internal(AV *av, ArrayBuildState *astate,
+						int *ndims, int *dims, int cur_depth,
+						Oid arraytypid, Oid elemtypid, int32 typmod,
+						FmgrInfo *finfo, Oid typioparam);
 static Datum plperl_hash_to_datum(SV *src, TupleDesc td);
 
 static void plperl_init_shared_libs(pTHX);
@@ -988,9 +991,8 @@ strip_trailing_ws(const char *msg)
 /* Build a tuple from a hash. */
 
 static HeapTuple
-plperl_build_tuple_result(HV *perlhash, AttInMetadata *attinmeta)
+plperl_build_tuple_result(HV *perlhash, TupleDesc td)
 {
-	TupleDesc	td = attinmeta->tupdesc;
 	Datum	   *values;
 	bool	   *nulls;
 	HE		   *he;
@@ -1006,7 +1008,6 @@ plperl_build_tuple_result(HV *perlhash, AttInMetadata *attinmeta)
 		SV		   *val = HeVAL(he);
 		char	   *key = hek2cstr(he);
 		int			attn = SPI_fnumber(td, key);
-		bool		isnull;
 
 		if (attn <= 0 || td->attrs[attn - 1]->attisdropped)
 			ereport(ERROR,
@@ -1015,12 +1016,12 @@ plperl_build_tuple_result(HV *perlhash, AttInMetadata *attinmeta)
 							key)));
 
 		values[attn - 1] = plperl_sv_to_datum(val,
-											  NULL,
 											  td->attrs[attn - 1]->atttypid,
-											  InvalidOid,
 											  td->attrs[attn - 1]->atttypmod,
-											  &isnull);
-		nulls[attn - 1] = isnull;
+											  NULL,
+											  NULL,
+											  InvalidOid,
+											  &nulls[attn - 1]);
 
 		pfree(key);
 	}
@@ -1036,8 +1037,7 @@ plperl_build_tuple_result(HV *perlhash, AttInMetadata *attinmeta)
 static Datum
 plperl_hash_to_datum(SV *src, TupleDesc td)
 {
-	AttInMetadata *attinmeta = TupleDescGetAttInMetadata(td);
-	HeapTuple	tup = plperl_build_tuple_result((HV *) SvRV(src), attinmeta);
+	HeapTuple	tup = plperl_build_tuple_result((HV *) SvRV(src), td);
 
 	return HeapTupleGetDatum(tup);
 }
@@ -1069,13 +1069,15 @@ get_perl_array_ref(SV *sv)
 }
 
 /*
- * helper function for plperl_array_to_datum, does the main recursing
+ * helper function for plperl_array_to_datum, recurses for multi-D arrays
  */
 static ArrayBuildState *
-_array_to_datum(AV *av, int *ndims, int *dims, int cur_depth,
-				ArrayBuildState *astate, Oid typid, Oid atypid)
+array_to_datum_internal(AV *av, ArrayBuildState *astate,
+						int *ndims, int *dims, int cur_depth,
+						Oid arraytypid, Oid elemtypid, int32 typmod,
+						FmgrInfo *finfo, Oid typioparam)
 {
-	int			i = 0;
+	int			i;
 	int			len = av_len(av) + 1;
 
 	for (i = 0; i < len; i++)
@@ -1091,36 +1093,51 @@ _array_to_datum(AV *av, int *ndims, int *dims, int cur_depth,
 		{
 			AV		   *nav = (AV *) SvRV(sav);
 
+			/* dimensionality checks */
 			if (cur_depth + 1 > MAXDIM)
 				ereport(ERROR,
 						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
 								cur_depth + 1, MAXDIM)));
 
-			/* size based off the first element */
+			/* set size when at first element in this level, else compare */
 			if (i == 0 && *ndims == cur_depth)
 			{
 				dims[*ndims] = av_len(nav) + 1;
 				(*ndims)++;
 			}
-			else
-			{
-				if (av_len(nav) + 1 != dims[cur_depth])
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-							 errmsg("multidimensional arrays must have array expressions with matching dimensions")));
-			}
-
-			astate = _array_to_datum(nav, ndims, dims, cur_depth + 1, astate,
-									 typid, atypid);
+			else if (av_len(nav) + 1 != dims[cur_depth])
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+						 errmsg("multidimensional arrays must have array expressions with matching dimensions")));
+
+			/* recurse to fetch elements of this sub-array */
+			astate = array_to_datum_internal(nav, astate,
+											 ndims, dims, cur_depth + 1,
+											 arraytypid, elemtypid, typmod,
+											 finfo, typioparam);
 		}
 		else
 		{
+			Datum		dat;
 			bool		isnull;
-			Datum		dat = plperl_sv_to_datum(svp ? *svp : NULL, NULL,
-												 atypid, 0, -1, &isnull);
 
-			astate = accumArrayResult(astate, dat, isnull, atypid, NULL);
+			/* scalar after some sub-arrays at same level? */
+			if (*ndims != cur_depth)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+						 errmsg("multidimensional arrays must have array expressions with matching dimensions")));
+
+			dat = plperl_sv_to_datum(svp ? *svp : NULL,
+									 elemtypid,
+									 typmod,
+									 NULL,
+									 finfo,
+									 typioparam,
+									 &isnull);
+
+			astate = accumArrayResult(astate, dat, isnull,
+									  elemtypid, CurrentMemoryContext);
 		}
 	}
 
@@ -1131,89 +1148,141 @@ _array_to_datum(AV *av, int *ndims, int *dims, int cur_depth,
  * convert perl array ref to a datum
  */
 static Datum
-plperl_array_to_datum(SV *src, Oid typid)
+plperl_array_to_datum(SV *src, Oid typid, int32 typmod)
 {
-	ArrayBuildState *astate = NULL;
-	Oid			atypid;
+	ArrayBuildState *astate;
+	Oid			elemtypid;
+	FmgrInfo	finfo;
+	Oid			typioparam;
 	int			dims[MAXDIM];
 	int			lbs[MAXDIM];
 	int			ndims = 1;
 	int			i;
 
-	atypid = get_element_type(typid);
-	if (!atypid)
-		atypid = typid;
+	elemtypid = get_element_type(typid);
+	if (!elemtypid)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("cannot convert Perl array to non-array type %s",
+						format_type_be(typid))));
+
+	_sv_to_datum_finfo(elemtypid, &finfo, &typioparam);
 
 	memset(dims, 0, sizeof(dims));
 	dims[0] = av_len((AV *) SvRV(src)) + 1;
 
-	astate = _array_to_datum((AV *) SvRV(src), &ndims, dims, 1, astate, typid,
-							 atypid);
+	astate = array_to_datum_internal((AV *) SvRV(src), NULL,
+									 &ndims, dims, 1,
+									 typid, elemtypid, typmod,
+									 &finfo, typioparam);
 
 	if (!astate)
-		return PointerGetDatum(construct_empty_array(atypid));
+		return PointerGetDatum(construct_empty_array(elemtypid));
 
 	for (i = 0; i < ndims; i++)
 		lbs[i] = 1;
 
-	return makeMdArrayResult(astate, ndims, dims, lbs, CurrentMemoryContext, true);
+	return makeMdArrayResult(astate, ndims, dims, lbs,
+							 CurrentMemoryContext, true);
 }
 
+/* Get the information needed to convert data to the specified PG type */
 static void
-_sv_to_datum_finfo(FmgrInfo *fcinfo, Oid typid, Oid *typioparam)
+_sv_to_datum_finfo(Oid typid, FmgrInfo *finfo, Oid *typioparam)
 {
 	Oid			typinput;
 
 	/* XXX would be better to cache these lookups */
 	getTypeInputInfo(typid,
 					 &typinput, typioparam);
-	fmgr_info(typinput, fcinfo);
+	fmgr_info(typinput, finfo);
 }
 
 /*
- * convert a sv to datum
- * fcinfo and typioparam are optional and will be looked-up if needed
+ * convert Perl SV to PG datum of type typid, typmod typmod
+ *
+ * Pass the PL/Perl function's fcinfo when attempting to convert to the
+ * function's result type; otherwise pass NULL.  This is used when we need to
+ * resolve the actual result type of a function returning RECORD.
+ *
+ * finfo and typioparam should be the results of _sv_to_datum_finfo for the
+ * given typid, or NULL/InvalidOid to let this function do the lookups.
+ *
+ * *isnull is an output parameter.
  */
 static Datum
-plperl_sv_to_datum(SV *sv, FmgrInfo *finfo, Oid typid, Oid typioparam,
-				   int32 typmod, bool *isnull)
+plperl_sv_to_datum(SV *sv, Oid typid, int32 typmod,
+				   FunctionCallInfo fcinfo,
+				   FmgrInfo *finfo, Oid typioparam,
+				   bool *isnull)
 {
 	FmgrInfo	tmp;
 
 	/* we might recurse */
 	check_stack_depth();
 
-	if (isnull)
-		*isnull = false;
+	*isnull = false;
 
-	if (!sv || !SvOK(sv))
+	/*
+	 * Return NULL if result is undef, or if we're in a function returning
+	 * VOID.  In the latter case, we should pay no attention to the last Perl
+	 * statement's result, and this is a convenient means to ensure that.
+	 */
+	if (!sv || !SvOK(sv) || typid == VOIDOID)
 	{
+		/* look up type info if they did not pass it */
 		if (!finfo)
 		{
-			_sv_to_datum_finfo(&tmp, typid, &typioparam);
+			_sv_to_datum_finfo(typid, &tmp, &typioparam);
 			finfo = &tmp;
 		}
-		if (isnull)
-			*isnull = true;
+		*isnull = true;
+		/* must call typinput in case it wants to reject NULL */
 		return InputFunctionCall(finfo, NULL, typioparam, typmod);
 	}
 	else if (SvROK(sv))
 	{
+		/* handle references */
 		SV		   *sav = get_perl_array_ref(sv);
 
 		if (sav)
 		{
-			return plperl_array_to_datum(sav, typid);
+			/* handle an arrayref */
+			return plperl_array_to_datum(sav, typid, typmod);
 		}
 		else if (SvTYPE(SvRV(sv)) == SVt_PVHV)
 		{
-			TupleDesc	td = lookup_rowtype_tupdesc(typid, typmod);
-			Datum		ret = plperl_hash_to_datum(sv, td);
+			/* handle a hashref */
+			Datum		ret;
+			TupleDesc	td;
 
+			if (!type_is_rowtype(typid))
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("cannot convert Perl hash to non-composite type %s",
+								format_type_be(typid))));
+
+			td = lookup_rowtype_tupdesc_noerror(typid, typmod, true);
+			if (td == NULL)
+			{
+				/* Try to look it up based on our result type */
+				if (fcinfo == NULL ||
+					get_call_result_type(fcinfo, NULL, &td) != TYPEFUNC_COMPOSITE)
+					ereport(ERROR,
+							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							 errmsg("function returning record called in context "
+									"that cannot accept type record")));
+			}
+
+			ret = plperl_hash_to_datum(sv, td);
+
+			/* Release on the result of get_call_result_type is harmless */
 			ReleaseTupleDesc(td);
+
 			return ret;
 		}
 
+		/* Reference, but not reference to hash or array ... */
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 		 errmsg("PL/Perl function must return reference to hash or array")));
@@ -1221,12 +1290,14 @@ plperl_sv_to_datum(SV *sv, FmgrInfo *finfo, Oid typid, Oid typioparam,
 	}
 	else
 	{
+		/* handle a string/number */
 		Datum		ret;
 		char	   *str = sv2cstr(sv);
 
+		/* did not pass in any typeinfo? look it up */
 		if (!finfo)
 		{
-			_sv_to_datum_finfo(&tmp, typid, &typioparam);
+			_sv_to_datum_finfo(typid, &tmp, &typioparam);
 			finfo = &tmp;
 		}
 
@@ -1251,7 +1322,10 @@ plperl_sv_to_literal(SV *sv, char *fqtypename)
 	if (!OidIsValid(typid))
 		elog(ERROR, "lookup failed for type %s", fqtypename);
 
-	datum = plperl_sv_to_datum(sv, NULL, typid, 0, -1, &isnull);
+	datum = plperl_sv_to_datum(sv,
+							   typid, -1,
+							   NULL, NULL, InvalidOid,
+							   &isnull);
 
 	if (isnull)
 		return NULL;
@@ -1542,10 +1616,11 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
 							key)));
 
 		modvalues[slotsused] = plperl_sv_to_datum(val,
-												  NULL,
 										  tupdesc->attrs[attn - 1]->atttypid,
-												  InvalidOid,
 										 tupdesc->attrs[attn - 1]->atttypmod,
+												  NULL,
+												  NULL,
+												  InvalidOid,
 												  &isnull);
 
 		modnulls[slotsused] = isnull ? 'n' : ' ';
@@ -2043,10 +2118,9 @@ plperl_func_handler(PG_FUNCTION_ARGS)
 	Datum		retval = 0;
 	ReturnSetInfo *rsi;
 	ErrorContextCallback pl_error_context;
-	bool		has_retval = false;
 
 	/*
-	 * Create the call_data beforing connecting to SPI, so that it is not
+	 * Create the call_data before connecting to SPI, so that it is not
 	 * allocated in the SPI memory context
 	 */
 	current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
@@ -2129,51 +2203,19 @@ plperl_func_handler(PG_FUNCTION_ARGS)
 			rsi->setDesc = current_call_data->ret_tdesc;
 		}
 		retval = (Datum) 0;
-		has_retval = true;
 	}
-	else if (!SvOK(perlret))
-	{
-		/* Return NULL if Perl code returned undef */
-		if (rsi && IsA(rsi, ReturnSetInfo))
-			rsi->isDone = ExprEndResult;
-	}
-	else if (prodesc->fn_retistuple)
-	{
-		/* Return a perl hash converted to a Datum */
-		TupleDesc	td;
-
-		if (!SvOK(perlret) || !SvROK(perlret) ||
-			SvTYPE(SvRV(perlret)) != SVt_PVHV)
-		{
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("composite-returning PL/Perl function "
-							"must return reference to hash")));
-		}
-
-		/* XXX should cache the attinmeta data instead of recomputing */
-		if (get_call_result_type(fcinfo, NULL, &td) != TYPEFUNC_COMPOSITE)
-		{
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("function returning record called in context "
-							"that cannot accept type record")));
-		}
-
-		retval = plperl_hash_to_datum(perlret, td);
-		has_retval = true;
-	}
-
-	if (!has_retval)
+	else
 	{
-		bool		isnull;
-
 		retval = plperl_sv_to_datum(perlret,
-									&prodesc->result_in_func,
 									prodesc->result_oid,
-									prodesc->result_typioparam, -1, &isnull);
-		fcinfo->isnull = isnull;
-		has_retval = true;
+									-1,
+									fcinfo,
+									&prodesc->result_in_func,
+									prodesc->result_typioparam,
+									&fcinfo->isnull);
+
+		if (fcinfo->isnull && rsi && IsA(rsi, ReturnSetInfo))
+			rsi->isDone = ExprEndResult;
 	}
 
 	/* Restore the previous error callback */
@@ -2196,7 +2238,7 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
 	ErrorContextCallback pl_error_context;
 
 	/*
-	 * Create the call_data beforing connecting to SPI, so that it is not
+	 * Create the call_data before connecting to SPI, so that it is not
 	 * allocated in the SPI memory context
 	 */
 	current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
@@ -2842,19 +2884,11 @@ plperl_return_next(SV *sv)
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("cannot use return_next in a non-SETOF function")));
 
-	if (prodesc->fn_retistuple &&
-		!(SvOK(sv) && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV))
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-				 errmsg("SETOF-composite-returning PL/Perl function "
-						"must call return_next with reference to hash")));
-
 	if (!current_call_data->ret_tdesc)
 	{
 		TupleDesc	tupdesc;
 
 		Assert(!current_call_data->tuple_store);
-		Assert(!current_call_data->attinmeta);
 
 		/*
 		 * This is the first call to return_next in the current PL/Perl
@@ -2875,11 +2909,6 @@ plperl_return_next(SV *sv)
 		current_call_data->tuple_store =
 			tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
 								  false, work_mem);
-		if (prodesc->fn_retistuple)
-		{
-			current_call_data->attinmeta =
-				TupleDescGetAttInMetadata(current_call_data->ret_tdesc);
-		}
 
 		MemoryContextSwitchTo(old_cxt);
 	}
@@ -2893,7 +2922,7 @@ plperl_return_next(SV *sv)
 	if (!current_call_data->tmp_cxt)
 	{
 		current_call_data->tmp_cxt =
-			AllocSetContextCreate(rsi->econtext->ecxt_per_tuple_memory,
+			AllocSetContextCreate(CurrentMemoryContext,
 								  "PL/Perl return_next temporary cxt",
 								  ALLOCSET_DEFAULT_MINSIZE,
 								  ALLOCSET_DEFAULT_INITSIZE,
@@ -2906,8 +2935,14 @@ plperl_return_next(SV *sv)
 	{
 		HeapTuple	tuple;
 
+		if (!(SvOK(sv) && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV))
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("SETOF-composite-returning PL/Perl function "
+							"must call return_next with reference to hash")));
+
 		tuple = plperl_build_tuple_result((HV *) SvRV(sv),
-										  current_call_data->attinmeta);
+										  current_call_data->ret_tdesc);
 		tuplestore_puttuple(current_call_data->tuple_store, tuple);
 	}
 	else
@@ -2916,10 +2951,12 @@ plperl_return_next(SV *sv)
 		bool		isNull;
 
 		ret = plperl_sv_to_datum(sv,
-								 &prodesc->result_in_func,
 								 prodesc->result_oid,
+								 -1,
+								 fcinfo,
+								 &prodesc->result_in_func,
 								 prodesc->result_typioparam,
-								 -1, &isNull);
+								 &isNull);
 
 		tuplestore_putvalues(current_call_data->tuple_store,
 							 current_call_data->ret_tdesc,
@@ -3318,10 +3355,12 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
 			bool		isnull;
 
 			argvalues[i] = plperl_sv_to_datum(argv[i],
-											  &qdesc->arginfuncs[i],
 											  qdesc->argtypes[i],
+											  -1,
+											  NULL,
+											  &qdesc->arginfuncs[i],
 											  qdesc->argtypioparams[i],
-											  -1, &isnull);
+											  &isnull);
 			nulls[i] = isnull ? 'n' : ' ';
 		}
 
@@ -3443,10 +3482,12 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
 			bool		isnull;
 
 			argvalues[i] = plperl_sv_to_datum(argv[i],
-											  &qdesc->arginfuncs[i],
 											  qdesc->argtypes[i],
+											  -1,
+											  NULL,
+											  &qdesc->arginfuncs[i],
 											  qdesc->argtypioparams[i],
-											  -1, &isnull);
+											  &isnull);
 			nulls[i] = isnull ? 'n' : ' ';
 		}
 
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index 4aaca2a27c55d4581d1977ed9a0482f9153c968c..a5e3840dac23667ff2d599c46b6e6887cc6875a8 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -50,6 +50,13 @@ $$ LANGUAGE plperl;
 SELECT perl_row();
 SELECT * FROM perl_row();
 
+-- test returning a composite literal
+CREATE OR REPLACE FUNCTION perl_row_lit() RETURNS testrowperl AS $$
+    return '(1,hello,world,"({{1}})")';
+$$ LANGUAGE plperl;
+
+SELECT perl_row_lit();
+
 
 CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$
     return undef;
@@ -415,3 +422,43 @@ DO $do$ use strict; my $name = "foo"; my $ref = $$name; $do$ LANGUAGE plperl;
 -- check that we can "use warnings" (in this case to turn a warn into an error)
 -- yields "ERROR:  Useless use of sort in scalar context."
 DO $do$ use warnings FATAL => qw(void) ; my @y; my $x = sort @y; 1; $do$ LANGUAGE plperl;
+
+-- make sure functions marked as VOID without an explicit return work
+CREATE OR REPLACE FUNCTION myfuncs() RETURNS void AS $$
+   $_SHARED{myquote} = sub {
+       my $arg = shift;
+       $arg =~ s/(['\\])/\\$1/g;
+       return "'$arg'";
+   };
+$$ LANGUAGE plperl;
+
+SELECT myfuncs();
+
+-- make sure we can't return an array as a scalar
+CREATE OR REPLACE FUNCTION text_arrayref() RETURNS text AS $$
+	return ['array'];
+$$ LANGUAGE plperl;
+
+SELECT text_arrayref();
+
+--- make sure we can't return a hash as a scalar
+CREATE OR REPLACE FUNCTION text_hashref() RETURNS text AS $$
+	return {'hash'=>1};
+$$ LANGUAGE plperl;
+
+SELECT text_hashref();
+
+---- make sure we can't return a blessed object as a scalar
+CREATE OR REPLACE FUNCTION text_obj() RETURNS text AS $$
+	return bless({}, 'Fake::Object');
+$$ LANGUAGE plperl;
+
+SELECT text_obj();
+
+----- make sure we can't return a scalar ref
+CREATE OR REPLACE FUNCTION text_scalarref() RETURNS text AS $$
+	my $str = 'str';
+	return \$str;
+$$ LANGUAGE plperl;
+
+SELECT text_scalarref();
diff --git a/src/pl/plperl/sql/plperl_array.sql b/src/pl/plperl/sql/plperl_array.sql
index bc67c1ad0df0171aaba740c371e7e9894f5a9207..818a48ec200d6c18d95e6e2e98e2f7c6bd49506c 100644
--- a/src/pl/plperl/sql/plperl_array.sql
+++ b/src/pl/plperl/sql/plperl_array.sql
@@ -152,6 +152,13 @@ $$ LANGUAGE plperl;
 
 select plperl_arrays_inout('{{1}, {2}, {3}}');
 
+-- check that we can return an array literal
+CREATE OR REPLACE FUNCTION plperl_arrays_inout_l(INTEGER[]) returns INTEGER[] AS $$
+	return shift.''; # stringify it
+$$ LANGUAGE plperl;
+
+select plperl_arrays_inout_l('{{1}, {2}, {3}}');
+
 -- make sure setof works
 create or replace function perl_setof_array(integer[]) returns setof integer[] language plperl as $$
 	my $arr = shift;