Commit 12c2f2f6 authored by Peter Eisentraut's avatar Peter Eisentraut

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.
parent c21ac0b5
...@@ -587,3 +587,64 @@ SELECT * FROM test_type_conversion_array_error(); ...@@ -587,3 +587,64 @@ SELECT * FROM test_type_conversion_array_error();
ERROR: PL/Python: return value of function with array return type is not a Python sequence ERROR: PL/Python: return value of function with array return type is not a Python sequence
CONTEXT: while creating return value CONTEXT: while creating return value
PL/Python function "test_type_conversion_array_error" PL/Python function "test_type_conversion_array_error"
--
-- Prepared statements
--
CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
LANGUAGE plpythonu
AS $$
plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
return rv[0]['val']
$$;
SELECT test_prep_bool_input(); -- 1
test_prep_bool_input
----------------------
1
(1 row)
CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
LANGUAGE plpythonu
AS $$
plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
rv = plpy.execute(plan, [0], 5)
plpy.info(rv[0])
return rv[0]['val']
$$;
SELECT test_prep_bool_output(); -- false
INFO: {'val': False}
CONTEXT: PL/Python function "test_prep_bool_output"
test_prep_bool_output
-----------------------
f
(1 row)
CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
LANGUAGE plpythonu
AS $$
plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
rv = plpy.execute(plan, [bb], 5)
return rv[0]['val']
$$;
SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
test_prep_bytea_input
-----------------------
3
(1 row)
CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
LANGUAGE plpythonu
AS $$
plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
rv = plpy.execute(plan, [], 5)
plpy.info(rv[0])
return rv[0]['val']
$$;
SELECT test_prep_bytea_output();
INFO: {'val': '\xaa\x00\xbb'}
CONTEXT: PL/Python function "test_prep_bytea_output"
test_prep_bytea_output
------------------------
\xaa00bb
(1 row)
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$ CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_bool(true); SELECT * FROM test_type_conversion_bool(true);
INFO: (True, <class 'bool'>) INFO: (True, <class 'bool'>)
CONTEXT: PL/Python function "test_type_conversion_bool" CONTEXT: PL/Python function "test_type_conversion_bool"
...@@ -51,7 +51,7 @@ elif n == 5: ...@@ -51,7 +51,7 @@ elif n == 5:
ret = [0] ret = [0]
plpy.info(ret, not not ret) plpy.info(ret, not not ret)
return ret return ret
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_bool_other(0); SELECT * FROM test_type_conversion_bool_other(0);
INFO: (0, False) INFO: (0, False)
CONTEXT: PL/Python function "test_type_conversion_bool_other" CONTEXT: PL/Python function "test_type_conversion_bool_other"
...@@ -103,7 +103,7 @@ CONTEXT: PL/Python function "test_type_conversion_bool_other" ...@@ -103,7 +103,7 @@ CONTEXT: PL/Python function "test_type_conversion_bool_other"
CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$ CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_char('a'); SELECT * FROM test_type_conversion_char('a');
INFO: ('a', <class 'str'>) INFO: ('a', <class 'str'>)
CONTEXT: PL/Python function "test_type_conversion_char" CONTEXT: PL/Python function "test_type_conversion_char"
...@@ -123,7 +123,7 @@ CONTEXT: PL/Python function "test_type_conversion_char" ...@@ -123,7 +123,7 @@ CONTEXT: PL/Python function "test_type_conversion_char"
CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$ CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_int2(100::int2); SELECT * FROM test_type_conversion_int2(100::int2);
INFO: (100, <class 'int'>) INFO: (100, <class 'int'>)
CONTEXT: PL/Python function "test_type_conversion_int2" CONTEXT: PL/Python function "test_type_conversion_int2"
...@@ -151,7 +151,7 @@ CONTEXT: PL/Python function "test_type_conversion_int2" ...@@ -151,7 +151,7 @@ CONTEXT: PL/Python function "test_type_conversion_int2"
CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$ CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_int4(100); SELECT * FROM test_type_conversion_int4(100);
INFO: (100, <class 'int'>) INFO: (100, <class 'int'>)
CONTEXT: PL/Python function "test_type_conversion_int4" CONTEXT: PL/Python function "test_type_conversion_int4"
...@@ -179,9 +179,9 @@ CONTEXT: PL/Python function "test_type_conversion_int4" ...@@ -179,9 +179,9 @@ CONTEXT: PL/Python function "test_type_conversion_int4"
CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$ CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_int8(100); SELECT * FROM test_type_conversion_int8(100);
INFO: (100L, <type 'long'>) INFO: (100, <class 'int'>)
CONTEXT: PL/Python function "test_type_conversion_int8" CONTEXT: PL/Python function "test_type_conversion_int8"
test_type_conversion_int8 test_type_conversion_int8
--------------------------- ---------------------------
...@@ -189,7 +189,7 @@ CONTEXT: PL/Python function "test_type_conversion_int8" ...@@ -189,7 +189,7 @@ CONTEXT: PL/Python function "test_type_conversion_int8"
(1 row) (1 row)
SELECT * FROM test_type_conversion_int8(-100); SELECT * FROM test_type_conversion_int8(-100);
INFO: (-100L, <type 'long'>) INFO: (-100, <class 'int'>)
CONTEXT: PL/Python function "test_type_conversion_int8" CONTEXT: PL/Python function "test_type_conversion_int8"
test_type_conversion_int8 test_type_conversion_int8
--------------------------- ---------------------------
...@@ -197,7 +197,7 @@ CONTEXT: PL/Python function "test_type_conversion_int8" ...@@ -197,7 +197,7 @@ CONTEXT: PL/Python function "test_type_conversion_int8"
(1 row) (1 row)
SELECT * FROM test_type_conversion_int8(5000000000); SELECT * FROM test_type_conversion_int8(5000000000);
INFO: (5000000000L, <type 'long'>) INFO: (5000000000, <class 'int'>)
CONTEXT: PL/Python function "test_type_conversion_int8" CONTEXT: PL/Python function "test_type_conversion_int8"
test_type_conversion_int8 test_type_conversion_int8
--------------------------- ---------------------------
...@@ -215,7 +215,7 @@ CONTEXT: PL/Python function "test_type_conversion_int8" ...@@ -215,7 +215,7 @@ CONTEXT: PL/Python function "test_type_conversion_int8"
CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
/* The current implementation converts numeric to float. */ /* The current implementation converts numeric to float. */
SELECT * FROM test_type_conversion_numeric(100); SELECT * FROM test_type_conversion_numeric(100);
INFO: (100.0, <class 'float'>) INFO: (100.0, <class 'float'>)
...@@ -252,7 +252,7 @@ CONTEXT: PL/Python function "test_type_conversion_numeric" ...@@ -252,7 +252,7 @@ CONTEXT: PL/Python function "test_type_conversion_numeric"
CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$ CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_float4(100); SELECT * FROM test_type_conversion_float4(100);
INFO: (100.0, <class 'float'>) INFO: (100.0, <class 'float'>)
CONTEXT: PL/Python function "test_type_conversion_float4" CONTEXT: PL/Python function "test_type_conversion_float4"
...@@ -288,7 +288,7 @@ CONTEXT: PL/Python function "test_type_conversion_float4" ...@@ -288,7 +288,7 @@ CONTEXT: PL/Python function "test_type_conversion_float4"
CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$ CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_float8(100); SELECT * FROM test_type_conversion_float8(100);
INFO: (100.0, <class 'float'>) INFO: (100.0, <class 'float'>)
CONTEXT: PL/Python function "test_type_conversion_float8" CONTEXT: PL/Python function "test_type_conversion_float8"
...@@ -324,7 +324,7 @@ CONTEXT: PL/Python function "test_type_conversion_float8" ...@@ -324,7 +324,7 @@ CONTEXT: PL/Python function "test_type_conversion_float8"
CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$ CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_text('hello world'); SELECT * FROM test_type_conversion_text('hello world');
INFO: ('hello world', <class 'str'>) INFO: ('hello world', <class 'str'>)
CONTEXT: PL/Python function "test_type_conversion_text" CONTEXT: PL/Python function "test_type_conversion_text"
...@@ -344,7 +344,7 @@ CONTEXT: PL/Python function "test_type_conversion_text" ...@@ -344,7 +344,7 @@ CONTEXT: PL/Python function "test_type_conversion_text"
CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$ CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_bytea('hello world'); SELECT * FROM test_type_conversion_bytea('hello world');
INFO: (b'hello world', <class 'bytes'>) INFO: (b'hello world', <class 'bytes'>)
CONTEXT: PL/Python function "test_type_conversion_bytea" CONTEXT: PL/Python function "test_type_conversion_bytea"
...@@ -372,14 +372,14 @@ CONTEXT: PL/Python function "test_type_conversion_bytea" ...@@ -372,14 +372,14 @@ CONTEXT: PL/Python function "test_type_conversion_bytea"
CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$ CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
import marshal import marshal
return marshal.dumps('hello world') return marshal.dumps('hello world')
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$ CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
import marshal import marshal
try: try:
return marshal.loads(x) return marshal.loads(x)
except ValueError, e: except ValueError as e:
return 'FAILED: ' + str(e) return 'FAILED: ' + str(e)
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT test_type_unmarshal(x) FROM test_type_marshal() x; SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
test_type_unmarshal test_type_unmarshal
--------------------- ---------------------
...@@ -392,7 +392,7 @@ SELECT test_type_unmarshal(x) FROM test_type_marshal() x; ...@@ -392,7 +392,7 @@ SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL); CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$ CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
return y return y
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_booltrue(true, true); SELECT * FROM test_type_conversion_booltrue(true, true);
test_type_conversion_booltrue test_type_conversion_booltrue
------------------------------- -------------------------------
...@@ -409,7 +409,7 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0); ...@@ -409,7 +409,7 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$ CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return y return y
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_uint2(100::uint2, 50); SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
INFO: (100, <class 'int'>) INFO: (100, <class 'int'>)
CONTEXT: PL/Python function "test_type_conversion_uint2" CONTEXT: PL/Python function "test_type_conversion_uint2"
...@@ -435,7 +435,7 @@ CONTEXT: PL/Python function "test_type_conversion_uint2" ...@@ -435,7 +435,7 @@ CONTEXT: PL/Python function "test_type_conversion_uint2"
CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL); CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$ CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
return y return y
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_nnint(10, 20); SELECT * FROM test_type_conversion_nnint(10, 20);
test_type_conversion_nnint test_type_conversion_nnint
---------------------------- ----------------------------
...@@ -452,7 +452,7 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT ...@@ -452,7 +452,7 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$ CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return y return y
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold'); SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
INFO: (b'hello wold', <class 'bytes'>) INFO: (b'hello wold', <class 'bytes'>)
CONTEXT: PL/Python function "test_type_conversion_bytea10" CONTEXT: PL/Python function "test_type_conversion_bytea10"
...@@ -483,7 +483,7 @@ PL/Python function "test_type_conversion_bytea10" ...@@ -483,7 +483,7 @@ PL/Python function "test_type_conversion_bytea10"
CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$ CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]); SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
INFO: ([0, 100], <class 'list'>) INFO: ([0, 100], <class 'list'>)
CONTEXT: PL/Python function "test_type_conversion_array_int4" CONTEXT: PL/Python function "test_type_conversion_array_int4"
...@@ -531,7 +531,7 @@ CONTEXT: PL/Python function "test_type_conversion_array_int4" ...@@ -531,7 +531,7 @@ CONTEXT: PL/Python function "test_type_conversion_array_int4"
CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$ CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
plpy.info(x, type(x)) plpy.info(x, type(x))
return x return x
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]); SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
INFO: ([b'\xde\xad\xbe\xef', None], <class 'list'>) INFO: ([b'\xde\xad\xbe\xef', None], <class 'list'>)
CONTEXT: PL/Python function "test_type_conversion_array_bytea" CONTEXT: PL/Python function "test_type_conversion_array_bytea"
...@@ -542,7 +542,7 @@ CONTEXT: PL/Python function "test_type_conversion_array_bytea" ...@@ -542,7 +542,7 @@ CONTEXT: PL/Python function "test_type_conversion_array_bytea"
CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$ CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
return [123, 'abc'] return [123, 'abc']
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_array_mixed1(); SELECT * FROM test_type_conversion_array_mixed1();
test_type_conversion_array_mixed1 test_type_conversion_array_mixed1
----------------------------------- -----------------------------------
...@@ -551,20 +551,20 @@ SELECT * FROM test_type_conversion_array_mixed1(); ...@@ -551,20 +551,20 @@ SELECT * FROM test_type_conversion_array_mixed1();
CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$ CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
return [123, 'abc'] return [123, 'abc']
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_array_mixed2(); SELECT * FROM test_type_conversion_array_mixed2();
ERROR: invalid input syntax for integer: "abc" ERROR: invalid input syntax for integer: "abc"
CONTEXT: while creating return value CONTEXT: while creating return value
PL/Python function "test_type_conversion_array_mixed2" PL/Python function "test_type_conversion_array_mixed2"
CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$ CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
return [None] return [None]
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_array_record(); SELECT * FROM test_type_conversion_array_record();
ERROR: PL/Python functions cannot return type type_record[] ERROR: PL/Python functions cannot return type type_record[]
DETAIL: PL/Python does not support conversion to arrays of row types. DETAIL: PL/Python does not support conversion to arrays of row types.
CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$ CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
return 'abc' return 'abc'
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_array_string(); SELECT * FROM test_type_conversion_array_string();
test_type_conversion_array_string test_type_conversion_array_string
----------------------------------- -----------------------------------
...@@ -573,7 +573,7 @@ SELECT * FROM test_type_conversion_array_string(); ...@@ -573,7 +573,7 @@ SELECT * FROM test_type_conversion_array_string();
CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$ CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
return ('abc', 'def') return ('abc', 'def')
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_array_tuple(); SELECT * FROM test_type_conversion_array_tuple();
test_type_conversion_array_tuple test_type_conversion_array_tuple
---------------------------------- ----------------------------------
...@@ -582,8 +582,69 @@ SELECT * FROM test_type_conversion_array_tuple(); ...@@ -582,8 +582,69 @@ SELECT * FROM test_type_conversion_array_tuple();
CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$ CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
return 5 return 5
$$ LANGUAGE plpythonu; $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_array_error(); SELECT * FROM test_type_conversion_array_error();
ERROR: PL/Python: return value of function with array return type is not a Python sequence ERROR: PL/Python: return value of function with array return type is not a Python sequence
CONTEXT: while creating return value CONTEXT: while creating return value
PL/Python function "test_type_conversion_array_error" PL/Python function "test_type_conversion_array_error"
--
-- Prepared statements
--
CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
LANGUAGE plpython3u
AS $$
plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
return rv[0]['val']
$$;
SELECT test_prep_bool_input(); -- 1
test_prep_bool_input
----------------------
1
(1 row)
CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
LANGUAGE plpython3u
AS $$
plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
rv = plpy.execute(plan, [0], 5)
plpy.info(rv[0])
return rv[0]['val']
$$;
SELECT test_prep_bool_output(); -- false
INFO: {'val': False}
CONTEXT: PL/Python function "test_prep_bool_output"
test_prep_bool_output
-----------------------
f
(1 row)
CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
LANGUAGE plpython3u
AS $$
plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
rv = plpy.execute(plan, [bb], 5)
return rv[0]['val']
$$;
SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
test_prep_bytea_input
-----------------------
3
(1 row)
CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
LANGUAGE plpython3u
AS $$
plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
rv = plpy.execute(plan, [], 5)
plpy.info(rv[0])
return rv[0]['val']
$$;
SELECT test_prep_bytea_output();
INFO: {'val': b'\xaa\x00\xbb'}
CONTEXT: PL/Python function "test_prep_bytea_output"
test_prep_bytea_output
------------------------
\xaa00bb
(1 row)
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * plpython.c - python as a procedural language for PostgreSQL
* *
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.139 2010/02/26 02:01:36 momjian Exp $ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.140 2010/03/18 13:23:56 petere Exp $
* *
********************************************************************* *********************************************************************
*/ */
...@@ -287,7 +287,6 @@ static void *PLy_malloc0(size_t); ...@@ -287,7 +287,6 @@ static void *PLy_malloc0(size_t);
static char *PLy_strdup(const char *); static char *PLy_strdup(const char *);
static void PLy_free(void *); static void PLy_free(void *);
static PyObject *PLyUnicode_Str(PyObject *unicode);
static PyObject *PLyUnicode_Bytes(PyObject *unicode); static PyObject *PLyUnicode_Bytes(PyObject *unicode);
static char *PLyUnicode_AsString(PyObject *unicode); static char *PLyUnicode_AsString(PyObject *unicode);
...@@ -2983,38 +2982,24 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit) ...@@ -2983,38 +2982,24 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
for (j = 0; j < nargs; j++) for (j = 0; j < nargs; j++)
{ {
PyObject *elem, PyObject *elem;
*so;
elem = PySequence_GetItem(list, j); elem = PySequence_GetItem(list, j);
if (elem != Py_None) if (elem != Py_None)
{ {
if (PyUnicode_Check(elem))
so = PLyUnicode_Str(elem);
else
so = PyObject_Str(elem);
if (!so)
PLy_elog(ERROR, "could not execute plan");
Py_DECREF(elem);
PG_TRY(); PG_TRY();
{ {
char *sv = PyString_AsString(so);
plan->values[j] = plan->values[j] =
InputFunctionCall(&(plan->args[j].out.d.typfunc), plan->args[j].out.d.func(NULL, &(plan->args[j].out.d), elem);
sv,
plan->args[j].out.d.typioparam,
-1);
} }
PG_CATCH(); PG_CATCH();
{ {
Py_DECREF(so); Py_DECREF(elem);
PG_RE_THROW(); PG_RE_THROW();
} }
PG_END_TRY(); PG_END_TRY();
Py_DECREF(so); Py_DECREF(elem);
nulls[j] = ' '; nulls[j] = ' ';
} }
else else
...@@ -3637,26 +3622,6 @@ PLy_free(void *ptr) ...@@ -3637,26 +3622,6 @@ PLy_free(void *ptr)
free(ptr); free(ptr);
} }
/*
* Convert a Unicode object to a Python string.
*/
static PyObject *
PLyUnicode_Str(PyObject *unicode)
{
#if PY_MAJOR_VERSION >= 3
/* In Python 3, this is a noop. */
Py_INCREF(unicode);
return unicode;
#else
/*
* In Python 2, this means converting the Unicode to bytes in the server
* encoding.
*/
return PLyUnicode_Bytes(unicode);
#endif
}
/* /*
* Convert a Python unicode object to a Python string/bytes object in * Convert a Python unicode object to a Python string/bytes object in
* PostgreSQL server encoding. Reference ownership is passed to the * PostgreSQL server encoding. Reference ownership is passed to the
......
...@@ -269,3 +269,53 @@ return 5 ...@@ -269,3 +269,53 @@ return 5
$$ LANGUAGE plpythonu; $$ LANGUAGE plpythonu;
SELECT * FROM test_type_conversion_array_error(); SELECT * FROM test_type_conversion_array_error();
--
-- Prepared statements
--
CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
LANGUAGE plpythonu
AS $$
plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
return rv[0]['val']
$$;
SELECT test_prep_bool_input(); -- 1
CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
LANGUAGE plpythonu
AS $$
plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
rv = plpy.execute(plan, [0], 5)
plpy.info(rv[0])
return rv[0]['val']
$$;
SELECT test_prep_bool_output(); -- false
CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
LANGUAGE plpythonu
AS $$
plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
rv = plpy.execute(plan, [bb], 5)
return rv[0]['val']
$$;
SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
LANGUAGE plpythonu
AS $$
plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
rv = plpy.execute(plan, [], 5)
plpy.info(rv[0])
return rv[0]['val']
$$;
SELECT test_prep_bytea_output();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment