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

Commit dc1503d

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 df27d76 commit dc1503d

File tree

6 files changed

+67
-17
lines changed

6 files changed

+67
-17
lines changed

src/backend/catalog/pg_proc.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -945,9 +945,10 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
945945
}
946946

947947
check_sql_fn_statements(querytree_list);
948-
(void) check_sql_fn_retval(funcoid, proc->prorettype,
949-
querytree_list,
950-
NULL, NULL);
948+
(void) check_sql_fn_retval_ext(funcoid, proc->prorettype,
949+
proc->prokind,
950+
querytree_list,
951+
NULL, NULL);
951952
}
952953

953954
error_context_stack = sqlerrcontext.previous;

src/backend/executor/functions.c

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -744,11 +744,12 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
744744
* coerce the returned rowtype to the desired form (unless the result type
745745
* is VOID, in which case there's nothing to coerce to).
746746
*/
747-
fcache->returnsTuple = check_sql_fn_retval(foid,
748-
rettype,
749-
flat_query_list,
750-
NULL,
751-
&fcache->junkFilter);
747+
fcache->returnsTuple = check_sql_fn_retval_ext(foid,
748+
rettype,
749+
procedureStruct->prokind,
750+
flat_query_list,
751+
NULL,
752+
&fcache->junkFilter);
752753

753754
if (fcache->returnsTuple)
754755
{
@@ -1590,6 +1591,20 @@ bool
15901591
check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
15911592
bool *modifyTargetList,
15921593
JunkFilter **junkFilter)
1594+
{
1595+
/* Wrapper function to preserve ABI compatibility in released branches */
1596+
return check_sql_fn_retval_ext(func_id, rettype,
1597+
PROKIND_FUNCTION,
1598+
queryTreeList,
1599+
modifyTargetList,
1600+
junkFilter);
1601+
}
1602+
1603+
bool
1604+
check_sql_fn_retval_ext(Oid func_id, Oid rettype, char prokind,
1605+
List *queryTreeList,
1606+
bool *modifyTargetList,
1607+
JunkFilter **junkFilter)
15931608
{
15941609
Query *parse;
15951610
List **tlist_ptr;
@@ -1608,7 +1623,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
16081623

16091624
/*
16101625
* If it's declared to return VOID, we don't care what's in the function.
1611-
* (This takes care of the procedure case, as well.)
1626+
* (This takes care of procedures with no output parameters, as well.)
16121627
*/
16131628
if (rettype == VOIDOID)
16141629
return false;
@@ -1753,8 +1768,13 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
17531768
* will succeed for any composite restype. For the moment we rely on
17541769
* runtime type checking to catch any discrepancy, but it'd be nice to
17551770
* do better at parse time.
1771+
*
1772+
* We must *not* do this for a procedure, however. Procedures with
1773+
* output parameter(s) have rettype RECORD, and the CALL code expects
1774+
* to get results corresponding to the list of output parameters, even
1775+
* when there's just one parameter that's composite.
17561776
*/
1757-
if (tlistlen == 1)
1777+
if (tlistlen == 1 && prokind != PROKIND_PROCEDURE)
17581778
{
17591779
TargetEntry *tle = (TargetEntry *) linitial(tlist);
17601780

src/backend/optimizer/util/clauses.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4624,8 +4624,9 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
46244624
* Note: we do not try this until we have verified that no rewriting was
46254625
* needed; that's probably not important, but let's be careful.
46264626
*/
4627-
if (check_sql_fn_retval(funcid, result_type, list_make1(querytree),
4628-
&modifyTargetList, NULL))
4627+
if (check_sql_fn_retval_ext(funcid, result_type, funcform->prokind,
4628+
list_make1(querytree),
4629+
&modifyTargetList, NULL))
46294630
goto fail; /* reject whole-tuple-result cases */
46304631

46314632
/* Now we can grab the tlist expression */
@@ -5149,9 +5150,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
51495150
* check_sql_fn_retval, we deliberately exclude domains over composite
51505151
* here.)
51515152
*/
5152-
if (!check_sql_fn_retval(func_oid, fexpr->funcresulttype,
5153-
querytree_list,
5154-
&modifyTargetList, NULL) &&
5153+
if (!check_sql_fn_retval_ext(func_oid, fexpr->funcresulttype,
5154+
funcform->prokind,
5155+
querytree_list,
5156+
&modifyTargetList, NULL) &&
51555157
(get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE ||
51565158
fexpr->funcresulttype == RECORDOID))
51575159
goto fail; /* reject not-whole-tuple-result cases */

src/include/executor/functions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ extern bool check_sql_fn_retval(Oid func_id, Oid rettype,
3636
bool *modifyTargetList,
3737
JunkFilter **junkFilter);
3838

39+
extern bool check_sql_fn_retval_ext(Oid func_id, Oid rettype,
40+
char prokind,
41+
List *queryTreeList,
42+
bool *modifyTargetList,
43+
JunkFilter **junkFilter);
44+
3945
extern DestReceiver *CreateSQLFunctionDestReceiver(void);
4046

4147
#endif /* FUNCTIONS_H */

src/test/regress/expected/create_procedure.out

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,19 @@ CALL ptest4a(a, b); -- error, not supported
106106
$$;
107107
ERROR: calling procedures with output arguments is not supported in SQL functions
108108
CONTEXT: SQL function "ptest4b"
109-
DROP PROCEDURE ptest4a;
109+
-- we used to get confused by a single output argument that is composite
110+
CREATE PROCEDURE ptest4c(INOUT comp int8_tbl)
111+
LANGUAGE SQL
112+
AS $$
113+
SELECT ROW(1, 2)::int8_tbl;
114+
$$;
115+
CALL ptest4c(NULL);
116+
comp
117+
-------
118+
(1,2)
119+
(1 row)
120+
121+
DROP PROCEDURE ptest4a, ptest4c;
110122
-- named and default parameters
111123
CREATE OR REPLACE PROCEDURE ptest5(a int, b text, c int default 100)
112124
LANGUAGE SQL

src/test/regress/sql/create_procedure.sql

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,16 @@ AS $$
6868
CALL ptest4a(a, b); -- error, not supported
6969
$$;
7070

71-
DROP PROCEDURE ptest4a;
71+
-- we used to get confused by a single output argument that is composite
72+
CREATE PROCEDURE ptest4c(INOUT comp int8_tbl)
73+
LANGUAGE SQL
74+
AS $$
75+
SELECT ROW(1, 2)::int8_tbl;
76+
$$;
77+
78+
CALL ptest4c(NULL);
79+
80+
DROP PROCEDURE ptest4a, ptest4c;
7281

7382

7483
-- named and default parameters

0 commit comments

Comments
 (0)