130 lines
4.9 KiB
Diff
130 lines
4.9 KiB
Diff
From 6e29963edd55cfc038304322049d8fe697580f2c Mon Sep 17 00:00:00 2001
|
|
From: Tom Lane <tgl@sss.pgh.pa.us>
|
|
Date: Thu, 9 May 2024 13:16:21 -0400
|
|
Subject: [PATCH] Fix recursive RECORD-returning plpython functions.
|
|
|
|
If we recursed to a new call of the same function, with a different
|
|
coldeflist (AS clause), it would fail because the inner call would
|
|
overwrite the outer call's idea of what to return. This is vaguely
|
|
like 1d2fe56e4 and c5bec5426, but it's not due to any API decisions:
|
|
it's just that we computed the actual output rowtype at the start of
|
|
the call, and saved it in the per-procedure data structure. We can
|
|
fix it at basically zero cost by doing the computation at the end
|
|
of each call instead of the start.
|
|
|
|
It's not clear that there's any real-world use-case for such a
|
|
function, but given that it doesn't cost anything to fix,
|
|
it'd be silly not to.
|
|
|
|
Per report from Andreas Karlsson. Back-patch to all supported
|
|
branches.
|
|
|
|
Discussion: https://postgr.es/m/1651a46d-3c15-4028-a8c1-d74937b54e19@proxel.se
|
|
---
|
|
.../plpython/expected/plpython_composite.out | 14 ++++++++
|
|
src/pl/plpython/plpy_exec.c | 33 ++++++++++---------
|
|
src/pl/plpython/sql/plpython_composite.sql | 11 +++++++
|
|
3 files changed, 42 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out
|
|
index bb101e0..674af93 100644
|
|
--- a/src/pl/plpython/expected/plpython_composite.out
|
|
+++ b/src/pl/plpython/expected/plpython_composite.out
|
|
@@ -569,6 +569,20 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
|
|
1 | 2 | 3
|
|
(1 row)
|
|
|
|
+-- recursion with a different inner result type didn't use to work
|
|
+CREATE FUNCTION return_record_3(t text) RETURNS record AS $$
|
|
+if t == "text":
|
|
+ plpy.execute("SELECT * FROM return_record_3('int') AS (a int)");
|
|
+ return { "a": "x" }
|
|
+elif t == "int":
|
|
+ return { "a": 1 }
|
|
+$$ LANGUAGE plpython3u;
|
|
+SELECT * FROM return_record_3('text') AS (a text);
|
|
+ a
|
|
+---
|
|
+ x
|
|
+(1 row)
|
|
+
|
|
-- multi-dimensional array of composite types.
|
|
CREATE FUNCTION composite_type_as_list() RETURNS type_record[] AS $$
|
|
return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
|
|
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
|
|
index 993a4e2..517b1fe 100644
|
|
--- a/src/pl/plpython/plpy_exec.c
|
|
+++ b/src/pl/plpython/plpy_exec.c
|
|
@@ -231,7 +231,23 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
|
|
}
|
|
else
|
|
{
|
|
- /* Normal conversion of result */
|
|
+ /*
|
|
+ * Normal conversion of result. However, if the result is of type
|
|
+ * RECORD, we have to set up for that each time through, since it
|
|
+ * might be different from last time.
|
|
+ */
|
|
+ if (proc->result.typoid == RECORDOID)
|
|
+ {
|
|
+ TupleDesc desc;
|
|
+
|
|
+ if (get_call_result_type(fcinfo, NULL, &desc) != TYPEFUNC_COMPOSITE)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("function returning record called in context "
|
|
+ "that cannot accept type record")));
|
|
+ PLy_output_setup_record(&proc->result, desc, proc);
|
|
+ }
|
|
+
|
|
rv = PLy_output_convert(&proc->result, plrv,
|
|
&fcinfo->isnull);
|
|
}
|
|
@@ -450,21 +466,6 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc)
|
|
PLy_elog(ERROR, "PyDict_SetItemString() failed, while setting up arguments");
|
|
arg = NULL;
|
|
}
|
|
-
|
|
- /* Set up output conversion for functions returning RECORD */
|
|
- if (proc->result.typoid == RECORDOID)
|
|
- {
|
|
- TupleDesc desc;
|
|
-
|
|
- if (get_call_result_type(fcinfo, NULL, &desc) != TYPEFUNC_COMPOSITE)
|
|
- ereport(ERROR,
|
|
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
- errmsg("function returning record called in context "
|
|
- "that cannot accept type record")));
|
|
-
|
|
- /* cache the output conversion functions */
|
|
- PLy_output_setup_record(&proc->result, desc, proc);
|
|
- }
|
|
}
|
|
PG_CATCH();
|
|
{
|
|
diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql
|
|
index 2175770..1bb9b83 100644
|
|
--- a/src/pl/plpython/sql/plpython_composite.sql
|
|
+++ b/src/pl/plpython/sql/plpython_composite.sql
|
|
@@ -208,6 +208,17 @@ SELECT * FROM return_record_2('v4') AS (v1 int, v3 int, v2 int);
|
|
SELECT * FROM return_record_2('v3') AS (v1 int, v3 int, v2 int);
|
|
SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
|
|
|
|
+-- recursion with a different inner result type didn't use to work
|
|
+CREATE FUNCTION return_record_3(t text) RETURNS record AS $$
|
|
+if t == "text":
|
|
+ plpy.execute("SELECT * FROM return_record_3('int') AS (a int)");
|
|
+ return { "a": "x" }
|
|
+elif t == "int":
|
|
+ return { "a": 1 }
|
|
+$$ LANGUAGE plpython3u;
|
|
+
|
|
+SELECT * FROM return_record_3('text') AS (a text);
|
|
+
|
|
-- multi-dimensional array of composite types.
|
|
CREATE FUNCTION composite_type_as_list() RETURNS type_record[] AS $$
|
|
return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
|
|
--
|
|
2.27.0
|
|
|