Hi Tom,

Thanks for v2.

One small thing in 0002. A single OUT void parameter worked before
and is now rejected. RETURNS TABLE(v void) is the same.

This happens with check_function_bodies off too. And pg_dump auto
set that guc to off, so the backup is not able to restore...

The attached nocfbot patch goes on top of v2 and fixes it.

Thanks,
Shihao
From 22d0a96a13c1f4704d4ece80f789000f145ab03d Mon Sep 17 00:00:00 2001
From: shihao zhong <[email protected]>
Date: Sat, 31 Oct 2026 20:18:30 -0400
Subject: [PATCH] plpython validator: check only input arguments for
 pseudotypes

The previous commit moved the pseudotype check into the validator and
let OUT and TABLE arguments of pseudotype through only when prorettype
is RECORD.  That rejects a single OUT void parameter, which worked
before.  With one output argument, prorettype is that argument's type
and the result type check above already covers it, and with more than
one, prorettype is RECORD.  So only input arguments need checking, and
those are exactly proargtypes, as in fmgr_sql_validator().
---
 src/pl/plpython/expected/plpython_void.out | 10 ++++++
 src/pl/plpython/plpy_main.c                | 41 +++++++---------------
 src/pl/plpython/sql/plpython_void.sql      |  7 ++++
 3 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/src/pl/plpython/expected/plpython_void.out 
b/src/pl/plpython/expected/plpython_void.out
index 07d0760783e..07ef6a4814e 100644
--- a/src/pl/plpython/expected/plpython_void.out
+++ b/src/pl/plpython/expected/plpython_void.out
@@ -28,3 +28,13 @@ SELECT test_return_none(), test_return_none() IS NULL AS "is 
null";
                   | t
 (1 row)
 
+-- a single void OUT parameter works the same as RETURNS void
+CREATE FUNCTION test_void_out(OUT v void) AS $$
+x = 10
+$$ LANGUAGE plpython3u;
+SELECT test_void_out();
+ test_void_out 
+---------------
+ 
+(1 row)
+
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index ce0e7044d9d..a198166c956 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -10,7 +10,6 @@
 #include "commands/event_trigger.h"
 #include "commands/trigger.h"
 #include "executor/spi.h"
-#include "funcapi.h"
 #include "miscadmin.h"
 #include "plpy_elog.h"
 #include "plpy_exec.h"
@@ -115,10 +114,6 @@ plpython3_validator(PG_FUNCTION_ARGS)
        HeapTuple       tuple;
        Form_pg_proc procStruct;
        Oid                     rettype;
-       Oid                *argtypes;
-       char      **argnames,
-                          *argmodes;
-       int                     nargs;
        PLyTrigType is_trigger;
        TriggerData trigdata;
        EventTriggerData etrigdata;
@@ -149,30 +144,20 @@ plpython3_validator(PG_FUNCTION_ARGS)
                                                        
format_type_be(rettype))));
        }
 
-       /* Disallow pseudotype arguments, except for output records */
-       nargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes);
-       for (int i = 0; i < nargs; i++)
+       /*
+        * Disallow pseudotype input arguments.  Output arguments need no check:
+        * with one of them, prorettype is its type and was checked above, and
+        * with more than one, prorettype is RECORD.
+        */
+       for (int i = 0; i < procStruct->pronargs; i++)
        {
-               if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
-               {
-                       if (argmodes &&
-                               (argmodes[i] == PROARGMODE_OUT ||
-                                argmodes[i] == PROARGMODE_TABLE))
-                       {
-                               if (rettype == RECORDOID)
-                                        /* okay */ ;
-                               else
-                                       ereport(ERROR,
-                                                       
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                        errmsg("PL/Python 
functions cannot return type %s",
-                                                                       
format_type_be(argtypes[i]))));
-                       }
-                       else
-                               ereport(ERROR,
-                                               
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("PL/Python functions 
cannot accept type %s",
-                                                               
format_type_be(argtypes[i]))));
-               }
+               Oid                     argtype = 
procStruct->proargtypes.values[i];
+
+               if (get_typtype(argtype) == TYPTYPE_PSEUDO)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("PL/Python functions cannot 
accept type %s",
+                                                       
format_type_be(argtype))));
        }
 
        is_trigger = PLy_procedure_is_trigger(procStruct);
diff --git a/src/pl/plpython/sql/plpython_void.sql 
b/src/pl/plpython/sql/plpython_void.sql
index 5a1a6711fb0..0854d0c4740 100644
--- a/src/pl/plpython/sql/plpython_void.sql
+++ b/src/pl/plpython/sql/plpython_void.sql
@@ -20,3 +20,10 @@ $$ LANGUAGE plpython3u;
 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";
+
+-- a single void OUT parameter works the same as RETURNS void
+CREATE FUNCTION test_void_out(OUT v void) AS $$
+x = 10
+$$ LANGUAGE plpython3u;
+
+SELECT test_void_out();

Reply via email to