OSDN Git Service

Allow PL/Python functions to return void, per gripe from James Robinson
authorNeil Conway <neilc@samurai.com>
Tue, 28 Feb 2006 20:03:52 +0000 (20:03 +0000)
committerNeil Conway <neilc@samurai.com>
Tue, 28 Feb 2006 20:03:52 +0000 (20:03 +0000)
(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
src/pl/plpython/expected/plpython_test.out
src/pl/plpython/plpython.c
src/pl/plpython/sql/plpython_function.sql
src/pl/plpython/sql/plpython_test.sql

index 516d057..dc52f4a 100644 (file)
@@ -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;
index 08704cb..9224373 100644 (file)
@@ -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)
+
index 2df1ef3..14c805a 100644 (file)
@@ -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;
 }
index e3ec2af..7428a59 100644 (file)
@@ -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;
index 17d6b2e..2ebdb69 100644 (file)
@@ -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";