diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index fa583fab164d1c3fec4d2b17a16300a39d360ba8..fa5b25a5fade10047fba63df98a433cbedc96caf 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -896,18 +896,13 @@ static HeapTuple
 PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 				 HeapTuple otup)
 {
+	HeapTuple	rtup;
 	PyObject   *volatile plntup;
 	PyObject   *volatile plkeys;
 	PyObject   *volatile plval;
-	HeapTuple	rtup;
-	int			natts,
-				i,
-				attn,
-				atti;
-	int		   *volatile modattrs;
 	Datum	   *volatile modvalues;
-	char	   *volatile modnulls;
-	TupleDesc	tupdesc;
+	bool	   *volatile modnulls;
+	bool	   *volatile modrepls;
 	ErrorContextCallback plerrcontext;
 
 	plerrcontext.callback = plpython_trigger_error_callback;
@@ -915,12 +910,16 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 	error_context_stack = &plerrcontext;
 
 	plntup = plkeys = plval = NULL;
-	modattrs = NULL;
 	modvalues = NULL;
 	modnulls = NULL;
+	modrepls = NULL;
 
 	PG_TRY();
 	{
+		TupleDesc	tupdesc;
+		int			nkeys,
+					i;
+
 		if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -932,18 +931,20 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 					 errmsg("TD[\"new\"] is not a dictionary")));
 
 		plkeys = PyDict_Keys(plntup);
-		natts = PyList_Size(plkeys);
-
-		modattrs = (int *) palloc(natts * sizeof(int));
-		modvalues = (Datum *) palloc(natts * sizeof(Datum));
-		modnulls = (char *) palloc(natts * sizeof(char));
+		nkeys = PyList_Size(plkeys);
 
 		tupdesc = tdata->tg_relation->rd_att;
 
-		for (i = 0; i < natts; i++)
+		modvalues = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
+		modnulls = (bool *) palloc0(tupdesc->natts * sizeof(bool));
+		modrepls = (bool *) palloc0(tupdesc->natts * sizeof(bool));
+
+		for (i = 0; i < nkeys; i++)
 		{
 			PyObject   *platt;
 			char	   *plattstr;
+			int			attn;
+			PLyObToDatum *att;
 
 			platt = PyList_GetItem(plkeys, i);
 			if (PyString_Check(platt))
@@ -963,7 +964,12 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 						(errcode(ERRCODE_UNDEFINED_COLUMN),
 						 errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
 								plattstr)));
-			atti = attn - 1;
+			if (attn <= 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("cannot set system attribute \"%s\"",
+								plattstr)));
+			att = &proc->result.out.r.atts[attn - 1];
 
 			plval = PyDict_GetItem(plntup, platt);
 			if (plval == NULL)
@@ -971,41 +977,31 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 
 			Py_INCREF(plval);
 
-			modattrs[i] = attn;
-
-			if (tupdesc->attrs[atti]->attisdropped)
-			{
-				modvalues[i] = (Datum) 0;
-				modnulls[i] = 'n';
-			}
-			else if (plval != Py_None)
+			if (plval != Py_None)
 			{
-				PLyObToDatum *att = &proc->result.out.r.atts[atti];
-
-				modvalues[i] = (att->func) (att,
-											tupdesc->attrs[atti]->atttypmod,
-											plval,
-											false);
-				modnulls[i] = ' ';
+				modvalues[attn - 1] =
+					(att->func) (att,
+								 tupdesc->attrs[attn - 1]->atttypmod,
+								 plval,
+								 false);
+				modnulls[attn - 1] = false;
 			}
 			else
 			{
-				modvalues[i] =
-					InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
+				modvalues[attn - 1] =
+					InputFunctionCall(&att->typfunc,
 									  NULL,
-									proc->result.out.r.atts[atti].typioparam,
-									  tupdesc->attrs[atti]->atttypmod);
-				modnulls[i] = 'n';
+									  att->typioparam,
+									  tupdesc->attrs[attn - 1]->atttypmod);
+				modnulls[attn - 1] = true;
 			}
+			modrepls[attn - 1] = true;
 
 			Py_DECREF(plval);
 			plval = NULL;
 		}
 
-		rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
-							   modattrs, modvalues, modnulls);
-		if (rtup == NULL)
-			elog(ERROR, "SPI_modifytuple failed: error %d", SPI_result);
+		rtup = heap_modify_tuple(otup, tupdesc, modvalues, modnulls, modrepls);
 	}
 	PG_CATCH();
 	{
@@ -1013,12 +1009,12 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 		Py_XDECREF(plkeys);
 		Py_XDECREF(plval);
 
-		if (modnulls)
-			pfree(modnulls);
 		if (modvalues)
 			pfree(modvalues);
-		if (modattrs)
-			pfree(modattrs);
+		if (modnulls)
+			pfree(modnulls);
+		if (modrepls)
+			pfree(modrepls);
 
 		PG_RE_THROW();
 	}
@@ -1027,9 +1023,9 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 	Py_DECREF(plntup);
 	Py_DECREF(plkeys);
 
-	pfree(modattrs);
 	pfree(modvalues);
 	pfree(modnulls);
+	pfree(modrepls);
 
 	error_context_stack = plerrcontext.previous;