From 87daae1143520309da299b78a8e7a68f141a268f Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Tue, 28 Feb 2006 20:03:52 +0000 Subject: [PATCH] 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. --- src/pl/plpython/expected/plpython_function.out | 11 +++++++++ src/pl/plpython/expected/plpython_test.out | 16 +++++++++++++ src/pl/plpython/plpython.c | 32 ++++++++++++++++++++------ src/pl/plpython/sql/plpython_function.sql | 15 ++++++++++++ src/pl/plpython/sql/plpython_test.sql | 5 ++++ 5 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/pl/plpython/expected/plpython_function.out b/src/pl/plpython/expected/plpython_function.out index 516d057689..dc52f4af17 100644 --- a/src/pl/plpython/expected/plpython_function.out +++ b/src/pl/plpython/expected/plpython_function.out @@ -289,3 +289,14 @@ plan = plpy.prepare("SELECT $1 AS testvalue1, $2 AS testvalue2", ["text", "text" rv = plpy.execute(plan, u"\\x80", 1) return rv[0]["testvalue1"] ' 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; diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out index 08704cb571..92243738f6 100644 --- a/src/pl/plpython/expected/plpython_test.out +++ b/src/pl/plpython/expected/plpython_test.out @@ -182,3 +182,19 @@ SELECT newline_crlf(); 123 (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) + diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index 2df1ef3989..14c805a29e 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -29,7 +29,7 @@ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * 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 { - FmgrInfo typfunc; + FmgrInfo typfunc; /* The type's input function */ + Oid typoid; /* The OID of the type */ Oid typioparam; bool typbyval; } PLyObToDatum; @@ -138,7 +139,7 @@ typedef struct PLyProcedure int nargs; PyObject *code; /* compiled procedure code */ 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 * PLyProcedure */ } PLyProcedure; @@ -757,9 +758,24 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) 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; rv = PointerGetDatum(NULL); @@ -1031,8 +1047,9 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid, procStruct->prorettype); rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup); - /* Disallow pseudotype result */ - if (rvTypeStruct->typtype == 'p') + /* Disallow pseudotype result, except for void */ + if (rvTypeStruct->typtype == 'p' && + procStruct->prorettype != VOIDOID) { if (procStruct->prorettype == TRIGGEROID) ereport(ERROR, @@ -1329,6 +1346,7 @@ PLy_output_datum_func2(PLyObToDatum * arg, HeapTuple typeTup) Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); perm_fmgr_info(typeStruct->typinput, &arg->typfunc); + arg->typoid = HeapTupleGetOid(typeTup); arg->typioparam = getTypeIOParam(typeTup); arg->typbyval = typeStruct->typbyval; } diff --git a/src/pl/plpython/sql/plpython_function.sql b/src/pl/plpython/sql/plpython_function.sql index e3ec2afe01..7428a599ee 100644 --- a/src/pl/plpython/sql/plpython_function.sql +++ b/src/pl/plpython/sql/plpython_function.sql @@ -341,3 +341,18 @@ plan = plpy.prepare("SELECT $1 AS testvalue1, $2 AS testvalue2", ["text", "text" rv = plpy.execute(plan, u"\\x80", 1) return rv[0]["testvalue1"] ' 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; diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql index 17d6b2e792..2ebdb695a9 100644 --- a/src/pl/plpython/sql/plpython_test.sql +++ b/src/pl/plpython/sql/plpython_test.sql @@ -68,3 +68,8 @@ SELECT join_sequences(sequences) FROM sequences SELECT newline_lf(); SELECT newline_cr(); 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"; -- 2.11.0