Fix PL/Python for recursion and interleaved set-returning functions.
[pgsql.git] / src / pl / plpython / sql / plpython_spi.sql
blob87170609da2114e44fde426241a694e08a62d485
1 --
2 -- nested calls
3 --
5 CREATE FUNCTION nested_call_one(a text) RETURNS text
6         AS
7 'q = "SELECT nested_call_two(''%s'')" % a
8 r = plpy.execute(q)
9 return r[0]'
10         LANGUAGE plpythonu ;
12 CREATE FUNCTION nested_call_two(a text) RETURNS text
13         AS
14 'q = "SELECT nested_call_three(''%s'')" % a
15 r = plpy.execute(q)
16 return r[0]'
17         LANGUAGE plpythonu ;
19 CREATE FUNCTION nested_call_three(a text) RETURNS text
20         AS
21 'return a'
22         LANGUAGE plpythonu ;
24 -- some spi stuff
26 CREATE FUNCTION spi_prepared_plan_test_one(a text) RETURNS text
27         AS
28 'if "myplan" not in SD:
29         q = "SELECT count(*) FROM users WHERE lname = $1"
30         SD["myplan"] = plpy.prepare(q, [ "text" ])
31 try:
32         rv = plpy.execute(SD["myplan"], [a])
33         return "there are " + str(rv[0]["count"]) + " " + str(a) + "s"
34 except Exception, ex:
35         plpy.error(str(ex))
36 return None
38         LANGUAGE plpythonu;
40 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
41         AS
42 'if "myplan" not in SD:
43         q = "SELECT spi_prepared_plan_test_one(''%s'') as count" % a
44         SD["myplan"] = plpy.prepare(q)
45 try:
46         rv = plpy.execute(SD["myplan"])
47         if len(rv):
48                 return rv[0]["count"]
49 except Exception, ex:
50         plpy.error(str(ex))
51 return None
53         LANGUAGE plpythonu;
55 CREATE FUNCTION join_sequences(s sequences) RETURNS text
56         AS
57 'if not s["multipart"]:
58         return s["sequence"]
59 q = "SELECT sequence FROM xsequences WHERE pid = ''%s''" % s["pid"]
60 rv = plpy.execute(q)
61 seq = s["sequence"]
62 for r in rv:
63         seq = seq + r["sequence"]
64 return seq
66         LANGUAGE plpythonu;
68 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
69         AS
70 'r = 0
71 if a > 1:
72     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
73 return a + r
75         LANGUAGE plpythonu;
78 -- spi and nested calls
80 select nested_call_one('pass this along');
81 select spi_prepared_plan_test_one('doe');
82 select spi_prepared_plan_test_one('smith');
83 select spi_prepared_plan_test_nested('smith');
85 SELECT join_sequences(sequences) FROM sequences;
86 SELECT join_sequences(sequences) FROM sequences
87         WHERE join_sequences(sequences) ~* '^A';
88 SELECT join_sequences(sequences) FROM sequences
89         WHERE join_sequences(sequences) ~* '^B';
91 SELECT spi_recursive_sum(10);
94 -- plan and result objects
97 CREATE FUNCTION result_metadata_test(cmd text) RETURNS int
98 AS $$
99 plan = plpy.prepare(cmd)
100 plpy.info(plan.status()) # not really documented or useful
101 result = plpy.execute(plan)
102 if result.status() > 0:
103    plpy.info(result.colnames())
104    plpy.info(result.coltypes())
105    plpy.info(result.coltypmods())
106    return result.nrows()
107 else:
108    return None
109 $$ LANGUAGE plpythonu;
111 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
112 SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
114 CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
115 AS $$
116 result = plpy.execute(cmd)
117 return result.nrows()
118 $$ LANGUAGE plpythonu;
120 SELECT result_nrows_test($$SELECT 1$$);
121 SELECT result_nrows_test($$CREATE TEMPORARY TABLE foo2 (a int, b text)$$);
122 SELECT result_nrows_test($$INSERT INTO foo2 VALUES (1, 'one'), (2, 'two')$$);
123 SELECT result_nrows_test($$UPDATE foo2 SET b = '' WHERE a = 2$$);
125 CREATE FUNCTION result_len_test(cmd text) RETURNS int
126 AS $$
127 result = plpy.execute(cmd)
128 return len(result)
129 $$ LANGUAGE plpythonu;
131 SELECT result_len_test($$SELECT 1$$);
132 SELECT result_len_test($$CREATE TEMPORARY TABLE foo3 (a int, b text)$$);
133 SELECT result_len_test($$INSERT INTO foo3 VALUES (1, 'one'), (2, 'two')$$);
134 SELECT result_len_test($$UPDATE foo3 SET b= '' WHERE a = 2$$);
136 CREATE FUNCTION result_subscript_test() RETURNS void
137 AS $$
138 result = plpy.execute("SELECT 1 AS c UNION SELECT 2 "
139                       "UNION SELECT 3 UNION SELECT 4")
141 plpy.info(result[1]['c'])
142 plpy.info(result[-1]['c'])
144 plpy.info([item['c'] for item in result[1:3]])
145 plpy.info([item['c'] for item in result[::2]])
147 result[-1] = {'c': 1000}
148 result[:2] = [{'c': 10}, {'c': 100}]
149 plpy.info([item['c'] for item in result[:]])
151 # raises TypeError, but the message differs on Python 2.6, so silence it
152 try:
153     plpy.info(result['foo'])
154 except TypeError:
155     pass
156 else:
157     assert False, "TypeError not raised"
159 $$ LANGUAGE plpythonu;
161 SELECT result_subscript_test();
163 CREATE FUNCTION result_empty_test() RETURNS void
164 AS $$
165 result = plpy.execute("select 1 where false")
167 plpy.info(result[:])
169 $$ LANGUAGE plpythonu;
171 SELECT result_empty_test();
173 CREATE FUNCTION result_str_test(cmd text) RETURNS text
174 AS $$
175 plan = plpy.prepare(cmd)
176 result = plpy.execute(plan)
177 return str(result)
178 $$ LANGUAGE plpythonu;
180 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
181 SELECT result_str_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
183 -- cursor objects
185 CREATE FUNCTION simple_cursor_test() RETURNS int AS $$
186 res = plpy.cursor("select fname, lname from users")
187 does = 0
188 for row in res:
189     if row['lname'] == 'doe':
190         does += 1
191 return does
192 $$ LANGUAGE plpythonu;
194 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
195 res = plpy.cursor("select fname, lname from users")
196 res.close()
197 res.close()
198 $$ LANGUAGE plpythonu;
200 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
201 res = plpy.cursor("select fname, lname from users")
202 assert len(res.fetch(3)) == 3
203 assert len(res.fetch(3)) == 1
204 assert len(res.fetch(3)) == 0
205 assert len(res.fetch(3)) == 0
206 try:
207     # use next() or __next__(), the method name changed in
208     # http://www.python.org/dev/peps/pep-3114/
209     try:
210         res.next()
211     except AttributeError:
212         res.__next__()
213 except StopIteration:
214     pass
215 else:
216     assert False, "StopIteration not raised"
217 $$ LANGUAGE plpythonu;
219 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
220 res = plpy.cursor("select fname, lname from users order by fname")
221 assert len(res.fetch(2)) == 2
223 item = None
224 try:
225     item = res.next()
226 except AttributeError:
227     item = res.__next__()
228 assert item['fname'] == 'rick'
230 assert len(res.fetch(2)) == 1
231 $$ LANGUAGE plpythonu;
233 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
234 res = plpy.cursor("select fname, lname from users")
235 res.close()
236 try:
237     res.fetch(1)
238 except ValueError:
239     pass
240 else:
241     assert False, "ValueError not raised"
242 $$ LANGUAGE plpythonu;
244 CREATE FUNCTION next_after_close() RETURNS int AS $$
245 res = plpy.cursor("select fname, lname from users")
246 res.close()
247 try:
248     try:
249         res.next()
250     except AttributeError:
251         res.__next__()
252 except ValueError:
253     pass
254 else:
255     assert False, "ValueError not raised"
256 $$ LANGUAGE plpythonu;
258 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
259 res = plpy.cursor("select fname, lname from users where false")
260 assert len(res.fetch(1)) == 0
261 try:
262     try:
263         res.next()
264     except AttributeError:
265         res.__next__()
266 except StopIteration:
267     pass
268 else:
269     assert False, "StopIteration not raised"
270 $$ LANGUAGE plpythonu;
272 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
273 plan = plpy.prepare(
274     "select fname, lname from users where fname like $1 || '%' order by fname",
275     ["text"])
276 for row in plpy.cursor(plan, ["w"]):
277     yield row['fname']
278 for row in plpy.cursor(plan, ["j"]):
279     yield row['fname']
280 $$ LANGUAGE plpythonu;
282 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
283 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
284                     ["text"])
285 c = plpy.cursor(plan, ["a", "b"])
286 $$ LANGUAGE plpythonu;
288 CREATE TYPE test_composite_type AS (
289   a1 int,
290   a2 varchar
293 CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS $$
294 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
295 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
296 return res[0]["c1"]
297 $$ LANGUAGE plpythonu;
299 SELECT simple_cursor_test();
300 SELECT double_cursor_close();
301 SELECT cursor_fetch();
302 SELECT cursor_mix_next_and_fetch();
303 SELECT fetch_after_close();
304 SELECT next_after_close();
305 SELECT cursor_fetch_next_empty();
306 SELECT cursor_plan();
307 SELECT cursor_plan_wrong_args();
308 SELECT plan_composite_args();