diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index f5738d0dc9..e4b7ce057f 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -3048,205 +3048,206 @@ plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc, bool include_generate
 	for (i = 0; i < tupdesc->natts; i++)
 	{
 		Datum		attr;
 		bool		isnull,
 					typisvarlena;
 		char	   *attname;
 		Oid			typoutput;
 		Form_pg_attribute att = TupleDescAttr(tupdesc, i);
 
 		if (att->attisdropped)
 			continue;
 
 		if (att->attgenerated)
 		{
 			/* don't include unless requested */
 			if (!include_generated)
 				continue;
 		}
 
 		attname = NameStr(att->attname);
 		attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
 
 		if (isnull)
 		{
 			/*
 			 * Store (attname => undef) and move on.  Note we can't use
 			 * &PL_sv_undef here; see "AVs, HVs and undefined values" in
 			 * perlguts for an explanation.
 			 */
 			hv_store_string(hv, attname, newSV(0));
 			continue;
 		}
 
 		if (type_is_rowtype(att->atttypid))
 		{
 			SV		   *sv = plperl_hash_from_datum(attr);
 
 			hv_store_string(hv, attname, sv);
 		}
 		else
 		{
 			SV		   *sv;
 			Oid			funcid;
 
 			if (OidIsValid(get_base_element_type(att->atttypid)))
 				sv = plperl_ref_from_pg_array(attr, att->atttypid);
 			else if ((funcid = get_transform_fromsql(att->atttypid, current_call_data->prodesc->lang_oid, current_call_data->prodesc->trftypes)))
 				sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, attr));
 			else
 			{
 				char	   *outputstr;
 
 				/* XXX should have a way to cache these lookups */
 				getTypeOutputInfo(att->atttypid, &typoutput, &typisvarlena);
 
 				outputstr = OidOutputFunctionCall(typoutput, attr);
 				sv = cstr2sv(outputstr);
 				pfree(outputstr);
 			}
 
 			hv_store_string(hv, attname, sv);
 		}
 	}
 	return newRV_noinc((SV *) hv);
 }
 
 
 static void
 check_spi_usage_allowed(void)
 {
 	/* see comment in plperl_fini() */
 	if (plperl_ending)
 	{
 		/* simple croak as we don't want to involve PostgreSQL code */
 		croak("SPI functions can not be used in END blocks");
 	}
 }
 
 
 HV *
 plperl_spi_exec(char *query, int limit)
 {
 	HV		   *ret_hv;
 
 	/*
 	 * Execute the query inside a sub-transaction, so we can cope with errors
 	 * sanely
 	 */
 	MemoryContext oldcontext = CurrentMemoryContext;
 	ResourceOwner oldowner = CurrentResourceOwner;
 
 	check_spi_usage_allowed();
 
 	BeginInternalSubTransaction(NULL);
 	/* Want to run inside function's memory context */
 	MemoryContextSwitchTo(oldcontext);
 
 	PG_TRY();
 	{
 		int			spi_rv;
+		/* current_call_data is not available during validation,  */
+		bool		read_only = (current_call_data ? current_call_data->prodesc->fn_readonly : 0);
 
 		pg_verifymbstr(query, strlen(query), false);
 
-		spi_rv = SPI_execute(query, current_call_data->prodesc->fn_readonly,
-							 limit);
+		spi_rv = SPI_execute(query, read_only, limit);
 		ret_hv = plperl_spi_execute_fetch_result(SPI_tuptable, SPI_processed,
 												 spi_rv);
 
 		/* Commit the inner transaction, return to outer xact context */
 		ReleaseCurrentSubTransaction();
 		MemoryContextSwitchTo(oldcontext);
 		CurrentResourceOwner = oldowner;
 	}
 	PG_CATCH();
 	{
 		ErrorData  *edata;
 
 		/* Save error info */
 		MemoryContextSwitchTo(oldcontext);
 		edata = CopyErrorData();
 		FlushErrorState();
 
 		/* Abort the inner transaction */
 		RollbackAndReleaseCurrentSubTransaction();
 		MemoryContextSwitchTo(oldcontext);
 		CurrentResourceOwner = oldowner;
 
 		/* Punt the error to Perl */
 		croak_cstr(edata->message);
 
 		/* Can't get here, but keep compiler quiet */
 		return NULL;
 	}
 	PG_END_TRY();
 
 	return ret_hv;
 }
 
 
 static HV  *
 plperl_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 processed,
 								int status)
 {
 	dTHX;
 	HV		   *result;
 
 	check_spi_usage_allowed();
 
 	result = newHV();
 
 	hv_store_string(result, "status",
 					cstr2sv(SPI_result_code_string(status)));
 	hv_store_string(result, "processed",
 					(processed > (uint64) UV_MAX) ?
 					newSVnv((NV) processed) :
 					newSVuv((UV) processed));
 
 	if (status > 0 && tuptable)
 	{
 		AV		   *rows;
 		SV		   *row;
 		uint64		i;
 
 		/* Prevent overflow in call to av_extend() */
 		if (processed > (uint64) AV_SIZE_MAX)
 			ereport(ERROR,
 					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 					 errmsg("query result has too many rows to fit in a Perl array")));
 
 		rows = newAV();
 		av_extend(rows, processed);
 		for (i = 0; i < processed; i++)
 		{
 			row = plperl_hash_from_tuple(tuptable->vals[i], tuptable->tupdesc, true);
 			av_push(rows, row);
 		}
 		hv_store_string(result, "rows",
 						newRV_noinc((SV *) rows));
 	}
 
 	SPI_freetuptable(tuptable);
 
 	return result;
 }
 
 
 /*
  * plperl_return_next catches any error and converts it to a Perl error.
  * We assume (perhaps without adequate justification) that we need not abort
  * the current transaction if the Perl code traps the error.
  */
 void
 plperl_return_next(SV *sv)
 {
 	MemoryContext oldcontext = CurrentMemoryContext;
 
 	PG_TRY();
 	{
 		plperl_return_next_internal(sv);
 	}
 	PG_CATCH();
 	{
 		ErrorData  *edata;
 
 		/* Must reset elog.c's state */
