aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-03-12 18:16:10 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2024-03-12 18:16:10 -0400
commit28184f039e3364a4a8e341dcbd713372689728db (patch)
treede5610dabd2c87681b2919df6c7e8eefc8444b95 /src
parentad5cd55e675c6b55a521d9db5e57bba5a02a9208 (diff)
downloadpostgresql-28184f039e3364a4a8e341dcbd713372689728db.tar.gz
postgresql-28184f039e3364a4a8e341dcbd713372689728db.zip
Fix confusion about the return rowtype of SQL-language procedures.
There is a very ancient hack in check_sql_fn_retval that allows a single SELECT targetlist entry of composite type to be taken as supplying all the output columns of a function returning composite. (This is grotty and fundamentally ambiguous, but it's really hard to do nested composite-returning functions without it.) As far as I know, that doesn't cause any problems in ordinary functions. It's disastrous for procedures however. All procedures that have any output parameters are labeled with prorettype RECORD, and the CALL code expects it will get back a record with one column per output parameter, regardless of whether any of those parameters is composite. Doing something else leads to an assertion failure or core dump. This is simple enough to fix: we just need to not apply that rule when considering procedures. However, that requires adding another argument to check_sql_fn_retval, which at least in principle might be getting called by external callers. Therefore, in the back branches convert check_sql_fn_retval into an ABI-preserving wrapper around a new function check_sql_fn_retval_ext. Per report from Yahor Yuzefovich. This has been broken since we implemented procedures, so back-patch to all supported branches. Discussion: https://postgr.es/m/CABz5gWHSjj2df6uG0NRiDhZ_Uz=Y8t0FJP-_SVSsRsnrQT76Gg@mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/pg_proc.c7
-rw-r--r--src/backend/executor/functions.c35
-rw-r--r--src/backend/optimizer/util/clauses.c14
-rw-r--r--src/include/executor/functions.h6
-rw-r--r--src/test/regress/expected/create_procedure.out14
-rw-r--r--src/test/regress/sql/create_procedure.sql11
6 files changed, 69 insertions, 18 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 3041b443d6a..0d754874c1e 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -926,9 +926,10 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
(void) get_func_result_type(funcoid, &rettype, &rettupdesc);
- (void) check_sql_fn_retval(querytree_list,
- rettype, rettupdesc,
- false, NULL);
+ (void) check_sql_fn_retval_ext(querytree_list,
+ rettype, rettupdesc,
+ proc->prokind,
+ false, NULL);
}
error_context_stack = sqlerrcontext.previous;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index d7f41d5aed0..982155e740c 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -733,11 +733,12 @@ init_sql_fcache(FunctionCallInfo fcinfo, Oid collation, bool lazyEvalOK)
* the rowtype column into multiple columns, since we have no way to
* notify the caller that it should do that.)
*/
- fcache->returnsTuple = check_sql_fn_retval(queryTree_list,
- rettype,
- rettupdesc,
- false,
- &resulttlist);
+ fcache->returnsTuple = check_sql_fn_retval_ext(queryTree_list,
+ rettype,
+ rettupdesc,
+ procedureStruct->prokind,
+ false,
+ &resulttlist);
/*
* Construct a JunkFilter we can use to coerce the returned rowtype to the
@@ -1617,6 +1618,21 @@ check_sql_fn_retval(List *queryTreeLists,
bool insertDroppedCols,
List **resultTargetList)
{
+ /* Wrapper function to preserve ABI compatibility in released branches */
+ return check_sql_fn_retval_ext(queryTreeLists,
+ rettype, rettupdesc,
+ PROKIND_FUNCTION,
+ insertDroppedCols,
+ resultTargetList);
+}
+
+bool
+check_sql_fn_retval_ext(List *queryTreeLists,
+ Oid rettype, TupleDesc rettupdesc,
+ char prokind,
+ bool insertDroppedCols,
+ List **resultTargetList)
+{
bool is_tuple_result = false;
Query *parse;
ListCell *parse_cell;
@@ -1633,7 +1649,7 @@ check_sql_fn_retval(List *queryTreeLists,
/*
* If it's declared to return VOID, we don't care what's in the function.
- * (This takes care of the procedure case, as well.)
+ * (This takes care of procedures with no output parameters, as well.)
*/
if (rettype == VOIDOID)
return false;
@@ -1787,8 +1803,13 @@ check_sql_fn_retval(List *queryTreeLists,
* or not the record type really matches. For the moment we rely on
* runtime type checking to catch any discrepancy, but it'd be nice to
* do better at parse time.
+ *
+ * We must *not* do this for a procedure, however. Procedures with
+ * output parameter(s) have rettype RECORD, and the CALL code expects
+ * to get results corresponding to the list of output parameters, even
+ * when there's just one parameter that's composite.
*/
- if (tlistlen == 1)
+ if (tlistlen == 1 && prokind != PROKIND_PROCEDURE)
{
TargetEntry *tle = (TargetEntry *) linitial(tlist);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 4ad66b9594d..85532cab031 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4617,9 +4617,10 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
* needed; that's probably not important, but let's be careful.
*/
querytree_list = list_make1(querytree);
- if (check_sql_fn_retval(list_make1(querytree_list),
- result_type, rettupdesc,
- false, NULL))
+ if (check_sql_fn_retval_ext(list_make1(querytree_list),
+ result_type, rettupdesc,
+ funcform->prokind,
+ false, NULL))
goto fail; /* reject whole-tuple-result cases */
/*
@@ -5140,9 +5141,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* shows it's returning a whole tuple result; otherwise what it's
* returning is a single composite column which is not what we need.
*/
- if (!check_sql_fn_retval(list_make1(querytree_list),
- fexpr->funcresulttype, rettupdesc,
- true, NULL) &&
+ if (!check_sql_fn_retval_ext(list_make1(querytree_list),
+ fexpr->funcresulttype, rettupdesc,
+ funcform->prokind,
+ true, NULL) &&
(functypclass == TYPEFUNC_COMPOSITE ||
functypclass == TYPEFUNC_COMPOSITE_DOMAIN ||
functypclass == TYPEFUNC_RECORD))
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index a0db24bde69..c24c157fea2 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -36,6 +36,12 @@ extern bool check_sql_fn_retval(List *queryTreeLists,
bool insertDroppedCols,
List **resultTargetList);
+extern bool check_sql_fn_retval_ext(List *queryTreeLists,
+ Oid rettype, TupleDesc rettupdesc,
+ char prokind,
+ bool insertDroppedCols,
+ List **resultTargetList);
+
extern DestReceiver *CreateSQLFunctionDestReceiver(void);
#endif /* FUNCTIONS_H */
diff --git a/src/test/regress/expected/create_procedure.out b/src/test/regress/expected/create_procedure.out
index 211a42cefa0..215abcc8c79 100644
--- a/src/test/regress/expected/create_procedure.out
+++ b/src/test/regress/expected/create_procedure.out
@@ -106,7 +106,19 @@ CALL ptest4a(a, b); -- error, not supported
$$;
ERROR: calling procedures with output arguments is not supported in SQL functions
CONTEXT: SQL function "ptest4b"
-DROP PROCEDURE ptest4a;
+-- we used to get confused by a single output argument that is composite
+CREATE PROCEDURE ptest4c(INOUT comp int8_tbl)
+LANGUAGE SQL
+AS $$
+SELECT ROW(1, 2);
+$$;
+CALL ptest4c(NULL);
+ comp
+-------
+ (1,2)
+(1 row)
+
+DROP PROCEDURE ptest4a, ptest4c;
-- named and default parameters
CREATE OR REPLACE PROCEDURE ptest5(a int, b text, c int default 100)
LANGUAGE SQL
diff --git a/src/test/regress/sql/create_procedure.sql b/src/test/regress/sql/create_procedure.sql
index 89b96d580ff..127120278c0 100644
--- a/src/test/regress/sql/create_procedure.sql
+++ b/src/test/regress/sql/create_procedure.sql
@@ -68,7 +68,16 @@ AS $$
CALL ptest4a(a, b); -- error, not supported
$$;
-DROP PROCEDURE ptest4a;
+-- we used to get confused by a single output argument that is composite
+CREATE PROCEDURE ptest4c(INOUT comp int8_tbl)
+LANGUAGE SQL
+AS $$
+SELECT ROW(1, 2);
+$$;
+
+CALL ptest4c(NULL);
+
+DROP PROCEDURE ptest4a, ptest4c;
-- named and default parameters