Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c03523e

Browse files
committed
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
1 parent 4efbb7d commit c03523e

File tree

4 files changed

+49
-13
lines changed

4 files changed

+49
-13
lines changed

doc/src/sgml/plpython.sgml

+8
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,14 @@ foo = rv[i]["my_column"]
935935
Return a list of column names, list of column type OIDs, and list of
936936
type-specific type modifiers for the columns, respectively.
937937
</para>
938+
939+
<para>
940+
These methods raise an exception when called on a result object from
941+
a command that did not produce a result set, e.g.,
942+
<command>UPDATE</command> without <literal>RETURNING</literal>, or
943+
<command>DROP TABLE</command>. But it is OK to use these methods on
944+
a result set containing zero rows.
945+
</para>
938946
</listitem>
939947
</varlistentry>
940948
</variablelist>

src/pl/plpython/expected/plpython_spi.out

+18-10
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ SELECT join_sequences(sequences) FROM sequences
115115
--
116116
-- plan and result objects
117117
--
118-
CREATE FUNCTION result_nrows_test() RETURNS int
118+
CREATE FUNCTION result_metadata_test(cmd text) RETURNS int
119119
AS $$
120-
plan = plpy.prepare("SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'")
120+
plan = plpy.prepare(cmd)
121121
plpy.info(plan.status()) # not really documented or useful
122122
result = plpy.execute(plan)
123123
if result.status() > 0:
@@ -128,20 +128,28 @@ if result.status() > 0:
128128
else:
129129
return None
130130
$$ LANGUAGE plpythonu;
131-
SELECT result_nrows_test();
131+
SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
132132
INFO: True
133-
CONTEXT: PL/Python function "result_nrows_test"
133+
CONTEXT: PL/Python function "result_metadata_test"
134134
INFO: ['foo', 'bar']
135-
CONTEXT: PL/Python function "result_nrows_test"
135+
CONTEXT: PL/Python function "result_metadata_test"
136136
INFO: [23, 25]
137-
CONTEXT: PL/Python function "result_nrows_test"
137+
CONTEXT: PL/Python function "result_metadata_test"
138138
INFO: [-1, -1]
139-
CONTEXT: PL/Python function "result_nrows_test"
140-
result_nrows_test
141-
-------------------
142-
2
139+
CONTEXT: PL/Python function "result_metadata_test"
140+
result_metadata_test
141+
----------------------
142+
2
143143
(1 row)
144144

145+
SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
146+
INFO: True
147+
CONTEXT: PL/Python function "result_metadata_test"
148+
ERROR: plpy.Error: command did not produce a result set
149+
CONTEXT: Traceback (most recent call last):
150+
PL/Python function "result_metadata_test", line 6, in <module>
151+
plpy.info(result.colnames())
152+
PL/Python function "result_metadata_test"
145153
-- cursor objects
146154
CREATE FUNCTION simple_cursor_test() RETURNS int AS $$
147155
res = plpy.cursor("select fname, lname from users")

src/pl/plpython/plpy_resultobject.c

+19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "plpython.h"
1010

1111
#include "plpy_resultobject.h"
12+
#include "plpy_elog.h"
1213

1314

1415
static void PLy_result_dealloc(PyObject *arg);
@@ -131,6 +132,12 @@ PLy_result_colnames(PyObject *self, PyObject *unused)
131132
PyObject *list;
132133
int i;
133134

135+
if (!ob->tupdesc)
136+
{
137+
PLy_exception_set(PLy_exc_error, "command did not produce a result set");
138+
return NULL;
139+
}
140+
134141
list = PyList_New(ob->tupdesc->natts);
135142
for (i = 0; i < ob->tupdesc->natts; i++)
136143
PyList_SET_ITEM(list, i, PyString_FromString(NameStr(ob->tupdesc->attrs[i]->attname)));
@@ -145,6 +152,12 @@ PLy_result_coltypes(PyObject *self, PyObject *unused)
145152
PyObject *list;
146153
int i;
147154

155+
if (!ob->tupdesc)
156+
{
157+
PLy_exception_set(PLy_exc_error, "command did not produce a result set");
158+
return NULL;
159+
}
160+
148161
list = PyList_New(ob->tupdesc->natts);
149162
for (i = 0; i < ob->tupdesc->natts; i++)
150163
PyList_SET_ITEM(list, i, PyInt_FromLong(ob->tupdesc->attrs[i]->atttypid));
@@ -159,6 +172,12 @@ PLy_result_coltypmods(PyObject *self, PyObject *unused)
159172
PyObject *list;
160173
int i;
161174

175+
if (!ob->tupdesc)
176+
{
177+
PLy_exception_set(PLy_exc_error, "command did not produce a result set");
178+
return NULL;
179+
}
180+
162181
list = PyList_New(ob->tupdesc->natts);
163182
for (i = 0; i < ob->tupdesc->natts; i++)
164183
PyList_SET_ITEM(list, i, PyInt_FromLong(ob->tupdesc->attrs[i]->atttypmod));

src/pl/plpython/sql/plpython_spi.sql

+4-3
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ SELECT join_sequences(sequences) FROM sequences
9393
-- plan and result objects
9494
--
9595

96-
CREATE FUNCTION result_nrows_test() RETURNS int
96+
CREATE FUNCTION result_metadata_test(cmd text) RETURNS int
9797
AS $$
98-
plan = plpy.prepare("SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'")
98+
plan = plpy.prepare(cmd)
9999
plpy.info(plan.status()) # not really documented or useful
100100
result = plpy.execute(plan)
101101
if result.status() > 0:
@@ -107,7 +107,8 @@ else:
107107
return None
108108
$$ LANGUAGE plpythonu;
109109

110-
SELECT result_nrows_test();
110+
SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
111+
SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
111112

112113

113114
-- cursor objects

0 commit comments

Comments
 (0)