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

Lines changed: 8 additions & 0 deletions
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

Lines changed: 18 additions & 10 deletions
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

Lines changed: 19 additions & 0 deletions
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

Lines changed: 4 additions & 3 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy