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();
ERROR: PL/Python: return value of function with array return type is not a Python sequence
CONTEXT: while creating return value
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)
/**********************************************************************
* 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);
static char *PLy_strdup(const char *);
static void PLy_free(void *);
static PyObject *PLyUnicode_Str(PyObject *unicode);
static PyObject *PLyUnicode_Bytes(PyObject *unicode);
static char *PLyUnicode_AsString(PyObject *unicode);
......@@ -2983,38 +2982,24 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
for (j = 0; j < nargs; j++)
{
PyObject *elem,
*so;
PyObject *elem;
elem = PySequence_GetItem(list, j);
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();
{
char *sv = PyString_AsString(so);
plan->values[j] =
InputFunctionCall(&(plan->args[j].out.d.typfunc),
sv,
plan->args[j].out.d.typioparam,
-1);
plan->args[j].out.d.func(NULL, &(plan->args[j].out.d), elem);
}
PG_CATCH();
{
Py_DECREF(so);
Py_DECREF(elem);
PG_RE_THROW();
}
PG_END_TRY();
Py_DECREF(so);
Py_DECREF(elem);
nulls[j] = ' ';
}
else
......@@ -3637,26 +3622,6 @@ PLy_free(void *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
* PostgreSQL server encoding. Reference ownership is passed to the
......
......@@ -269,3 +269,53 @@ return 5
$$ LANGUAGE plpythonu;
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