Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 12c2f2f

Browse files
committed
Use data-type specific conversion functions also in plpy.execute
In PLy_spi_execute_plan, use the data-type specific Python-to-PostgreSQL conversion function instead of passing everything through InputFunctionCall as a string. The equivalent fix was already done months ago for function parameters and return values, but this other gateway between Python and PostgreSQL was apparently forgotten. As a result, data types that need special treatment, such as bytea, would misbehave when used with plpy.execute.
1 parent c21ac0b commit 12c2f2f

File tree

4 files changed

+206
-69
lines changed

4 files changed

+206
-69
lines changed

src/pl/plpython/expected/plpython_types.out

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,3 +587,64 @@ SELECT * FROM test_type_conversion_array_error();
587587
ERROR: PL/Python: return value of function with array return type is not a Python sequence
588588
CONTEXT: while creating return value
589589
PL/Python function "test_type_conversion_array_error"
590+
--
591+
-- Prepared statements
592+
--
593+
CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
594+
LANGUAGE plpythonu
595+
AS $$
596+
plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
597+
rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
598+
return rv[0]['val']
599+
$$;
600+
SELECT test_prep_bool_input(); -- 1
601+
test_prep_bool_input
602+
----------------------
603+
1
604+
(1 row)
605+
606+
CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
607+
LANGUAGE plpythonu
608+
AS $$
609+
plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
610+
rv = plpy.execute(plan, [0], 5)
611+
plpy.info(rv[0])
612+
return rv[0]['val']
613+
$$;
614+
SELECT test_prep_bool_output(); -- false
615+
INFO: {'val': False}
616+
CONTEXT: PL/Python function "test_prep_bool_output"
617+
test_prep_bool_output
618+
-----------------------
619+
f
620+
(1 row)
621+
622+
CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
623+
LANGUAGE plpythonu
624+
AS $$
625+
plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
626+
rv = plpy.execute(plan, [bb], 5)
627+
return rv[0]['val']
628+
$$;
629+
SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
630+
test_prep_bytea_input
631+
-----------------------
632+
3
633+
(1 row)
634+
635+
CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
636+
LANGUAGE plpythonu
637+
AS $$
638+
plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
639+
rv = plpy.execute(plan, [], 5)
640+
plpy.info(rv[0])
641+
return rv[0]['val']
642+
$$;
643+
SELECT test_prep_bytea_output();
644+
INFO: {'val': '\xaa\x00\xbb'}
645+
CONTEXT: PL/Python function "test_prep_bytea_output"
646+
test_prep_bytea_output
647+
------------------------
648+
\xaa00bb
649+
(1 row)
650+

src/pl/plpython/expected/plpython_types_3.out

Lines changed: 90 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
88
plpy.info(x, type(x))
99
return x
10-
$$ LANGUAGE plpythonu;
10+
$$ LANGUAGE plpython3u;
1111
SELECT * FROM test_type_conversion_bool(true);
1212
INFO: (True, <class 'bool'>)
1313
CONTEXT: PL/Python function "test_type_conversion_bool"
@@ -51,7 +51,7 @@ elif n == 5:
5151
ret = [0]
5252
plpy.info(ret, not not ret)
5353
return ret
54-
$$ LANGUAGE plpythonu;
54+
$$ LANGUAGE plpython3u;
5555
SELECT * FROM test_type_conversion_bool_other(0);
5656
INFO: (0, False)
5757
CONTEXT: PL/Python function "test_type_conversion_bool_other"
@@ -103,7 +103,7 @@ CONTEXT: PL/Python function "test_type_conversion_bool_other"
103103
CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
104104
plpy.info(x, type(x))
105105
return x
106-
$$ LANGUAGE plpythonu;
106+
$$ LANGUAGE plpython3u;
107107
SELECT * FROM test_type_conversion_char('a');
108108
INFO: ('a', <class 'str'>)
109109
CONTEXT: PL/Python function "test_type_conversion_char"
@@ -123,7 +123,7 @@ CONTEXT: PL/Python function "test_type_conversion_char"
123123
CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
124124
plpy.info(x, type(x))
125125
return x
126-
$$ LANGUAGE plpythonu;
126+
$$ LANGUAGE plpython3u;
127127
SELECT * FROM test_type_conversion_int2(100::int2);
128128
INFO: (100, <class 'int'>)
129129
CONTEXT: PL/Python function "test_type_conversion_int2"
@@ -151,7 +151,7 @@ CONTEXT: PL/Python function "test_type_conversion_int2"
151151
CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
152152
plpy.info(x, type(x))
153153
return x
154-
$$ LANGUAGE plpythonu;
154+
$$ LANGUAGE plpython3u;
155155
SELECT * FROM test_type_conversion_int4(100);
156156
INFO: (100, <class 'int'>)
157157
CONTEXT: PL/Python function "test_type_conversion_int4"
@@ -179,25 +179,25 @@ CONTEXT: PL/Python function "test_type_conversion_int4"
179179
CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
180180
plpy.info(x, type(x))
181181
return x
182-
$$ LANGUAGE plpythonu;
182+
$$ LANGUAGE plpython3u;
183183
SELECT * FROM test_type_conversion_int8(100);
184-
INFO: (100L, <type 'long'>)
184+
INFO: (100, <class 'int'>)
185185
CONTEXT: PL/Python function "test_type_conversion_int8"
186186
test_type_conversion_int8
187187
---------------------------
188188
100
189189
(1 row)
190190

191191
SELECT * FROM test_type_conversion_int8(-100);
192-
INFO: (-100L, <type 'long'>)
192+
INFO: (-100, <class 'int'>)
193193
CONTEXT: PL/Python function "test_type_conversion_int8"
194194
test_type_conversion_int8
195195
---------------------------
196196
-100
197197
(1 row)
198198

199199
SELECT * FROM test_type_conversion_int8(5000000000);
200-
INFO: (5000000000L, <type 'long'>)
200+
INFO: (5000000000, <class 'int'>)
201201
CONTEXT: PL/Python function "test_type_conversion_int8"
202202
test_type_conversion_int8
203203
---------------------------
@@ -215,7 +215,7 @@ CONTEXT: PL/Python function "test_type_conversion_int8"
215215
CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
216216
plpy.info(x, type(x))
217217
return x
218-
$$ LANGUAGE plpythonu;
218+
$$ LANGUAGE plpython3u;
219219
/* The current implementation converts numeric to float. */
220220
SELECT * FROM test_type_conversion_numeric(100);
221221
INFO: (100.0, <class 'float'>)
@@ -252,7 +252,7 @@ CONTEXT: PL/Python function "test_type_conversion_numeric"
252252
CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
253253
plpy.info(x, type(x))
254254
return x
255-
$$ LANGUAGE plpythonu;
255+
$$ LANGUAGE plpython3u;
256256
SELECT * FROM test_type_conversion_float4(100);
257257
INFO: (100.0, <class 'float'>)
258258
CONTEXT: PL/Python function "test_type_conversion_float4"
@@ -288,7 +288,7 @@ CONTEXT: PL/Python function "test_type_conversion_float4"
288288
CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
289289
plpy.info(x, type(x))
290290
return x
291-
$$ LANGUAGE plpythonu;
291+
$$ LANGUAGE plpython3u;
292292
SELECT * FROM test_type_conversion_float8(100);
293293
INFO: (100.0, <class 'float'>)
294294
CONTEXT: PL/Python function "test_type_conversion_float8"
@@ -324,7 +324,7 @@ CONTEXT: PL/Python function "test_type_conversion_float8"
324324
CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
325325
plpy.info(x, type(x))
326326
return x
327-
$$ LANGUAGE plpythonu;
327+
$$ LANGUAGE plpython3u;
328328
SELECT * FROM test_type_conversion_text('hello world');
329329
INFO: ('hello world', <class 'str'>)
330330
CONTEXT: PL/Python function "test_type_conversion_text"
@@ -344,7 +344,7 @@ CONTEXT: PL/Python function "test_type_conversion_text"
344344
CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
345345
plpy.info(x, type(x))
346346
return x
347-
$$ LANGUAGE plpythonu;
347+
$$ LANGUAGE plpython3u;
348348
SELECT * FROM test_type_conversion_bytea('hello world');
349349
INFO: (b'hello world', <class 'bytes'>)
350350
CONTEXT: PL/Python function "test_type_conversion_bytea"
@@ -372,14 +372,14 @@ CONTEXT: PL/Python function "test_type_conversion_bytea"
372372
CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
373373
import marshal
374374
return marshal.dumps('hello world')
375-
$$ LANGUAGE plpythonu;
375+
$$ LANGUAGE plpython3u;
376376
CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
377377
import marshal
378378
try:
379379
return marshal.loads(x)
380-
except ValueError, e:
380+
except ValueError as e:
381381
return 'FAILED: ' + str(e)
382-
$$ LANGUAGE plpythonu;
382+
$$ LANGUAGE plpython3u;
383383
SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
384384
test_type_unmarshal
385385
---------------------
@@ -392,7 +392,7 @@ SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
392392
CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
393393
CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
394394
return y
395-
$$ LANGUAGE plpythonu;
395+
$$ LANGUAGE plpython3u;
396396
SELECT * FROM test_type_conversion_booltrue(true, true);
397397
test_type_conversion_booltrue
398398
-------------------------------
@@ -409,7 +409,7 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
409409
CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
410410
plpy.info(x, type(x))
411411
return y
412-
$$ LANGUAGE plpythonu;
412+
$$ LANGUAGE plpython3u;
413413
SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
414414
INFO: (100, <class 'int'>)
415415
CONTEXT: PL/Python function "test_type_conversion_uint2"
@@ -435,7 +435,7 @@ CONTEXT: PL/Python function "test_type_conversion_uint2"
435435
CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
436436
CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
437437
return y
438-
$$ LANGUAGE plpythonu;
438+
$$ LANGUAGE plpython3u;
439439
SELECT * FROM test_type_conversion_nnint(10, 20);
440440
test_type_conversion_nnint
441441
----------------------------
@@ -452,7 +452,7 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
452452
CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
453453
plpy.info(x, type(x))
454454
return y
455-
$$ LANGUAGE plpythonu;
455+
$$ LANGUAGE plpython3u;
456456
SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
457457
INFO: (b'hello wold', <class 'bytes'>)
458458
CONTEXT: PL/Python function "test_type_conversion_bytea10"
@@ -483,7 +483,7 @@ PL/Python function "test_type_conversion_bytea10"
483483
CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
484484
plpy.info(x, type(x))
485485
return x
486-
$$ LANGUAGE plpythonu;
486+
$$ LANGUAGE plpython3u;
487487
SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
488488
INFO: ([0, 100], <class 'list'>)
489489
CONTEXT: PL/Python function "test_type_conversion_array_int4"
@@ -531,7 +531,7 @@ CONTEXT: PL/Python function "test_type_conversion_array_int4"
531531
CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
532532
plpy.info(x, type(x))
533533
return x
534-
$$ LANGUAGE plpythonu;
534+
$$ LANGUAGE plpython3u;
535535
SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
536536
INFO: ([b'\xde\xad\xbe\xef', None], <class 'list'>)
537537
CONTEXT: PL/Python function "test_type_conversion_array_bytea"
@@ -542,7 +542,7 @@ CONTEXT: PL/Python function "test_type_conversion_array_bytea"
542542

