mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Allow PL/Python functions to return void, per gripe from James Robinson
(I didn't use his patch, however). A void-returning PL/Python function must return None (from Python), which is translated into a void datum (and *not* NULL) for Postgres. I also added some regression tests for this functionality.
This commit is contained in:
@ -289,3 +289,14 @@ plan = plpy.prepare("SELECT $1 AS testvalue1, $2 AS testvalue2", ["text", "text"
|
|||||||
rv = plpy.execute(plan, u"\\x80", 1)
|
rv = plpy.execute(plan, u"\\x80", 1)
|
||||||
return rv[0]["testvalue1"]
|
return rv[0]["testvalue1"]
|
||||||
' LANGUAGE plpythonu;
|
' LANGUAGE plpythonu;
|
||||||
|
-- Tests for functions that return void
|
||||||
|
CREATE FUNCTION test_void_func1() RETURNS void AS $$
|
||||||
|
x = 10
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
-- illegal: can't return non-None value in void-returning func
|
||||||
|
CREATE FUNCTION test_void_func2() RETURNS void AS $$
|
||||||
|
return 10
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
CREATE FUNCTION test_return_none() RETURNS int AS $$
|
||||||
|
None
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
@ -182,3 +182,19 @@ SELECT newline_crlf();
|
|||||||
123
|
123
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- Tests for functions returning void
|
||||||
|
SELECT test_void_func1(), test_void_func1() IS NULL AS "is null";
|
||||||
|
test_void_func1 | is null
|
||||||
|
-----------------+---------
|
||||||
|
| f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT test_void_func2(); -- should fail
|
||||||
|
ERROR: unexpected return value from plpython procedure
|
||||||
|
DETAIL: void-returning functions must return "None"
|
||||||
|
SELECT test_return_none(), test_return_none() IS NULL AS "is null";
|
||||||
|
test_return_none | is null
|
||||||
|
------------------+---------
|
||||||
|
| t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.71 2006/02/20 20:10:37 neilc Exp $
|
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.72 2006/02/28 20:03:52 neilc Exp $
|
||||||
*
|
*
|
||||||
*********************************************************************
|
*********************************************************************
|
||||||
*/
|
*/
|
||||||
@ -91,7 +91,8 @@ typedef union PLyTypeInput
|
|||||||
*/
|
*/
|
||||||
typedef struct PLyObToDatum
|
typedef struct PLyObToDatum
|
||||||
{
|
{
|
||||||
FmgrInfo typfunc;
|
FmgrInfo typfunc; /* The type's input function */
|
||||||
|
Oid typoid; /* The OID of the type */
|
||||||
Oid typioparam;
|
Oid typioparam;
|
||||||
bool typbyval;
|
bool typbyval;
|
||||||
} PLyObToDatum;
|
} PLyObToDatum;
|
||||||
@ -138,7 +139,7 @@ typedef struct PLyProcedure
|
|||||||
int nargs;
|
int nargs;
|
||||||
PyObject *code; /* compiled procedure code */
|
PyObject *code; /* compiled procedure code */
|
||||||
PyObject *statics; /* data saved across calls, local scope */
|
PyObject *statics; /* data saved across calls, local scope */
|
||||||
PyObject *globals; /* data saved across calls, global score */
|
PyObject *globals; /* data saved across calls, global scope */
|
||||||
PyObject *me; /* PyCObject containing pointer to this
|
PyObject *me; /* PyCObject containing pointer to this
|
||||||
* PLyProcedure */
|
* PLyProcedure */
|
||||||
} PLyProcedure;
|
} PLyProcedure;
|
||||||
@ -757,9 +758,24 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
|
|||||||
elog(ERROR, "SPI_finish failed");
|
elog(ERROR, "SPI_finish failed");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* convert the python PyObject to a postgresql Datum
|
* If the function is declared to return void, the Python
|
||||||
|
* return value must be None. For void-returning functions, we
|
||||||
|
* also treat a None return value as a special "void datum"
|
||||||
|
* rather than NULL (as is the case for non-void-returning
|
||||||
|
* functions).
|
||||||
*/
|
*/
|
||||||
if (plrv == Py_None)
|
if (proc->result.out.d.typoid == VOIDOID)
|
||||||
|
{
|
||||||
|
if (plrv != Py_None)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("unexpected return value from plpython procedure"),
|
||||||
|
errdetail("void-returning functions must return \"None\"")));
|
||||||
|
|
||||||
|
fcinfo->isnull = false;
|
||||||
|
rv = (Datum) 0;
|
||||||
|
}
|
||||||
|
else if (plrv == Py_None)
|
||||||
{
|
{
|
||||||
fcinfo->isnull = true;
|
fcinfo->isnull = true;
|
||||||
rv = PointerGetDatum(NULL);
|
rv = PointerGetDatum(NULL);
|
||||||
@ -1031,8 +1047,9 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
|
|||||||
procStruct->prorettype);
|
procStruct->prorettype);
|
||||||
rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
|
rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
|
||||||
|
|
||||||
/* Disallow pseudotype result */
|
/* Disallow pseudotype result, except for void */
|
||||||
if (rvTypeStruct->typtype == 'p')
|
if (rvTypeStruct->typtype == 'p' &&
|
||||||
|
procStruct->prorettype != VOIDOID)
|
||||||
{
|
{
|
||||||
if (procStruct->prorettype == TRIGGEROID)
|
if (procStruct->prorettype == TRIGGEROID)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -1329,6 +1346,7 @@ PLy_output_datum_func2(PLyObToDatum * arg, HeapTuple typeTup)
|
|||||||
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
||||||
|
|
||||||
perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
|
perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
|
||||||
|
arg->typoid = HeapTupleGetOid(typeTup);
|
||||||
arg->typioparam = getTypeIOParam(typeTup);
|
arg->typioparam = getTypeIOParam(typeTup);
|
||||||
arg->typbyval = typeStruct->typbyval;
|
arg->typbyval = typeStruct->typbyval;
|
||||||
}
|
}
|
||||||
|
@ -341,3 +341,18 @@ plan = plpy.prepare("SELECT $1 AS testvalue1, $2 AS testvalue2", ["text", "text"
|
|||||||
rv = plpy.execute(plan, u"\\x80", 1)
|
rv = plpy.execute(plan, u"\\x80", 1)
|
||||||
return rv[0]["testvalue1"]
|
return rv[0]["testvalue1"]
|
||||||
' LANGUAGE plpythonu;
|
' LANGUAGE plpythonu;
|
||||||
|
|
||||||
|
-- Tests for functions that return void
|
||||||
|
|
||||||
|
CREATE FUNCTION test_void_func1() RETURNS void AS $$
|
||||||
|
x = 10
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
|
||||||
|
-- illegal: can't return non-None value in void-returning func
|
||||||
|
CREATE FUNCTION test_void_func2() RETURNS void AS $$
|
||||||
|
return 10
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
|
||||||
|
CREATE FUNCTION test_return_none() RETURNS int AS $$
|
||||||
|
None
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
@ -68,3 +68,8 @@ SELECT join_sequences(sequences) FROM sequences
|
|||||||
SELECT newline_lf();
|
SELECT newline_lf();
|
||||||
SELECT newline_cr();
|
SELECT newline_cr();
|
||||||
SELECT newline_crlf();
|
SELECT newline_crlf();
|
||||||
|
|
||||||
|
-- Tests for functions returning void
|
||||||
|
SELECT test_void_func1(), test_void_func1() IS NULL AS "is null";
|
||||||
|
SELECT test_void_func2(); -- should fail
|
||||||
|
SELECT test_return_none(), test_return_none() IS NULL AS "is null";
|
||||||
|
Reference in New Issue
Block a user