Move return statements out of PG_TRY blocks.

If we exit a PG_TRY block early via "continue", "break", "goto", or
"return", we'll skip unwinding its exception stack.  This change
moves a couple of such "return" statements in PL/Python out of
PG_TRY blocks.  This was introduced in d0aa965c0a and affects all
supported versions.

We might also be able to add compile-time checks to prevent
recurrence, but that is left as a future exercise.

Reported-by: Mikhail Gribkov, Xing Guo
Author: Xing Guo
Reviewed-by: Michael Paquier, Andres Freund, Tom Lane
Discussion: https://postgr.es/m/CAMEv5_v5Y%2B-D%3DCO1%2Bqoe16sAmgC4sbbQjz%2BUtcHmB6zcgS%2B5Ew%40mail.gmail.com
Discussion: https://postgr.es/m/CACpMh%2BCMsGMRKFzFMm3bYTzQmMU5nfEEoEDU2apJcc4hid36AQ%40mail.gmail.com
Backpatch-through: 11 (all supported versions)
This commit is contained in:
Nathan Bossart 2023-05-03 11:32:43 -07:00
parent 4624aad61a
commit 0af386b0f4
1 changed files with 37 additions and 19 deletions

View File

@ -423,15 +423,20 @@ static PyObject *
PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc) PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc)
{ {
PyObject *volatile arg = NULL; PyObject *volatile arg = NULL;
PyObject *volatile args = NULL; PyObject *args;
int i; int i;
/*
* Make any Py*_New() calls before the PG_TRY block so that we can quickly
* return NULL on failure. We can't return within the PG_TRY block, else
* we'd miss unwinding the exception stack.
*/
args = PyList_New(proc->nargs);
if (!args)
return NULL;
PG_TRY(); PG_TRY();
{ {
args = PyList_New(proc->nargs);
if (!args)
return NULL;
for (i = 0; i < proc->nargs; i++) for (i = 0; i < proc->nargs; i++)
{ {
PLyDatumToOb *arginfo = &proc->args[i]; PLyDatumToOb *arginfo = &proc->args[i];
@ -695,19 +700,34 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
*pltlevel, *pltlevel,
*pltrelid, *pltrelid,
*plttablename, *plttablename,
*plttableschema; *plttableschema,
PyObject *pltargs, *pltargs = NULL,
*pytnew, *pytnew,
*pytold; *pytold,
PyObject *volatile pltdata = NULL; *pltdata;
char *stroid; char *stroid;
/*
* Make any Py*_New() calls before the PG_TRY block so that we can quickly
* return NULL on failure. We can't return within the PG_TRY block, else
* we'd miss unwinding the exception stack.
*/
pltdata = PyDict_New();
if (!pltdata)
return NULL;
if (tdata->tg_trigger->tgnargs)
{
pltargs = PyList_New(tdata->tg_trigger->tgnargs);
if (!pltargs)
{
Py_DECREF(pltdata);
return NULL;
}
}
PG_TRY(); PG_TRY();
{ {
pltdata = PyDict_New();
if (!pltdata)
return NULL;
pltname = PyString_FromString(tdata->tg_trigger->tgname); pltname = PyString_FromString(tdata->tg_trigger->tgname);
PyDict_SetItemString(pltdata, "name", pltname); PyDict_SetItemString(pltdata, "name", pltname);
Py_DECREF(pltname); Py_DECREF(pltname);
@ -838,12 +858,9 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
int i; int i;
PyObject *pltarg; PyObject *pltarg;
pltargs = PyList_New(tdata->tg_trigger->tgnargs); /* pltargs should have been allocated before the PG_TRY block. */
if (!pltargs) Assert(pltargs);
{
Py_DECREF(pltdata);
return NULL;
}
for (i = 0; i < tdata->tg_trigger->tgnargs; i++) for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
{ {
pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]); pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]);
@ -864,6 +881,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
} }
PG_CATCH(); PG_CATCH();
{ {
Py_XDECREF(pltargs);
Py_XDECREF(pltdata); Py_XDECREF(pltdata);
PG_RE_THROW(); PG_RE_THROW();
} }