Skip to content

Commit 70ec3f1

Browse files
committed
PL/Python: Add cursor and execute methods to plan object
Instead of plan = plpy.prepare(...) res = plpy.execute(plan, ...) you can now write plan = plpy.prepare(...) res = plan.execute(...) or even res = plpy.prepare(...).execute(...) and similarly for the cursor() method. This is more in object oriented style, and makes the hybrid nature of the existing execute() function less confusing. Reviewed-by: Andrew Dunstan <andrew.dunstan@2ndquadrant.com>
1 parent 090010f commit 70ec3f1

File tree

8 files changed

+79
-11
lines changed

8 files changed

+79
-11
lines changed

doc/src/sgml/plpython.sgml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,14 @@ rv = plpy.execute(plan, ["name"], 5)
10461046
The third argument is the optional row limit as before.
10471047
</para>
10481048

1049+
<para>
1050+
Alternatively, you can call the <function>execute</function> method on
1051+
the plan object:
1052+
<programlisting>
1053+
rv = plan.execute(["name"], 5)
1054+
</programlisting>
1055+
</para>
1056+
10491057
<para>
10501058
Query parameters and result row fields are converted between PostgreSQL
10511059
and Python data types as described in <xref linkend="plpython-data">.
@@ -1081,7 +1089,9 @@ $$ LANGUAGE plpythonu;
10811089
as <literal>plpy.execute</literal> (except for the row limit) and returns
10821090
a cursor object, which allows you to process large result sets in smaller
10831091
chunks. As with <literal>plpy.execute</literal>, either a query string
1084-
or a plan object along with a list of arguments can be used.
1092+
or a plan object along with a list of arguments can be used, or
1093+
the <function>cursor</function> function can be called as a method of
1094+
the plan object.
10851095
</para>
10861096

10871097
<para>
@@ -1125,7 +1135,7 @@ $$ LANGUAGE plpythonu;
11251135
CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$
11261136
odd = 0
11271137
plan = plpy.prepare("select num from largetable where num % $1 &lt;&gt; 0", ["integer"])
1128-
rows = list(plpy.cursor(plan, [2]))
1138+
rows = list(plpy.cursor(plan, [2])) # or: = list(plan.cursor([2]))
11291139

11301140
return len(rows)
11311141
$$ LANGUAGE plpythonu;

src/pl/plpython/expected/plpython_spi.out

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ try:
2929
except Exception, ex:
3030
plpy.error(str(ex))
3131
return None
32+
'
33+
LANGUAGE plpythonu;
34+
CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
35+
AS
36+
'if "myplan" not in SD:
37+
q = "SELECT count(*) FROM users WHERE lname = $1"
38+
SD["myplan"] = plpy.prepare(q, [ "text" ])
39+
try:
40+
rv = SD["myplan"].execute([a])
41+
return "there are " + str(rv[0]["count"]) + " " + str(a) + "s"
42+
except Exception, ex:
43+
plpy.error(str(ex))
44+
return None
3245
'
3346
LANGUAGE plpythonu;
3447
CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
@@ -80,8 +93,8 @@ select spi_prepared_plan_test_one('doe');
8093
there are 3 does
8194
(1 row)
8295

83-
select spi_prepared_plan_test_one('smith');
84-
spi_prepared_plan_test_one
96+
select spi_prepared_plan_test_two('smith');
97+
spi_prepared_plan_test_two
8598
----------------------------
8699
there are 1 smiths
87100
(1 row)
@@ -372,7 +385,7 @@ plan = plpy.prepare(
372385
["text"])
373386
for row in plpy.cursor(plan, ["w"]):
374387
yield row['fname']
375-
for row in plpy.cursor(plan, ["j"]):
388+
for row in plan.cursor(["j"]):
376389
yield row['fname']
377390
$$ LANGUAGE plpythonu;
378391
CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$

src/pl/plpython/plpy_cursorobject.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626

2727
static PyObject *PLy_cursor_query(const char *query);
28-
static PyObject *PLy_cursor_plan(PyObject *ob, PyObject *args);
2928
static void PLy_cursor_dealloc(PyObject *arg);
3029
static PyObject *PLy_cursor_iternext(PyObject *self);
3130
static PyObject *PLy_cursor_fetch(PyObject *self, PyObject *args);
@@ -160,7 +159,7 @@ PLy_cursor_query(const char *query)
160159
return (PyObject *) cursor;
161160
}
162161

