Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 40d1bde

Browse files
committed
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
1 parent 539e328 commit 40d1bde

File tree

6 files changed

+69
-18
lines changed

6 files changed

+69
-18
lines changed

src/backend/catalog/pg_proc.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -959,9 +959,10 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
959959

960960
(void) get_func_result_type(funcoid, &rettype, &rettupdesc);
961961

962-
(void) check_sql_fn_retval(querytree_list,
963-
rettype, rettupdesc,
964-
false, NULL);
962+
(void) check_sql_fn_retval_ext(querytree_list,
963+
rettype, rettupdesc,
964+
proc->prokind,
965+
false, NULL);
965966
}
966967

967968
error_context_stack = sqlerrcontext.previous;

src/backend/executor/functions.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -743,11 +743,12 @@ init_sql_fcache(FunctionCallInfo fcinfo, Oid collation, bool lazyEvalOK)
743743
* the rowtype column into multiple columns, since we have no way to
744744
* notify the caller that it should do that.)
745745
*/
746-
fcache->returnsTuple = check_sql_fn_retval(queryTree_list,
747-
rettype,
748-
rettupdesc,
749-
false,
750-
&resulttlist);
746+
fcache->returnsTuple = check_sql_fn_retval_ext(queryTree_list,
747+
rettype,
748+
rettupdesc,
749+
procedureStruct->prokind,
750+
false,
751+
&resulttlist);
751752

752753
/*
753754
* Construct a JunkFilter we can use to coerce the returned rowtype to the
@@ -1608,6 +1609,21 @@ check_sql_fn_retval(List *queryTreeLists,
16081609
Oid rettype, TupleDesc rettupdesc,
16091610
bool insertDroppedCols,
16101611
List **resultTargetList)
1612+
{
1613+
/* Wrapper function to preserve ABI compatibility in released branches */
1614+
return check_sql_fn_retval_ext(queryTreeLists,
1615+
rettype, rettupdesc,
1616+
PROKIND_FUNCTION,
1617+
insertDroppedCols,
1618+
resultTargetList);
1619+
}
1620+
1621+
bool
1622+
check_sql_fn_retval_ext(List *queryTreeLists,
1623+
Oid rettype, TupleDesc rettupdesc,
1624+
char prokind,
1625+
bool insertDroppedCols,
1626+
List **resultTargetList)
16111627
{
16121628
bool is_tuple_result = false;
16131629
Query *parse;
@@ -1625,7 +1641,7 @@ check_sql_fn_retval(List *queryTreeLists,
16251641

16261642
/*
16271643
* If it's declared to return VOID, we don't care what's in the function.
1628-
* (This takes care of the procedure case, as well.)
1644+
* (This takes care of procedures with no output parameters, as well.)
16291645
*/
16301646
if (rettype == VOIDOID)
16311647
return false;
@@ -1780,8 +1796,13 @@ check_sql_fn_retval(List *queryTreeLists,
17801796
* or not the record type really matches. For the moment we rely on
17811797
* runtime type checking to catch any discrepancy, but it'd be nice to
17821798
* do better at parse time.
1799+
*
1800+
* We must *not* do this for a procedure, however. Procedures with
1801+
* output parameter(s) have rettype RECORD, and the CALL code expects
1802+
* to get results corresponding to the list of output parameters, even
1803+
* when there's just one parameter that's composite.
17831804
*/
1784-
if (tlistlen == 1)
1805+
if (tlistlen == 1 && prokind != PROKIND_PROCEDURE)
17851806
{
17861807
TargetEntry *tle = (TargetEntry *) linitial(tlist);
17871808

src/backend/optimizer/util/clauses.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4708,9 +4708,10 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
47084708
* needed; that's probably not important, but let's be careful.
47094709
*/
47104710
querytree_list = list_make1(querytree);
4711-
if (check_sql_fn_retval(list_make1(querytree_list),
4712-
result_type, rettupdesc,
4713-
false, NULL))
4711+
if (check_sql_fn_retval_ext(list_make1(querytree_list),
4712+
result_type, rettupdesc,
4713+
funcform->prokind,
4714+
false, NULL))
47144715
goto fail; /* reject whole-tuple-result cases */
47154716

47164717
/*
@@ -5254,9 +5255,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
52545255
* shows it's returning a whole tuple result; otherwise what it's
52555256
* returning is a single composite column which is not what we need.
52565257
*/
5257-
if (!check_sql_fn_retval(list_make1(querytree_list),
5258-
fexpr->funcresulttype, rettupdesc,
5259-
true, NULL) &&
5258+
if (!check_sql_fn_retval_ext(list_make1(querytree_list),
5259+
fexpr->funcresulttype, rettupdesc,
5260+
funcform->prokind,
5261+
true, NULL) &&
52605262
(functypclass == TYPEFUNC_COMPOSITE ||
52615263
functypclass == TYPEFUNC_COMPOSITE_DOMAIN ||
52625264
functypclass == TYPEFUNC_RECORD))

src/include/executor/functions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ extern bool check_sql_fn_retval(List *queryTreeLists,
5050
bool insertDroppedCols,
5151
List **resultTargetList);
5252

53+
extern bool check_sql_fn_retval_ext(List *queryTreeLists,
54+
Oid rettype, TupleDesc rettupdesc,
55+
char prokind,
56+
bool insertDroppedCols,
57+
List **resultTargetList);
58+
5359
extern DestReceiver *CreateSQLFunctionDestReceiver(void);
5460

5561
#endif /* FUNCTIONS_H */

src/test/regress/expected/create_procedure.out

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,19 @@ CALL ptest4a(a, b); -- error, not supported
148148
$$;
149149
ERROR: calling procedures with output arguments is not supported in SQL functions
150150
CONTEXT: SQL function "ptest4b"
151-
DROP PROCEDURE ptest4a;
151+
-- we used to get confused by a single output argument that is composite
152+
CREATE PROCEDURE ptest4c(INOUT comp int8_tbl)
153+
LANGUAGE SQL
154+
AS $$
155+
SELECT ROW(1, 2);
156+
$$;
157+
CALL ptest4c(NULL);
158+
comp
159+
-------
160+
(1,2)
161+
(1 row)
162+
163+
DROP PROCEDURE ptest4a, ptest4c;
152164
-- named and default parameters
153165
CREATE OR REPLACE PROCEDURE ptest5(a int, b text, c int default 100)
154166
LANGUAGE SQL

src/test/regress/sql/create_procedure.sql

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,16 @@ AS $$
9090
CALL ptest4a(a, b); -- error, not supported
9191
$$;
9292

93-
DROP PROCEDURE ptest4a;
93+
-- we used to get confused by a single output argument that is composite
94+
CREATE PROCEDURE ptest4c(INOUT comp int8_tbl)
95+
LANGUAGE SQL
96+
AS $$
97+
SELECT ROW(1, 2);
98+
$$;
99+
100+
CALL ptest4c(NULL);
101+
102+
DROP PROCEDURE ptest4a, ptest4c;
94103

95104

96105
-- named and default parameters

0 commit comments

Comments
 (0)