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.
This commit is contained in:
Tom Lane 2016-11-08 12:00:24 -05:00
parent 0d4446083d
commit de4026c673
1 changed files with 41 additions and 45 deletions

View File

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