543543
CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
544544
return [123, 'abc']
545-
$$ LANGUAGE plpythonu;
545+
$$ LANGUAGE plpython3u;
546546
SELECT * FROM test_type_conversion_array_mixed1();
547547
test_type_conversion_array_mixed1
548548
-----------------------------------
@@ -551,20 +551,20 @@ SELECT * FROM test_type_conversion_array_mixed1();
551551

552552
CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
553553
return [123, 'abc']
554-
$$ LANGUAGE plpythonu;
554+
$$ LANGUAGE plpython3u;
555555
SELECT * FROM test_type_conversion_array_mixed2();
556556
ERROR: invalid input syntax for integer: "abc"
557557
CONTEXT: while creating return value
558558
PL/Python function "test_type_conversion_array_mixed2"
559559
CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
560560
return [None]
561-
$$ LANGUAGE plpythonu;
561+
$$ LANGUAGE plpython3u;
562562
SELECT * FROM test_type_conversion_array_record();
563563
ERROR: PL/Python functions cannot return type type_record[]
564564
DETAIL: PL/Python does not support conversion to arrays of row types.
565565
CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
566566
return 'abc'
567-
$$ LANGUAGE plpythonu;
567+
$$ LANGUAGE plpython3u;
568568
SELECT * FROM test_type_conversion_array_string();
569569
test_type_conversion_array_string
570570
-----------------------------------
@@ -573,7 +573,7 @@ SELECT * FROM test_type_conversion_array_string();
573573

574574
CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
575575
return ('abc', 'def')
576-
$$ LANGUAGE plpythonu;
576+
$$ LANGUAGE plpython3u;
577577
SELECT * FROM test_type_conversion_array_tuple();
578578
test_type_conversion_array_tuple
579579
----------------------------------
@@ -582,8 +582,69 @@ SELECT * FROM test_type_conversion_array_tuple();
582582

583583
CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
584584
return 5
585-
$$ LANGUAGE plpythonu;
585+
$$ LANGUAGE plpython3u;
586586
SELECT * FROM test_type_conversion_array_error();
587587
ERROR: PL/Python: return value of function with array return type is not a Python sequence
588588
CONTEXT: while creating return value
589589
PL/Python function "test_type_conversion_array_error"
590+
--
591+
-- Prepared statements
592+
--
593+
CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
594+
LANGUAGE plpython3u
595+
AS $$
596+
plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
597+
rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
598+
return rv[0]['val']
599+
$$;
600+
SELECT test_prep_bool_input(); -- 1
601+
test_prep_bool_input
602+
----------------------
603+
1
604+
(1 row)
605+
606+
CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
607+
LANGUAGE plpython3u
608+
AS $$
609+
plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
610+
rv = plpy.execute(plan, [0], 5)
611+
plpy.info(rv[0])
612+
return rv[0]['val']
613+
$$;
614+
SELECT test_prep_bool_output(); -- false
615+
INFO: {'val': False}
616+
CONTEXT: PL/Python function "test_prep_bool_output"
617+
test_prep_bool_output
618+
-----------------------
619+
f
620+
(1 row)
621+
622+
CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
623+
LANGUAGE plpython3u
624+
AS $$
625+
plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
626+
rv = plpy.execute(plan, [bb], 5)
627+
return rv[0]['val']
628+
$$;
629+
SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
630+
test_prep_bytea_input
631+
-----------------------
632+
3
633+
(1 row)
634+
635+
CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
636+
LANGUAGE plpython3u
637+
AS $$
638+
plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
639+
rv = plpy.execute(plan, [], 5)
640+
plpy.info(rv[0])
641+
return rv[0]['val']
642+
$$;
643+
SELECT test_prep_bytea_output();
644+
INFO: {'val': b'\xaa\x00\xbb'}
645+
CONTEXT: PL/Python function "test_prep_bytea_output"
646+
test_prep_bytea_output
647+
------------------------
648+
\xaa00bb
649+
(1 row)
650+

0 commit comments

Comments
 (0)