From de4026c673f195cfdb7aa7cc87cc60e36963f094 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 8 Nov 2016 12:00:24 -0500
Subject: [PATCH] Use heap_modify_tuple not SPI_modifytuple in pl/python
 triggers.

The code here would need some change anyway given planned change in
SPI_modifytuple semantics, since this executes after we've exited the
SPI environment.  But really it's better to just use heap_modify_tuple.

While at it, normalize use of SPI_fnumber: make error messages distinguish
no-such-column from can't-set-system-column, and remove test for deleted
column which is going to migrate into SPI_fnumber.  The lack of a check
for system column names is actually a pre-existing bug here, and might
even qualify as a security bug except that we don't have any trusted
version of plpython.
---
 src/pl/plpython/plpy_exec.c | 86 ++++++++++++++++++-------------------
 1 file changed, 41 insertions(+), 45 deletions(-)

diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index fa583fab164..fa5b25a5fad 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;
 
-- 
GitLab