Allow PL/Python functions to return void, per gripe from James Robinson
authorNeil Conway
Tue, 28 Feb 2006 20:03:52 +0000 (20:03 +0000)
committerNeil Conway
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 516d0576899bfa95d94a926c7121ee4b05293d61..dc52f4af17d180b470b2ea44421e55cc66cab0b7 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 08704cb571dfea03074d004daa15001f40237070..92243738f6690dbf7f562475cfce066df4430dd4 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 2df1ef3989399bf861440a646d27d5da0f797dda..14c805a29efea8f968ff8f1cf577189632e3bae7 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 e3ec2afe01481e4cd4e396aed895bc0e3459fb4c..7428a599eea577dccf882e3fcd8a2f3c7e11f07d 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 17d6b2e79225cb3de151214f642adb47bdba62f8..2ebdb695a9801c2f72ac60258748abe78ea741b5 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";