PL/Python doesn't make any effort to support polymorphic arguments
or results.  (It should, but that's a task for some other day.)
However, I happened to notice that while previous releases throw
a useful error:

regression=# CREATE FUNCTION python_poly(x anyelement) RETURNS anyelement AS $$
  return x
$$ LANGUAGE plpython3u;
ERROR:  PL/Python functions cannot return type anyelement

in v19/master you get something pretty off-point:

regression=# CREATE FUNCTION python_poly(x anyelement) RETURNS anyelement AS $$
  return x
$$ LANGUAGE plpython3u;
ERROR:  could not determine actual result type for function "python_poly" 
declared to return type anyelement

The reason for this is that commit 9bcb8a694 restructured things
to let plpython use the funccache.c infrastructure.  That means
that the argument-type checks done in PLy_procedure_create are
now inside a callback function that funccache.c calls after it's
discovered it lacks a cache entry for the function.  And in order
to lookup a cache entry, it has to resolve polymorphic types to
real types, and the fake fcinfo that plpython3_validator makes
doesn't contain enough data to support that.

I don't want to resolve that conundrum right now --- for one thing,
I've not made up my mind whether it's more plpython's fault or
more funccache.c's fault.  What I think we should do about this
is to move the no-pseudotype-argument checks from where they are
into the validator's startup section, where they will get enforced
whether check_function_bodies is on or not.  (It's really a bug that
right now you don't hear about this if !check_function_bodies.)
In that way, they'll happen before funccache.c is asked to do
anything, so we don't encounter the underlying problem.  Even when
we do resolve the problem on the way to fixing plpython to support
polymorphics, this is a better code structure: it reports errors
sooner and takes some catalog lookups out of the normal function
call path.

Comments?

                        regards, tom lane

diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index 96bbdfb9586..42003ffd69b 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -458,3 +458,13 @@ ERROR:  error fetching next item from iterator
 DETAIL:  spiexceptions.InvalidDatetimeFormat: invalid value "xy" for "DD"
 CONTEXT:  Traceback (most recent call last):
 PL/Python function "python_error_detail"
+/* test the presently-unsupported case of polymorphic functions
+ */
+CREATE FUNCTION python_poly_ret(x anyelement) RETURNS anyelement AS $$
+  return x
+$$ LANGUAGE plpython3u;
+ERROR:  PL/Python functions cannot return type anyelement
+CREATE FUNCTION python_poly_arg(x anyelement) RETURNS int AS $$
+  return x
+$$ LANGUAGE plpython3u;
+ERROR:  PL/Python functions cannot accept type anyelement
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index df08d3fbd1b..2a9c2e6d85c 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -10,6 +10,7 @@
 #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"
@@ -17,7 +18,9 @@
 #include "plpy_plpymodule.h"
 #include "plpy_subxactobject.h"
 #include "plpy_util.h"
+#include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/syscache.h"
 
@@ -111,6 +114,11 @@ plpython3_validator(PG_FUNCTION_ARGS)
 	Oid			funcoid = PG_GETARG_OID(0);
 	HeapTuple	tuple;
 	Form_pg_proc procStruct;
+	Oid			rettype;
+	Oid		   *argtypes;
+	char	  **argnames,
+			   *argmodes;
+	int			nargs;
 	PLyTrigType is_trigger;
 	TriggerData trigdata;
 	EventTriggerData etrigdata;
@@ -120,19 +128,56 @@ plpython3_validator(PG_FUNCTION_ARGS)
 	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
 		PG_RETURN_VOID();
 
-	if (!check_function_bodies)
-		PG_RETURN_VOID();
-
 	/* Get the new function's pg_proc entry */
 	tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for function %u", funcoid);
 	procStruct = (Form_pg_proc) GETSTRUCT(tuple);
 
+	/* Disallow pseudotype result, except for void or record */
+	rettype = procStruct->prorettype;
+	if (get_typtype(rettype) == TYPTYPE_PSEUDO)
+	{
+		if (rettype == VOIDOID || rettype == RECORDOID)
+			 /* okay */ ;
+		else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
+			 /* okay */ ;
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("PL/Python functions cannot return type %s",
+							format_type_be(rettype))));
+	}
+
+	/* Disallow pseudotype arguments */
+	nargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes);
+	for (int i = 0; i < nargs; i++)
+	{
+		if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
+		{
+			if (argmodes &&
+				(argmodes[i] == PROARGMODE_OUT ||
+				 argmodes[i] == PROARGMODE_TABLE))
+				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]))));
+		}
+	}
+
 	is_trigger = PLy_procedure_is_trigger(procStruct);
 
 	ReleaseSysCache(tuple);
 
