From c03523ed3fc65e219068aff536330ce451f63ca7 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Sun, 15 Apr 2012 20:23:08 +0300 Subject: [PATCH] PL/Python: Fix crash when colnames() etc. called without result set The result object methods colnames() etc. would crash when called after a command that did not produce a result set. Now they throw an exception. discovery and initial patch by Jean-Baptiste Quenot --- doc/src/sgml/plpython.sgml | 8 +++++++ src/pl/plpython/expected/plpython_spi.out | 28 +++++++++++++++-------- src/pl/plpython/plpy_resultobject.c | 19 +++++++++++++++ src/pl/plpython/sql/plpython_spi.sql | 7 +++--- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml index 75a939406c..a39438836f 100644 --- a/doc/src/sgml/plpython.sgml +++ b/doc/src/sgml/plpython.sgml @@ -935,6 +935,14 @@ foo = rv[i]["my_column"] Return a list of column names, list of column type OIDs, and list of type-specific type modifiers for the columns, respectively. + + + These methods raise an exception when called on a result object from + a command that did not produce a result set, e.g., + UPDATE without RETURNING, or + DROP TABLE. But it is OK to use these methods on + a result set containing zero rows. + diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out index 9ed081b184..f3709aeb88 100644 --- a/src/pl/plpython/expected/plpython_spi.out +++ b/src/pl/plpython/expected/plpython_spi.out @@ -115,9 +115,9 @@ SELECT join_sequences(sequences) FROM sequences -- -- plan and result objects -- -CREATE FUNCTION result_nrows_test() RETURNS int +CREATE FUNCTION result_metadata_test(cmd text) RETURNS int AS $$ -plan = plpy.prepare("SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'") +plan = plpy.prepare(cmd) plpy.info(plan.status()) # not really documented or useful result = plpy.execute(plan) if result.status() > 0: @@ -128,20 +128,28 @@ if result.status() > 0: else: return None $$ LANGUAGE plpythonu; -SELECT result_nrows_test(); +SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$); INFO: True -CONTEXT: PL/Python function "result_nrows_test" +CONTEXT: PL/Python function "result_metadata_test" INFO: ['foo', 'bar'] -CONTEXT: PL/Python function "result_nrows_test" +CONTEXT: PL/Python function "result_metadata_test" INFO: [23, 25] -CONTEXT: PL/Python function "result_nrows_test" +CONTEXT: PL/Python function "result_metadata_test" INFO: [-1, -1] -CONTEXT: PL/Python function "result_nrows_test" - result_nrows_test -------------------- - 2 +CONTEXT: PL/Python function "result_metadata_test" + result_metadata_test +---------------------- + 2 (1 row) +SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$); +INFO: True +CONTEXT: PL/Python function "result_metadata_test" +ERROR: plpy.Error: command did not produce a result set +CONTEXT: Traceback (most recent call last): + PL/Python function "result_metadata_test", line 6, in + plpy.info(result.colnames()) +PL/Python function "result_metadata_test" -- cursor objects CREATE FUNCTION simple_cursor_test() RETURNS int AS $$ res = plpy.cursor("select fname, lname from users") diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c index b25e8083b9..fcf8074228 100644 --- a/src/pl/plpython/plpy_resultobject.c +++ b/src/pl/plpython/plpy_resultobject.c @@ -9,6 +9,7 @@ #include "plpython.h" #include "plpy_resultobject.h" +#include "plpy_elog.h" static void PLy_result_dealloc(PyObject *arg); @@ -131,6 +132,12 @@ PLy_result_colnames(PyObject *self, PyObject *unused) PyObject *list; int i; + if (!ob->tupdesc) + { + PLy_exception_set(PLy_exc_error, "command did not produce a result set"); + return NULL; + } + list = PyList_New(ob->tupdesc->natts); for (i = 0; i < ob->tupdesc->natts; i++) PyList_SET_ITEM(list, i, PyString_FromString(NameStr(ob->tupdesc->attrs[i]->attname))); @@ -145,6 +152,12 @@ PLy_result_coltypes(PyObject *self, PyObject *unused) PyObject *list; int i; + if (!ob->tupdesc) + { + PLy_exception_set(PLy_exc_error, "command did not produce a result set"); + return NULL; + } + list = PyList_New(ob->tupdesc->natts); for (i = 0; i < ob->tupdesc->natts; i++) PyList_SET_ITEM(list, i, PyInt_FromLong(ob->tupdesc->attrs[i]->atttypid)); @@ -159,6 +172,12 @@ PLy_result_coltypmods(PyObject *self, PyObject *unused) PyObject *list; int i; + if (!ob->tupdesc) + { + PLy_exception_set(PLy_exc_error, "command did not produce a result set"); + return NULL; + } + list = PyList_New(ob->tupdesc->natts); for (i = 0; i < ob->tupdesc->natts; i++) PyList_SET_ITEM(list, i, PyInt_FromLong(ob->tupdesc->attrs[i]->atttypmod)); diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql index b828744d1f..d8457ce98c 100644 --- a/src/pl/plpython/sql/plpython_spi.sql +++ b/src/pl/plpython/sql/plpython_spi.sql @@ -93,9 +93,9 @@ SELECT join_sequences(sequences) FROM sequences -- plan and result objects -- -CREATE FUNCTION result_nrows_test() RETURNS int +CREATE FUNCTION result_metadata_test(cmd text) RETURNS int AS $$ -plan = plpy.prepare("SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'") +plan = plpy.prepare(cmd) plpy.info(plan.status()) # not really documented or useful result = plpy.execute(plan) if result.status() > 0: @@ -107,7 +107,8 @@ else: return None $$ LANGUAGE plpythonu; -SELECT result_nrows_test(); +SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$); +SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$); -- cursor objects