Fix recursive RECORD-returning plpython functions.
[pgsql.git] / src / pl / plpython / expected / plpython_composite.out
blob674af93ddcfe07209865443d112f43f5b11f3d65
1 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
2 return (1, 2)
3 $$ LANGUAGE plpython3u;
4 SELECT multiout_simple();
5  multiout_simple 
6 -----------------
7  (1,2)
8 (1 row)
10 SELECT * FROM multiout_simple();
11  i | j 
12 ---+---
13  1 | 2
14 (1 row)
16 SELECT i, j + 2 FROM multiout_simple();
17  i | ?column? 
18 ---+----------
19  1 |        4
20 (1 row)
22 SELECT (multiout_simple()).j + 3;
23  ?column? 
24 ----------
25         5
26 (1 row)
28 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
29 return [(1, 2)] * n
30 $$ LANGUAGE plpython3u;
31 SELECT multiout_simple_setof();
32  multiout_simple_setof 
33 -----------------------
34  (1,2)
35 (1 row)
37 SELECT * FROM multiout_simple_setof();
38  column1 | column2 
39 ---------+---------
40        1 |       2
41 (1 row)
43 SELECT * FROM multiout_simple_setof(3);
44  column1 | column2 
45 ---------+---------
46        1 |       2
47        1 |       2
48        1 |       2
49 (3 rows)
51 CREATE FUNCTION multiout_record_as(typ text,
52                                    first text, OUT first text,
53                                    second integer, OUT second integer,
54                                    retnull boolean) RETURNS record AS $$
55 if retnull:
56     return None
57 if typ == 'dict':
58     return { 'first': first, 'second': second, 'additionalfield': 'must not cause trouble' }
59 elif typ == 'tuple':
60     return ( first, second )
61 elif typ == 'list':
62     return [ first, second ]
63 elif typ == 'obj':
64     class type_record: pass
65     type_record.first = first
66     type_record.second = second
67     return type_record
68 elif typ == 'str':
69     return "('%s',%r)" % (first, second)
70 $$ LANGUAGE plpython3u;
71 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
72  first | second 
73 -------+--------
74  foo   |      1
75 (1 row)
77 SELECT multiout_record_as('dict', 'foo', 1, 'f');
78  multiout_record_as 
79 --------------------
80  (foo,1)
81 (1 row)
83 SELECT * FROM multiout_record_as('dict', null, null, false);
84  first | second 
85 -------+--------
86        |       
87 (1 row)
89 SELECT * FROM multiout_record_as('dict', 'one', null, false);
90  first | second 
91 -------+--------
92  one   |       
93 (1 row)
95 SELECT * FROM multiout_record_as('dict', null, 2, false);
96  first | second 
97 -------+--------
98        |      2
99 (1 row)
101 SELECT * FROM multiout_record_as('dict', 'three', 3, false);
102  first | second 
103 -------+--------
104  three |      3
105 (1 row)
107 SELECT * FROM multiout_record_as('dict', null, null, true);
108  first | second 
109 -------+--------
110        |       
111 (1 row)
113 SELECT * FROM multiout_record_as('tuple', null, null, false);
114  first | second 
115 -------+--------
116        |       
117 (1 row)
119 SELECT * FROM multiout_record_as('tuple', 'one', null, false);
120  first | second 
121 -------+--------
122  one   |       
123 (1 row)
125 SELECT * FROM multiout_record_as('tuple', null, 2, false);
126  first | second 
127 -------+--------
128        |      2
129 (1 row)
131 SELECT * FROM multiout_record_as('tuple', 'three', 3, false);
132  first | second 
133 -------+--------
134  three |      3
135 (1 row)
137 SELECT * FROM multiout_record_as('tuple', null, null, true);
138  first | second 
139 -------+--------
140        |       
141 (1 row)
143 SELECT * FROM multiout_record_as('list', null, null, false);
144  first | second 
145 -------+--------
146        |       
147 (1 row)
149 SELECT * FROM multiout_record_as('list', 'one', null, false);
150  first | second 
151 -------+--------
152  one   |       
153 (1 row)
155 SELECT * FROM multiout_record_as('list', null, 2, false);
156  first | second 
157 -------+--------
158        |      2
159 (1 row)
161 SELECT * FROM multiout_record_as('list', 'three', 3, false);
162  first | second 
163 -------+--------
164  three |      3
165 (1 row)
167 SELECT * FROM multiout_record_as('list', null, null, true);
168  first | second 
169 -------+--------
170        |       
171 (1 row)
173 SELECT * FROM multiout_record_as('obj', null, null, false);
174  first | second 
175 -------+--------
176        |       
177 (1 row)
179 SELECT * FROM multiout_record_as('obj', 'one', null, false);
180  first | second 
181 -------+--------
182  one   |       
183 (1 row)
185 SELECT * FROM multiout_record_as('obj', null, 2, false);
186  first | second 
187 -------+--------
188        |      2
189 (1 row)
191 SELECT * FROM multiout_record_as('obj', 'three', 3, false);
192  first | second 
193 -------+--------
194  three |      3
195 (1 row)
197 SELECT * FROM multiout_record_as('obj', null, null, true);
198  first | second 
199 -------+--------
200        |       
201 (1 row)
203 SELECT * FROM multiout_record_as('str', 'one', 1, false);
204  first | second 
205 -------+--------
206  'one' |      1
207 (1 row)
209 SELECT * FROM multiout_record_as('str', 'one', 2, false);
210  first | second 
211 -------+--------
212  'one' |      2
213 (1 row)
215 SELECT *, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', NULL, 'f') AS f(f, s);
216   f  | s | snull 
217 -----+---+-------
218  xxx |   | t
219 (1 row)
221 SELECT *, f IS NULL AS fnull, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', 1, 't') AS f(f, s);
222  f | s | fnull | snull 
223 ---+---+-------+-------
224    |   | t     | t
225 (1 row)
227 SELECT * FROM multiout_record_as('obj', NULL, 10, 'f');
228  first | second 
229 -------+--------
230        |     10
231 (1 row)
233 CREATE FUNCTION multiout_setof(n integer,
234                                OUT power_of_2 integer,
235                                OUT length integer) RETURNS SETOF record AS $$
236 for i in range(n):
237     power = 2 ** i
238     length = plpy.execute("select length('%d')" % power)[0]['length']
239     yield power, length
240 $$ LANGUAGE plpython3u;
241 SELECT * FROM multiout_setof(3);
242  power_of_2 | length 
243 ------------+--------
244           1 |      1
245           2 |      1
246           4 |      1
247 (3 rows)
249 SELECT multiout_setof(5);
250  multiout_setof 
251 ----------------
252  (1,1)
253  (2,1)
254  (4,1)
255  (8,1)
256  (16,2)
257 (5 rows)
259 CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
260 return [{'x': 4, 'y' :'four'},
261         {'x': 7, 'y' :'seven'},
262         {'x': 0, 'y' :'zero'}]
263 $$ LANGUAGE plpython3u;
264 SELECT * FROM multiout_return_table();
265  x |   y   
266 ---+-------
267  4 | four
268  7 | seven
269  0 | zero
270 (3 rows)
272 CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS $$
273 yield [[1], 'a']
274 yield [[1,2], 'b']
275 yield [[1,2,3], None]
276 $$ LANGUAGE plpython3u;
277 SELECT * FROM multiout_array();
278  column1 | column2 
279 ---------+---------
280  {1}     | a
281  {1,2}   | b
282  {1,2,3} | 
283 (3 rows)
285 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
286 return {'first': 1, 'second': 2}
287 $$ LANGUAGE plpython3u;
288 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
289 return [{'first': 1, 'second': 2},
290        {'first': 3, 'second': 4 }]
291 $$ LANGUAGE plpython3u;
292 SELECT * FROM singleout_composite();
293  first | second 
294 -------+--------
295  1     |      2
296 (1 row)
298 SELECT * FROM multiout_composite();
299  first | second 
300 -------+--------
301  1     |      2
302  3     |      4
303 (2 rows)
305 -- composite OUT parameters in functions returning RECORD not supported yet
306 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
307 return (n, (n * 2, n * 3))
308 $$ LANGUAGE plpython3u;
309 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
310 if returnnull:
311     d = None
312 elif typ == 'dict':
313     d = {'first': n * 2, 'second': n * 3, 'extra': 'not important'}
314 elif typ == 'tuple':
315     d = (n * 2, n * 3)
316 elif typ == 'list':
317     d = [ n * 2, n * 3 ]
318 elif typ == 'obj':
319     class d: pass
320     d.first = n * 2
321     d.second = n * 3
322 elif typ == 'str':
323     d = "(%r,%r)" % (n * 2, n * 3)
324 for i in range(n):
325     yield (i, d)
326 $$ LANGUAGE plpython3u;
327 SELECT * FROM multiout_composite(2);
328  n | column2 
329 ---+---------
330  2 | (4,6)
331 (1 row)
333 SELECT * FROM multiout_table_type_setof('dict', 'f', 3);
334  n | column2 
335 ---+---------
336  0 | (6,9)
337  1 | (6,9)
338  2 | (6,9)
339 (3 rows)
341 SELECT * FROM multiout_table_type_setof('dict', 'f', 7);
342  n | column2 
343 ---+---------
344  0 | (14,21)
345  1 | (14,21)
346  2 | (14,21)
347  3 | (14,21)
348  4 | (14,21)
349  5 | (14,21)
350  6 | (14,21)
351 (7 rows)
353 SELECT * FROM multiout_table_type_setof('tuple', 'f', 2);
354  n | column2 
355 ---+---------
356  0 | (4,6)
357  1 | (4,6)
358 (2 rows)
360 SELECT * FROM multiout_table_type_setof('tuple', 'f', 3);
361  n | column2 
362 ---+---------
363  0 | (6,9)
364  1 | (6,9)
365  2 | (6,9)
366 (3 rows)
368 SELECT * FROM multiout_table_type_setof('list', 'f', 2);
369  n | column2 
370 ---+---------
371  0 | (4,6)
372  1 | (4,6)
373 (2 rows)
375 SELECT * FROM multiout_table_type_setof('list', 'f', 3);
376  n | column2 
377 ---+---------
378  0 | (6,9)
379  1 | (6,9)
380  2 | (6,9)
381 (3 rows)
383 SELECT * FROM multiout_table_type_setof('obj', 'f', 4);
384  n | column2 
385 ---+---------
386  0 | (8,12)
387  1 | (8,12)
388  2 | (8,12)
389  3 | (8,12)
390 (4 rows)
392 SELECT * FROM multiout_table_type_setof('obj', 'f', 5);
393  n | column2 
394 ---+---------
395  0 | (10,15)
396  1 | (10,15)
397  2 | (10,15)
398  3 | (10,15)
399  4 | (10,15)
400 (5 rows)
402 SELECT * FROM multiout_table_type_setof('str', 'f', 6);
403  n | column2 
404 ---+---------
405  0 | (12,18)
406  1 | (12,18)
407  2 | (12,18)
408  3 | (12,18)
409  4 | (12,18)
410  5 | (12,18)
411 (6 rows)
413 SELECT * FROM multiout_table_type_setof('str', 'f', 7);
414  n | column2 
415 ---+---------
416  0 | (14,21)
417  1 | (14,21)
418  2 | (14,21)
419  3 | (14,21)
420  4 | (14,21)
421  5 | (14,21)
422  6 | (14,21)
423 (7 rows)
425 SELECT * FROM multiout_table_type_setof('dict', 't', 3);
426  n | column2 
427 ---+---------
428  0 | 
429  1 | 
430  2 | 
431 (3 rows)
433 -- check what happens if a type changes under us
434 CREATE TABLE changing (
435     i integer,
436     j integer
438 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
439 return [(1, {'i': 1, 'j': 2}),
440         (1, (3, 4))]
441 $$ LANGUAGE plpython3u;
442 SELECT * FROM changing_test();
443  n | column2 
444 ---+---------
445  1 | (1,2)
446  1 | (3,4)
447 (2 rows)
449 ALTER TABLE changing DROP COLUMN j;
450 SELECT * FROM changing_test();
451 ERROR:  length of returned sequence did not match number of columns in row
452 CONTEXT:  while creating return value
453 PL/Python function "changing_test"
454 SELECT * FROM changing_test();
455 ERROR:  length of returned sequence did not match number of columns in row
456 CONTEXT:  while creating return value
457 PL/Python function "changing_test"
458 ALTER TABLE changing ADD COLUMN j integer;
459 SELECT * FROM changing_test();
460  n | column2 
461 ---+---------
462  1 | (1,2)
463  1 | (3,4)
464 (2 rows)
466 -- tables of composite types
467 CREATE FUNCTION composite_types_table(OUT tab table_record[], OUT typ type_record[] ) RETURNS SETOF record AS $$
468 yield {'tab': [('first', 1), ('second', 2)],
469       'typ': [{'first': 'third', 'second': 3},
470               {'first': 'fourth', 'second': 4}]}
471 yield {'tab': [('first', 1), ('second', 2)],
472       'typ': [{'first': 'third', 'second': 3},
473               {'first': 'fourth', 'second': 4}]}
474 yield {'tab': [('first', 1), ('second', 2)],
475       'typ': [{'first': 'third', 'second': 3},
476               {'first': 'fourth', 'second': 4}]}
477 $$ LANGUAGE plpython3u;
478 SELECT * FROM composite_types_table();
479             tab             |            typ             
480 ----------------------------+----------------------------
481  {"(first,1)","(second,2)"} | {"(third,3)","(fourth,4)"}
482  {"(first,1)","(second,2)"} | {"(third,3)","(fourth,4)"}
483  {"(first,1)","(second,2)"} | {"(third,3)","(fourth,4)"}
484 (3 rows)
486 -- check what happens if the output record descriptor changes
487 CREATE FUNCTION return_record(t text) RETURNS record AS $$
488 return {'t': t, 'val': 10}
489 $$ LANGUAGE plpython3u;
490 SELECT * FROM return_record('abc') AS r(t text, val integer);
491   t  | val 
492 -----+-----
493  abc |  10
494 (1 row)
496 SELECT * FROM return_record('abc') AS r(t text, val bigint);
497   t  | val 
498 -----+-----
499  abc |  10
500 (1 row)
502 SELECT * FROM return_record('abc') AS r(t text, val integer);
503   t  | val 
504 -----+-----
505  abc |  10
506 (1 row)
508 SELECT * FROM return_record('abc') AS r(t varchar(30), val integer);
509   t  | val 
510 -----+-----
511  abc |  10
512 (1 row)
514 SELECT * FROM return_record('abc') AS r(t varchar(100), val integer);
515   t  | val 
516 -----+-----
517  abc |  10
518 (1 row)
520 SELECT * FROM return_record('999') AS r(val text, t integer);
521  val |  t  
522 -----+-----
523  10  | 999
524 (1 row)
526 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
527 return {'v1':1,'v2':2,t:3}
528 $$ LANGUAGE plpython3u;
529 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
530  v3 | v2 | v1 
531 ----+----+----
532   3 |  2 |  1
533 (1 row)
535 SELECT * FROM return_record_2('v3') AS (v2 int, v3 int, v1 int);
536  v2 | v3 | v1 
537 ----+----+----
538   2 |  3 |  1
539 (1 row)
541 SELECT * FROM return_record_2('v4') AS (v1 int, v4 int, v2 int);
542  v1 | v4 | v2 
543 ----+----+----
544   1 |  3 |  2
545 (1 row)
547 SELECT * FROM return_record_2('v4') AS (v1 int, v4 int, v2 int);
548  v1 | v4 | v2 
549 ----+----+----
550   1 |  3 |  2
551 (1 row)
553 -- error
554 SELECT * FROM return_record_2('v4') AS (v1 int, v3 int, v2 int);
555 ERROR:  key "v3" not found in mapping
556 HINT:  To return null in a column, add the value None to the mapping with the key named after the column.
557 CONTEXT:  while creating return value
558 PL/Python function "return_record_2"
559 -- works
560 SELECT * FROM return_record_2('v3') AS (v1 int, v3 int, v2 int);
561  v1 | v3 | v2 
562 ----+----+----
563   1 |  3 |  2
564 (1 row)
566 SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
567  v1 | v2 | v3 
568 ----+----+----
569   1 |  2 |  3
570 (1 row)
572 -- recursion with a different inner result type didn't use to work
573 CREATE FUNCTION return_record_3(t text) RETURNS record AS $$
574 if t == "text":
575      plpy.execute("SELECT * FROM return_record_3('int') AS (a int)");
576      return { "a": "x" }
577 elif t == "int":
578      return { "a": 1 }
579 $$ LANGUAGE plpython3u;
580 SELECT * FROM return_record_3('text') AS (a text);
581  a 
584 (1 row)
586 -- multi-dimensional array of composite types.
587 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
588   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
589 $$ LANGUAGE plpython3u;
590 SELECT * FROM composite_type_as_list();
591                                composite_type_as_list                               
592 ------------------------------------------------------------------------------------
593  {{"(first,1)","(second,1)"},{"(first,2)","(second,2)"},{"(first,3)","(second,3)"}}
594 (1 row)
596 -- Starting with PostgreSQL 10, a composite type in an array cannot be
597 -- represented as a Python list, because it's ambiguous with multi-dimensional
598 -- arrays. So this throws an error now. The error should contain a useful hint
599 -- on the issue.
600 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
601   return [['first', 1]];
602 $$ LANGUAGE plpython3u;
603 SELECT * FROM composite_type_as_list_broken();
604 ERROR:  malformed record literal: "first"
605 DETAIL:  Missing left parenthesis.
606 HINT:  To return a composite type in an array, return the composite type as a Python tuple, e.g., "[('foo',)]".
607 CONTEXT:  while creating return value
608 PL/Python function "composite_type_as_list_broken"