+	/* The rest is not needed/wanted if check_function_bodies is off */
+	if (!check_function_bodies)
+		PG_RETURN_VOID();
+
 	/*
 	 * Set up a fake flinfo/fcinfo with just enough info to satisfy
 	 * PLy_procedure_get().  That function derives the call context (plain
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index acfc0d8f0f0..ce8b1e099e9 100644
--- a/src/pl/plpython/plpy_procedure.c
+++ b/src/pl/plpython/plpy_procedure.c
@@ -217,36 +217,16 @@ PLy_procedure_create(PLyProcedure *proc,
 		if (is_trigger == PLPY_NOT_TRIGGER)
 		{
 			Oid			rettype = procStruct->prorettype;
-			HeapTuple	rvTypeTup;
-			Form_pg_type rvTypeStruct;
 
-			rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
-			if (!HeapTupleIsValid(rvTypeTup))
-				elog(ERROR, "cache lookup failed for type %u", rettype);
-			rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
-
-			/* Disallow pseudotype result, except for void or record */
-			if (rvTypeStruct->typtype == TYPTYPE_PSEUDO)
-			{
-				if (rettype == VOIDOID ||
-					rettype == RECORDOID)
-					 /* okay */ ;
-				else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							 errmsg("trigger functions can only be called as triggers")));
-				else
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							 errmsg("PL/Python functions cannot return type %s",
-									format_type_be(rettype))));
-			}
+			/* reject if function is in fact a trigger function */
+			if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("trigger functions can only be called as triggers")));
 
 			/* set up output function for procedure result */
 			PLy_output_setup_func(&proc->result, proc->mcxt,
 								  rettype, -1, proc);
-
-			ReleaseSysCache(rvTypeTup);
 		}
 		else
 		{
@@ -296,9 +276,6 @@ PLy_procedure_create(PLyProcedure *proc,
 
 			for (i = pos = 0; i < total; i++)
 			{
-				HeapTuple	argTypeTup;
-				Form_pg_type argTypeStruct;
-
 				if (modes &&
 					(modes[i] == PROARGMODE_OUT ||
 					 modes[i] == PROARGMODE_TABLE))
@@ -306,19 +283,6 @@ PLy_procedure_create(PLyProcedure *proc,
 
 				Assert(types[i] == procStruct->proargtypes.values[pos]);
 
-				argTypeTup = SearchSysCache1(TYPEOID,
-											 ObjectIdGetDatum(types[i]));
-				if (!HeapTupleIsValid(argTypeTup))
-					elog(ERROR, "cache lookup failed for type %u", types[i]);
-				argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
-
-				/* disallow pseudotype arguments */
-				if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							 errmsg("PL/Python functions cannot accept type %s",
-									format_type_be(types[i]))));
-
 				/* set up I/O function info */
 				PLy_input_setup_func(&proc->args[pos], proc->mcxt,
 									 types[i], -1,	/* typmod not known */
@@ -327,8 +291,6 @@ PLy_procedure_create(PLyProcedure *proc,
 				/* get argument name */
 				proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;
 
-				ReleaseSysCache(argTypeTup);
-
 				pos++;
 			}
 		}
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index 9bb1c0b085a..35dd16440c4 100644
--- a/src/pl/plpython/sql/plpython_error.sql
+++ b/src/pl/plpython/sql/plpython_error.sql
@@ -355,3 +355,13 @@ CREATE FUNCTION python_error_detail() RETURNS SETOF text AS $$
 $$ LANGUAGE plpython3u;
 
 SELECT python_error_detail();
+
+/* test the presently-unsupported case of polymorphic functions
+ */
+CREATE FUNCTION python_poly_ret(x anyelement) RETURNS anyelement AS $$
+  return x
+$$ LANGUAGE plpython3u;
+
+CREATE FUNCTION python_poly_arg(x anyelement) RETURNS int AS $$
+  return x
+$$ LANGUAGE plpython3u;

Reply via email to