163-
static PyObject *
162+
PyObject *
164163
PLy_cursor_plan(PyObject *ob, PyObject *args)
165164
{
166165
PLyCursorObject *cursor;

src/pl/plpython/plpy_cursorobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ typedef struct PLyCursorObject
1919

2020
extern void PLy_cursor_init_type(void);
2121
extern PyObject *PLy_cursor(PyObject *self, PyObject *args);
22+
extern PyObject *PLy_cursor_plan(PyObject *ob, PyObject *args);
2223

2324
#endif /* PLPY_CURSOROBJECT_H */

src/pl/plpython/plpy_planobject.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,24 @@
1010

1111
#include "plpy_planobject.h"
1212

13+
#include "plpy_cursorobject.h"
1314
#include "plpy_elog.h"
15+
#include "plpy_spi.h"
1416
#include "utils/memutils.h"
1517

1618

1719
static void PLy_plan_dealloc(PyObject *arg);
20+
static PyObject *PLy_plan_cursor(PyObject *self, PyObject *args);
21+
static PyObject *PLy_plan_execute(PyObject *self, PyObject *args);
1822
static PyObject *PLy_plan_status(PyObject *self, PyObject *args);
1923

2024
static char PLy_plan_doc[] = {
2125
"Store a PostgreSQL plan"
2226
};
2327

2428
static PyMethodDef PLy_plan_methods[] = {
29+
{"cursor", PLy_plan_cursor, METH_VARARGS, NULL},
30+
{"execute", PLy_plan_execute, METH_VARARGS, NULL},
2531
{"status", PLy_plan_status, METH_VARARGS, NULL},
2632
{NULL, NULL, 0, NULL}
2733
};
@@ -111,6 +117,31 @@ PLy_plan_dealloc(PyObject *arg)
111117
}
112118

113119

120+
static PyObject *
121+
PLy_plan_cursor(PyObject *self, PyObject *args)
122+
{
123+
PyObject *planargs = NULL;
124+
125+
if (!PyArg_ParseTuple(args, "|O", &planargs))
126+
return NULL;
127+
128+
return PLy_cursor_plan(self, planargs);
129+
}
130+
131+
132+
static PyObject *
133+
PLy_plan_execute(PyObject *self, PyObject *args)
134+
{
135+
PyObject *list = NULL;
136+
long limit = 0;
137+
138+
if (!PyArg_ParseTuple(args, "|Ol", &list, &limit))
139+
return NULL;
140+
141+
return PLy_spi_execute_plan(self, list, limit);
142+
}
143+
144+
114145
static PyObject *
115146
PLy_plan_status(PyObject *self, PyObject *args)
116147
{

src/pl/plpython/plpy_spi.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131

3232
static PyObject *PLy_spi_execute_query(char *query, long limit);
33-
static PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
3433
static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable,
3534
uint64 rows, int status);
3635
static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
@@ -193,7 +192,7 @@ PLy_spi_execute(PyObject *self, PyObject *args)
193192
return NULL;
194193
}
195194

196-
static PyObject *
195+
PyObject *
197196
PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
198197
{
199198
volatile int nargs;

src/pl/plpython/plpy_spi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
extern PyObject *PLy_spi_prepare(PyObject *self, PyObject *args);
1212
extern PyObject *PLy_spi_execute(PyObject *self, PyObject *args);
13+
extern PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
1314

1415
typedef struct PLyExceptionEntry
1516
{

src/pl/plpython/sql/plpython_spi.sql

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ return None
3737
'
3838
LANGUAGE plpythonu;
3939

40+
CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
41+
AS
42+
'if "myplan" not in SD:
43+
q = "SELECT count(*) FROM users WHERE lname = $1"
44+
SD["myplan"] = plpy.prepare(q, [ "text" ])
45+
try:
46+
rv = SD["myplan"].execute([a])
47+
return "there are " + str(rv[0]["count"]) + " " + str(a) + "s"
48+
except Exception, ex:
49+
plpy.error(str(ex))
50+
return None
51+
'
52+
LANGUAGE plpythonu;
53+
4054
CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
4155
AS
4256
'if "myplan" not in SD:
@@ -79,7 +93,7 @@ return a + r
7993
--
8094
select nested_call_one('pass this along');
8195
select spi_prepared_plan_test_one('doe');
82-
select spi_prepared_plan_test_one('smith');
96+
select spi_prepared_plan_test_two('smith');
8397
select spi_prepared_plan_test_nested('smith');
8498

8599
SELECT join_sequences(sequences) FROM sequences;
@@ -275,7 +289,7 @@ plan = plpy.prepare(
275289
["text"])
276290
for row in plpy.cursor(plan, ["w"]):
277291
yield row['fname']
278-
for row in plpy.cursor(plan, ["j"]):
292+
for row in plan.cursor(["j"]):
279293
yield row['fname']
280294
$$ LANGUAGE plpythonu;
281295

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