While reviewing some unrelated code, I noticed that we are handling
error conditions from Python API functions such as PyList_New() and
PyDict_New() in pretty random ways or not at all.  Here is a patch to
fix that.

Arguably, this is a bug fix, but I'm not sure whether it's worth
meddling with this in the back branches.  Maybe only the places where
the errors are not caught at all should be fixed there.  Comments welcome.

-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From eca2709cd1654e2fc37b75fa3bdfe5e199c86cb9 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pete...@gmx.net>
Date: Tue, 31 Oct 2017 10:49:36 -0400
Subject: [PATCH] Consistently catch errors from Python _New() functions

Python Py*_New() functions can fail and return NULL in out-of-memory
conditions.  The previous code handled that inconsistently or not at
all.  This change organizes that better.  If we are in a function that
is called from Python, we just check for failure and return NULL
ourselves, which will cause any exception information to be passed up.
If we are called from PostgreSQL, we consistently create an "out of
memory" error.
---
 contrib/hstore_plpython/hstore_plpython.c |  4 ++++
 contrib/ltree_plpython/ltree_plpython.c   |  4 ++++
 src/pl/plpython/plpy_cursorobject.c       | 19 ++++++++++++-------
 src/pl/plpython/plpy_exec.c               | 10 +++++++++-
 src/pl/plpython/plpy_main.c               |  2 +-
 src/pl/plpython/plpy_plpymodule.c         |  6 ++++--
 src/pl/plpython/plpy_procedure.c          |  2 ++
 src/pl/plpython/plpy_resultobject.c       | 11 +++++++++++
 src/pl/plpython/plpy_spi.c                | 25 +++++++++++++++++--------
 src/pl/plpython/plpy_typeio.c             |  4 +++-
 10 files changed, 67 insertions(+), 20 deletions(-)

diff --git a/contrib/hstore_plpython/hstore_plpython.c 
b/contrib/hstore_plpython/hstore_plpython.c
index 22366bd40f..218e6612b1 100644
--- a/contrib/hstore_plpython/hstore_plpython.c
+++ b/contrib/hstore_plpython/hstore_plpython.c
@@ -93,6 +93,10 @@ hstore_to_plpython(PG_FUNCTION_ARGS)
        PyObject   *dict;
 
        dict = PyDict_New();
+       if (!dict)
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory")));
 
        for (i = 0; i < count; i++)
        {
diff --git a/contrib/ltree_plpython/ltree_plpython.c 
b/contrib/ltree_plpython/ltree_plpython.c
index ae9b90dd10..e88636a0a9 100644
--- a/contrib/ltree_plpython/ltree_plpython.c
+++ b/contrib/ltree_plpython/ltree_plpython.c
@@ -46,6 +46,10 @@ ltree_to_plpython(PG_FUNCTION_ARGS)
        ltree_level *curlevel;
 
        list = PyList_New(in->numlevel);
+       if (!list)
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory")));
 
        curlevel = LTREE_FIRST(in);
        for (i = 0; i < in->numlevel; i++)
diff --git a/src/pl/plpython/plpy_cursorobject.c 
b/src/pl/plpython/plpy_cursorobject.c
index 0108471bfe..4af7c6621f 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -464,15 +464,20 @@ PLy_cursor_fetch(PyObject *self, PyObject *args)
 
                        Py_DECREF(ret->rows);
                        ret->rows = PyList_New(SPI_processed);
-
-                       for (i = 0; i < SPI_processed; i++)
+                       if (!ret->rows)
                        {
-                               PyObject   *row = 
PLyDict_FromTuple(&cursor->result,
-                                                                               
                        SPI_tuptable->vals[i],
-                                                                               
                        SPI_tuptable->tupdesc);
-
-                               PyList_SetItem(ret->rows, i, row);
+                               Py_DECREF(ret);
+                               ret = NULL;
                        }
+                       else
+                               for (i = 0; i < SPI_processed; i++)
+                               {
+                                       PyObject   *row = 
PLyDict_FromTuple(&cursor->result,
+                                                                               
                                SPI_tuptable->vals[i],
+                                                                               
                                SPI_tuptable->tupdesc);
+
+                                       PyList_SetItem(ret->rows, i, row);
+                               }
                }
 
                SPI_freetuptable(SPI_tuptable);
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 26f61dd0f3..7cfa146c82 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -434,6 +434,9 @@ PLy_function_build_args(FunctionCallInfo fcinfo, 
PLyProcedure *proc)
        PG_TRY();
        {
                args = PyList_New(proc->nargs);
+               if (!args)
+                       return NULL;
+
                for (i = 0; i < proc->nargs; i++)
                {
                        if (proc->args[i].is_rowtype > 0)
@@ -740,7 +743,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, 
PLyProcedure *proc, HeapTuple *r
        {
                pltdata = PyDict_New();
                if (!pltdata)
-                       PLy_elog(ERROR, "could not create new dictionary while 
building trigger arguments");
+                       return NULL;
 
                pltname = PyString_FromString(tdata->tg_trigger->tgname);
                PyDict_SetItemString(pltdata, "name", pltname);
@@ -869,6 +872,11 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, 
PLyProcedure *proc, HeapTuple *r
                        PyObject   *pltarg;
 
                        pltargs = PyList_New(tdata->tg_trigger->tgnargs);
+                       if (!pltargs)
+                       {
+                               Py_DECREF(pltdata);
+                               return NULL;
+                       }
                        for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
                        {
                                pltarg = 
PyString_FromString(tdata->tg_trigger->tgargs[i]);
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index 7df50c09c8..12f61b7ab2 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -167,7 +167,7 @@ PLy_init_interp(void)
        PLy_interp_globals = PyModule_GetDict(mainmod);
        PLy_interp_safe_globals = PyDict_New();
        if (PLy_interp_safe_globals == NULL)
-               PLy_elog(ERROR, "could not create globals");
+               PLy_elog(ERROR, NULL);
        PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
        Py_DECREF(mainmod);
        if (PLy_interp_globals == NULL || PyErr_Occurred())
diff --git a/src/pl/plpython/plpy_plpymodule.c 
b/src/pl/plpython/plpy_plpymodule.c
index 759ad44932..08b0efb158 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -233,7 +233,7 @@ PLy_create_exception(char *name, PyObject *base, PyObject 
*dict,
 
        exc = PyErr_NewException(name, base, dict);
        if (exc == NULL)
-               PLy_elog(ERROR, "could not create exception \"%s\"", name);
+               return NULL;
 
        /*
         * PyModule_AddObject does not add a refcount to the object, for some 
odd
@@ -268,7 +268,7 @@ PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
                PyObject   *dict = PyDict_New();
 
                if (dict == NULL)
-                       PLy_elog(ERROR, "could not generate SPI exceptions");
+                       PLy_elog(ERROR, NULL);
 
                sqlstate = 
PyString_FromString(unpack_sql_state(exception_map[i].sqlstate));
                if (sqlstate == NULL)
@@ -279,6 +279,8 @@ PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
 
                exc = PLy_create_exception(exception_map[i].name, base, dict,
                                                                   
exception_map[i].classname, mod);
+               if (!exc)
+                       PLy_elog(ERROR, "could not create exception \"%s\"", 
exception_map[i].name);
 
                entry = hash_search(PLy_spi_exceptions, 
&exception_map[i].sqlstate,
                                                        HASH_ENTER, &found);
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index 26acc88b27..022bc00293 100644
--- a/src/pl/plpython/plpy_procedure.c
+++ b/src/pl/plpython/plpy_procedure.c
@@ -379,6 +379,8 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
         * all functions
         */
        proc->statics = PyDict_New();
+       if (!proc->statics)
+               PLy_elog(ERROR, NULL);
        PyDict_SetItemString(proc->globals, "SD", proc->statics);
 
        /*
diff --git a/src/pl/plpython/plpy_resultobject.c 
b/src/pl/plpython/plpy_resultobject.c
index 098a366f6f..ca70e25689 100644
--- a/src/pl/plpython/plpy_resultobject.c
+++ b/src/pl/plpython/plpy_resultobject.c
@@ -112,6 +112,11 @@ PLy_result_new(void)
        ob->nrows = PyInt_FromLong(-1);
        ob->rows = PyList_New(0);
        ob->tupdesc = NULL;
+       if (!ob->rows)
+       {
+               Py_DECREF(ob);
+               return NULL;
+       }
 
        return (PyObject *) ob;
 }
@@ -147,6 +152,8 @@ PLy_result_colnames(PyObject *self, PyObject *unused)
        }
 
        list = PyList_New(ob->tupdesc->natts);
+       if (!list)
+               return NULL;
        for (i = 0; i < ob->tupdesc->natts; i++)
        {
                Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
@@ -171,6 +178,8 @@ PLy_result_coltypes(PyObject *self, PyObject *unused)
        }
 
        list = PyList_New(ob->tupdesc->natts);
+       if (!list)
+               return NULL;
        for (i = 0; i < ob->tupdesc->natts; i++)
        {
                Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
@@ -195,6 +204,8 @@ PLy_result_coltypmods(PyObject *self, PyObject *unused)
        }
 
        list = PyList_New(ob->tupdesc->natts);
+       if (!list)
+               return NULL;
        for (i = 0; i < ob->tupdesc->natts; i++)
        {
                Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 955769c5e3..cad1324205 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -389,6 +389,8 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, 
uint64 rows, int status)
        volatile MemoryContext oldcontext;
 
        result = (PLyResultObject *) PLy_result_new();
+       if (!result)
+               return NULL;
        Py_DECREF(result->status);
        result->status = PyInt_FromLong(status);
 
@@ -435,15 +437,22 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, 
uint64 rows, int status)
 
                                Py_DECREF(result->rows);
                                result->rows = PyList_New(rows);
-
-                               PLy_input_tuple_funcs(&args, tuptable->tupdesc);
-                               for (i = 0; i < rows; i++)
+                               if (!result->rows)
                                {
-                                       PyObject   *row = 
PLyDict_FromTuple(&args,
-                                                                               
                                tuptable->vals[i],
-                                                                               
                                tuptable->tupdesc);
-
-                                       PyList_SetItem(result->rows, i, row);
+                                       Py_DECREF(result);
+                                       result = NULL;
+                               }
+                               else
+                               {
+                                       PLy_input_tuple_funcs(&args, 
tuptable->tupdesc);
+                                       for (i = 0; i < rows; i++)
+                                       {
+                                               PyObject   *row = 
PLyDict_FromTuple(&args,
+                                                                               
                                        tuptable->vals[i],
+                                                                               
                                        tuptable->tupdesc);
+
+                                               PyList_SetItem(result->rows, i, 
row);
+                                       }
                                }
                        }
 
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index e4af8cc9ef..569eb5862e 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -289,7 +289,7 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, 
TupleDesc desc)
 
        dict = PyDict_New();
        if (dict == NULL)
-               PLy_elog(ERROR, "could not create new dictionary");
+               return NULL;
 
        PG_TRY();
        {
@@ -675,6 +675,8 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int 
ndim, int dim,
        PyObject   *list;
 
        list = PyList_New(dims[dim]);
+       if (!list)
+               return NULL;
 
        if (dim < ndim - 1)
        {
-- 
2.14.3

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to