1--
2-- Test data type behavior
3--
4
5--
6-- Base/common types
7--
8
9CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
10plpy.info(x, type(x))
11return x
12$$ LANGUAGE plpythonu;
13
14SELECT * FROM test_type_conversion_bool(true);
15SELECT * FROM test_type_conversion_bool(false);
16SELECT * FROM test_type_conversion_bool(null);
17
18
19-- test various other ways to express Booleans in Python
20CREATE FUNCTION test_type_conversion_bool_other(n int) RETURNS bool AS $$
21# numbers
22if n == 0:
23   ret = 0
24elif n == 1:
25   ret = 5
26# strings
27elif n == 2:
28   ret = ''
29elif n == 3:
30   ret = 'fa' # true in Python, false in PostgreSQL
31# containers
32elif n == 4:
33   ret = []
34elif n == 5:
35   ret = [0]
36plpy.info(ret, not not ret)
37return ret
38$$ LANGUAGE plpythonu;
39
40SELECT * FROM test_type_conversion_bool_other(0);
41SELECT * FROM test_type_conversion_bool_other(1);
42SELECT * FROM test_type_conversion_bool_other(2);
43SELECT * FROM test_type_conversion_bool_other(3);
44SELECT * FROM test_type_conversion_bool_other(4);
45SELECT * FROM test_type_conversion_bool_other(5);
46
47
48CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
49plpy.info(x, type(x))
50return x
51$$ LANGUAGE plpythonu;
52
53SELECT * FROM test_type_conversion_char('a');
54SELECT * FROM test_type_conversion_char(null);
55
56
57CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
58plpy.info(x, type(x))
59return x
60$$ LANGUAGE plpythonu;
61
62SELECT * FROM test_type_conversion_int2(100::int2);
63SELECT * FROM test_type_conversion_int2(-100::int2);
64SELECT * FROM test_type_conversion_int2(null);
65
66
67CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
68plpy.info(x, type(x))
69return x
70$$ LANGUAGE plpythonu;
71
72SELECT * FROM test_type_conversion_int4(100);
73SELECT * FROM test_type_conversion_int4(-100);
74SELECT * FROM test_type_conversion_int4(null);
75
76
77CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
78plpy.info(x, type(x))
79return x
80$$ LANGUAGE plpythonu;
81
82SELECT * FROM test_type_conversion_int8(100);
83SELECT * FROM test_type_conversion_int8(-100);
84SELECT * FROM test_type_conversion_int8(5000000000);
85SELECT * FROM test_type_conversion_int8(null);
86
87
88CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
89# print just the class name, not the type, to avoid differences
90# between decimal and cdecimal
91plpy.info(str(x), x.__class__.__name__)
92return x
93$$ LANGUAGE plpythonu;
94
95SELECT * FROM test_type_conversion_numeric(100);
96SELECT * FROM test_type_conversion_numeric(-100);
97SELECT * FROM test_type_conversion_numeric(100.0);
98SELECT * FROM test_type_conversion_numeric(100.00);
99SELECT * FROM test_type_conversion_numeric(5000000000.5);
100SELECT * FROM test_type_conversion_numeric(1234567890.0987654321);
101SELECT * FROM test_type_conversion_numeric(-1234567890.0987654321);
102SELECT * FROM test_type_conversion_numeric(null);
103
104
105CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
106plpy.info(x, type(x))
107return x
108$$ LANGUAGE plpythonu;
109
110SELECT * FROM test_type_conversion_float4(100);
111SELECT * FROM test_type_conversion_float4(-100);
112SELECT * FROM test_type_conversion_float4(5000.5);
113SELECT * FROM test_type_conversion_float4(null);
114
115
116CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
117plpy.info(x, type(x))
118return x
119$$ LANGUAGE plpythonu;
120
121SELECT * FROM test_type_conversion_float8(100);
122SELECT * FROM test_type_conversion_float8(-100);
123SELECT * FROM test_type_conversion_float8(5000000000.5);
124SELECT * FROM test_type_conversion_float8(null);
125SELECT * FROM test_type_conversion_float8(100100100.654321);
126
127
128CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
129plpy.info(x, type(x))
130return x
131$$ LANGUAGE plpythonu;
132
133SELECT * FROM test_type_conversion_oid(100);
134SELECT * FROM test_type_conversion_oid(2147483649);
135SELECT * FROM test_type_conversion_oid(null);
136
137
138CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
139plpy.info(x, type(x))
140return x
141$$ LANGUAGE plpythonu;
142
143SELECT * FROM test_type_conversion_text('hello world');
144SELECT * FROM test_type_conversion_text(null);
145
146
147CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
148plpy.info(x, type(x))
149return x
150$$ LANGUAGE plpythonu;
151
152SELECT * FROM test_type_conversion_bytea('hello world');
153SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
154SELECT * FROM test_type_conversion_bytea(null);
155
156
157CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
158import marshal
159return marshal.dumps('hello world')
160$$ LANGUAGE plpythonu;
161
162CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
163import marshal
164try:
165    return marshal.loads(x)
166except ValueError, e:
167    return 'FAILED: ' + str(e)
168$$ LANGUAGE plpythonu;
169
170SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
171
172
173--
174-- Domains
175--
176
177CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
178
179CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
180return y
181$$ LANGUAGE plpythonu;
182
183SELECT * FROM test_type_conversion_booltrue(true, true);
184SELECT * FROM test_type_conversion_booltrue(false, true);
185SELECT * FROM test_type_conversion_booltrue(true, false);
186
187
188CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
189
190CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
191plpy.info(x, type(x))
192return y
193$$ LANGUAGE plpythonu;
194
195SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
196SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
197SELECT * FROM test_type_conversion_uint2(null, 1);
198
199
200CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
201
202CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
203return y
204$$ LANGUAGE plpythonu;
205
206SELECT * FROM test_type_conversion_nnint(10, 20);
207SELECT * FROM test_type_conversion_nnint(null, 20);
208SELECT * FROM test_type_conversion_nnint(10, null);
209
210
211CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL);
212
213CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
214plpy.info(x, type(x))
215return y
216$$ LANGUAGE plpythonu;
217
218SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
219SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
220SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
221SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
222SELECT * FROM test_type_conversion_bytea10('hello word', null);
223
224
225--
226-- Arrays
227--
228
229CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
230plpy.info(x, type(x))
231return x
232$$ LANGUAGE plpythonu;
233
234SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
235SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
236SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
237SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
238SELECT * FROM test_type_conversion_array_int4(NULL);
239SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
240SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
241SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
242
243CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
244plpy.info(x, type(x))
245return x
246$$ LANGUAGE plpythonu;
247
248SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
249
250CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
251plpy.info(x, type(x))
252return x
253$$ LANGUAGE plpythonu;
254
255SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
256            [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
257
258CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
259plpy.info(x, type(x))
260return x
261$$ LANGUAGE plpythonu;
262
263SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
264            [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
265            [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
266            ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
267
268
269CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 ) RETURNS int4[] AS $BODY$
270m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
271plpy.info(m, type(m))
272return m
273$BODY$ LANGUAGE plpythonu;
274
275select pyreturnmultidemint4(8,5,3,2);
276
277CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 ) RETURNS int8[] AS $BODY$
278m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
279plpy.info(m, type(m))
280return m
281$BODY$ LANGUAGE plpythonu;
282
283select pyreturnmultidemint8(5,5,3,2);
284
285CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4 ) RETURNS float4[] AS $BODY$
286m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
287plpy.info(m, type(m))
288return m
289$BODY$ LANGUAGE plpythonu;
290
291select pyreturnmultidemfloat4(6,5,3,2);
292
293CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4 ) RETURNS float8[] AS $BODY$
294m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
295plpy.info(m, type(m))
296return m
297$BODY$ LANGUAGE plpythonu;
298
299select pyreturnmultidemfloat8(7,5,3,2);
300
301CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
302plpy.info(x, type(x))
303return x
304$$ LANGUAGE plpythonu;
305
306SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
307SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
308
309
310CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
311plpy.info(x, type(x))
312return x
313$$ LANGUAGE plpythonu;
314
315SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
316
317
318CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
319return [123, 'abc']
320$$ LANGUAGE plpythonu;
321
322SELECT * FROM test_type_conversion_array_mixed1();
323
324
325CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
326return [123, 'abc']
327$$ LANGUAGE plpythonu;
328
329SELECT * FROM test_type_conversion_array_mixed2();
330
331CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
332return [[1,2,3],[4,5]]
333$$ LANGUAGE plpythonu;
334
335SELECT * FROM test_type_conversion_mdarray_malformed();
336
337CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
338return [[[[[[[1]]]]]]]
339$$ LANGUAGE plpythonu;
340
341SELECT * FROM test_type_conversion_mdarray_toodeep();
342
343
344CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
345return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
346$$ LANGUAGE plpythonu;
347
348SELECT * FROM test_type_conversion_array_record();
349
350
351CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
352return 'abc'
353$$ LANGUAGE plpythonu;
354
355SELECT * FROM test_type_conversion_array_string();
356
357CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
358return ('abc', 'def')
359$$ LANGUAGE plpythonu;
360
361SELECT * FROM test_type_conversion_array_tuple();
362
363CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
364return 5
365$$ LANGUAGE plpythonu;
366
367SELECT * FROM test_type_conversion_array_error();
368
369
370--
371-- Domains over arrays
372--
373
374CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AND VALUE[1] < VALUE[2]);
375
376CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
377plpy.info(x, type(x))
378return x
379$$ LANGUAGE plpythonu;
380
381SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
382SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
383
384CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
385return [2,1]
386$$ LANGUAGE plpythonu;
387SELECT * FROM test_type_conversion_array_domain_check_violation();
388
389
390--
391-- Arrays of domains
392--
393
394CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
395plpy.info(x, type(x))
396return x[0]
397$$ LANGUAGE plpythonu;
398
399select test_read_uint2_array(array[1::uint2]);
400
401CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
402return [x, x]
403$$ LANGUAGE plpythonu;
404
405select test_build_uint2_array(1::int2);
406select test_build_uint2_array(-1::int2);  -- fail
407
408--
409-- ideally this would work, but for now it doesn't, because the return value
410-- is [[2,4], [2,4]] which our conversion code thinks should become a 2-D
411-- integer array, not an array of arrays.
412--
413CREATE FUNCTION test_type_conversion_domain_array(x integer[])
414  RETURNS ordered_pair_domain[] AS $$
415return [x, x]
416$$ LANGUAGE plpythonu;
417
418select test_type_conversion_domain_array(array[2,4]);
419select test_type_conversion_domain_array(array[4,2]);  -- fail
420
421CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
422  RETURNS integer AS $$
423plpy.info(x, type(x))
424return x[1]
425$$ LANGUAGE plpythonu;
426
427select test_type_conversion_domain_array2(array[2,4]);
428select test_type_conversion_domain_array2(array[4,2]);  -- fail
429
430CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
431  RETURNS ordered_pair_domain AS $$
432plpy.info(x, type(x))
433return x[0]
434$$ LANGUAGE plpythonu;
435
436select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
437
438
439---
440--- Composite types
441---
442
443CREATE TABLE employee (
444    name text,
445    basesalary integer,
446    bonus integer
447);
448
449INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
450
451CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
452return e['basesalary'] + e['bonus']
453$$ LANGUAGE plpythonu;
454
455SELECT name, test_composite_table_input(employee.*) FROM employee;
456
457ALTER TABLE employee DROP bonus;
458
459SELECT name, test_composite_table_input(employee.*) FROM employee;
460
461ALTER TABLE employee ADD bonus integer;
462UPDATE employee SET bonus = 10;
463
464SELECT name, test_composite_table_input(employee.*) FROM employee;
465
466CREATE TYPE named_pair AS (
467    i integer,
468    j integer
469);
470
471CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
472return sum(p.values())
473$$ LANGUAGE plpythonu;
474
475SELECT test_composite_type_input(row(1, 2));
476
477ALTER TYPE named_pair RENAME TO named_pair_2;
478
479SELECT test_composite_type_input(row(1, 2));
480
481
482--
483-- Domains within composite
484--
485
486CREATE TYPE nnint_container AS (f1 int, f2 nnint);
487
488CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
489return {'f1': x, 'f2': y}
490$$ LANGUAGE plpythonu;
491
492SELECT nnint_test(null, 3);
493SELECT nnint_test(3, null);  -- fail
494
495
496--
497-- Domains of composite
498--
499
500CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
501
502CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
503return p['i'] + p['j']
504$$ LANGUAGE plpythonu;
505
506SELECT read_ordered_named_pair(row(1, 2));
507SELECT read_ordered_named_pair(row(2, 1));  -- fail
508
509CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
510return {'i': i, 'j': j}
511$$ LANGUAGE plpythonu;
512
513SELECT build_ordered_named_pair(1,2);
514SELECT build_ordered_named_pair(2,1);  -- fail
515
516CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
517return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
518$$ LANGUAGE plpythonu;
519
520SELECT build_ordered_named_pairs(1,2);
521SELECT build_ordered_named_pairs(2,1);  -- fail
522
523
524--
525-- Prepared statements
526--
527
528CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
529LANGUAGE plpythonu
530AS $$
531plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
532rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
533return rv[0]['val']
534$$;
535
536SELECT test_prep_bool_input(); -- 1
537
538
539CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
540LANGUAGE plpythonu
541AS $$
542plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
543rv = plpy.execute(plan, [0], 5)
544plpy.info(rv[0])
545return rv[0]['val']
546$$;
547
548SELECT test_prep_bool_output(); -- false
549
550
551CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
552LANGUAGE plpythonu
553AS $$
554plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
555rv = plpy.execute(plan, [bb], 5)
556return rv[0]['val']
557$$;
558
559SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
560
561
562CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
563LANGUAGE plpythonu
564AS $$
565plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
566rv = plpy.execute(plan, [], 5)
567plpy.info(rv[0])
568return rv[0]['val']
569$$;
570
571SELECT test_prep_bytea_output();
572