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

Commit 70ffb27

Browse files
committed
Fix handling of polymorphic output arguments for procedures.
Most of the infrastructure for procedure arguments was already okay with polymorphic output arguments, but it turns out that CallStmtResultDesc() was a few bricks shy of a load here. It thought all it needed to do was call build_function_result_tupdesc_t, but that function specifically disclaims responsibility for resolving polymorphic arguments. Failing to handle that doesn't seem to be a problem for CALL in plpgsql, but CALL from plain SQL would get errors like "cannot display a value of type anyelement", or even crash outright. In v14 and later we can simply examine the exposed types of the CallStmt.outargs nodes to get the right type OIDs. But it's a lot more complicated to fix in v12/v13, because those versions don't have CallStmt.outargs, nor do they do expand_function_arguments until ExecuteCallStmt runs. We have to duplicatively run expand_function_arguments, and then re-determine which elements of the args list are output arguments. Per bug #18463 from Drew Kimball. Back-patch to all supported versions, since it's busted in all of them. Discussion: https://postgr.es/m/18463-f8cd77e12564d8a2@postgresql.org
1 parent 2812059 commit 70ffb27

File tree

5 files changed

+189
-0
lines changed

5 files changed

+189
-0
lines changed

src/backend/commands/functioncmds.c

+73
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "executor/executor.h"
5555
#include "funcapi.h"
5656
#include "miscadmin.h"
57+
#include "nodes/nodeFuncs.h"
5758
#include "optimizer/optimizer.h"
5859
#include "parser/parse_coerce.h"
5960
#include "parser/parse_collate.h"
@@ -2506,6 +2507,78 @@ CallStmtResultDesc(CallStmt *stmt)
25062507

25072508
tupdesc = build_function_result_tupdesc_t(tuple);
25082509

2510+
/*
2511+
* The result of build_function_result_tupdesc_t has the right column
2512+
* names, but it just has the declared output argument types, which is the
2513+
* wrong thing in polymorphic cases. Get the correct types by examining
2514+
* the procedure's resolved argument expressions. We intentionally keep
2515+
* the atttypmod as -1 and the attcollation as the type's default, since
2516+
* that's always the appropriate thing for function outputs; there's no
2517+
* point in considering any additional info available from outargs. Note
2518+
* that tupdesc is null if there are no outargs.
2519+
*/
2520+
if (tupdesc)
2521+
{
2522+
Datum proargmodes;
2523+
bool isnull;
2524+
ArrayType *arr;
2525+
char *argmodes;
2526+
int nargs,
2527+
noutargs;
2528+
ListCell *lc;
2529+
2530+
/*
2531+
* Expand named arguments, defaults, etc. We do not want to scribble
2532+
* on the passed-in CallStmt parse tree, so first flat-copy fexpr,
2533+
* allowing us to replace its args field. (Note that
2534+
* expand_function_arguments will not modify any of the passed-in data
2535+
* structure.)
2536+
*/
2537+
{
2538+
FuncExpr *nexpr = makeNode(FuncExpr);
2539+
2540+
memcpy(nexpr, fexpr, sizeof(FuncExpr));
2541+
fexpr = nexpr;
2542+
}
2543+
2544+
fexpr->args = expand_function_arguments(fexpr->args,
2545+
fexpr->funcresulttype,
2546+
tuple);
2547+
2548+
/*
2549+
* If we're here, build_function_result_tupdesc_t already validated
2550+
* that the procedure has non-null proargmodes that is the right kind
2551+
* of array, so it seems unnecessary to check again.
2552+
*/
2553+
proargmodes = SysCacheGetAttr(PROCOID, tuple,
2554+
Anum_pg_proc_proargmodes,
2555+
&isnull);
2556+
Assert(!isnull);
2557+
arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
2558+
argmodes = (char *) ARR_DATA_PTR(arr);
2559+
2560+
nargs = noutargs = 0;
2561+
foreach(lc, fexpr->args)
2562+
{
2563+
Node *arg = (Node *) lfirst(lc);
2564+
Form_pg_attribute att = TupleDescAttr(tupdesc, noutargs);
2565+
char argmode = argmodes[nargs++];
2566+
2567+
/* ignore non-out arguments */
2568+
if (argmode == PROARGMODE_IN ||
2569+
argmode == PROARGMODE_VARIADIC)
2570+
continue;
2571+
2572+
TupleDescInitEntry(tupdesc,
2573+
++noutargs,
2574+
NameStr(att->attname),
2575+
exprType(arg),
2576+
-1,
2577+
0);
2578+
}
2579+
Assert(tupdesc->natts == noutargs);
2580+
}
2581+
25092582
ReleaseSysCache(tuple);
25102583

25112584
return tupdesc;

src/pl/plpgsql/src/expected/plpgsql_call.out

+34
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,40 @@ END
302302
$$;
303303
ERROR: procedure parameter "c" is an output parameter but corresponding argument is not writable
304304
CONTEXT: PL/pgSQL function inline_code_block line 5 at CALL
305+
-- polymorphic OUT arguments
306+
CREATE PROCEDURE test_proc12(a anyelement, INOUT b anyelement, INOUT c anyarray)
307+
LANGUAGE plpgsql
308+
AS $$
309+
BEGIN
310+
RAISE NOTICE 'a: %', a;
311+
b := a;
312+
c := array[a];
313+
END;
314+
$$;
315+
DO $$
316+
DECLARE _a int; _b int; _c int[];
317+
BEGIN
318+
_a := 10;
319+
CALL test_proc12(_a, _b, _c);
320+
RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
321+
END
322+
$$;
323+
NOTICE: a: 10
324+
NOTICE: _a: 10, _b: 10, _c: {10}
325+
DO $$
326+
DECLARE _a int; _b int; _c text[];
327+
BEGIN
328+
_a := 10;
329+
CALL test_proc12(_a, _b, _c); -- error
330+
RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
331+
END
332+
$$;
333+
ERROR: procedure test_proc12(integer, integer, text[]) does not exist
334+
LINE 1: CALL test_proc12(_a, _b, _c)
335+
^
336+
HINT: No procedure matches the given name and argument types. You might need to add explicit type casts.
337+
QUERY: CALL test_proc12(_a, _b, _c)
338+
CONTEXT: PL/pgSQL function inline_code_block line 5 at CALL
305339
-- transition variable assignment
306340
TRUNCATE test1;
307341
CREATE FUNCTION triggerfunc1() RETURNS trigger

src/pl/plpgsql/src/sql/plpgsql_call.sql

+30
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,36 @@ BEGIN
278278
END
279279
$$;
280280

281+
-- polymorphic OUT arguments
282+
283+
CREATE PROCEDURE test_proc12(a anyelement, INOUT b anyelement, INOUT c anyarray)
284+
LANGUAGE plpgsql
285+
AS $$
286+
BEGIN
287+
RAISE NOTICE 'a: %', a;
288+
b := a;
289+
c := array[a];
290+
END;
291+
$$;
292+
293+
DO $$
294+
DECLARE _a int; _b int; _c int[];
295+
BEGIN
296+
_a := 10;
297+
CALL test_proc12(_a, _b, _c);
298+
RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
299+
END
300+
$$;
301+
302+
DO $$
303+
DECLARE _a int; _b int; _c text[];
304+
BEGIN
305+
_a := 10;
306+
CALL test_proc12(_a, _b, _c); -- error
307+
RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
308+
END
309+
$$;
310+
281311

282312
-- transition variable assignment
283313

src/test/regress/expected/create_procedure.out

+34
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,40 @@ AS $$
151151
SELECT NULL::int;
152152
$$;
153153
CALL ptest6(1, 2);
154+
CREATE PROCEDURE ptest6a(inout a anyelement, inout b anyelement)
155+
LANGUAGE SQL
156+
AS $$
157+
SELECT $1, $1;
158+
$$;
159+
CALL ptest6a(1, null);
160+
a | b
161+
---+---
162+
1 | 1
163+
(1 row)
164+
165+
CALL ptest6a(1.1, null);
166+
a | b
167+
-----+-----
168+
1.1 | 1.1
169+
(1 row)
170+
171+
CREATE PROCEDURE ptest6b(a anyelement, inout b anyelement, inout c anyarray)
172+
LANGUAGE SQL
173+
AS $$
174+
SELECT $1, array[$1];
175+
$$;
176+
CALL ptest6b(1, null, null);
177+
b | c
178+
---+-----
179+
1 | {1}
180+
(1 row)
181+
182+
CALL ptest6b(1.1, null, null);
183+
b | c
184+
-----+-------
185+
1.1 | {1.1}
186+
(1 row)
187+
154188
-- collation assignment
155189
CREATE PROCEDURE ptest7(a text, b text)
156190
LANGUAGE SQL

src/test/regress/sql/create_procedure.sql

+18
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,24 @@ $$;
109109

110110
CALL ptest6(1, 2);
111111

112+
CREATE PROCEDURE ptest6a(inout a anyelement, inout b anyelement)
113+
LANGUAGE SQL
114+
AS $$
115+
SELECT $1, $1;
116+
$$;
117+
118+
CALL ptest6a(1, null);
119+
CALL ptest6a(1.1, null);
120+
121+
CREATE PROCEDURE ptest6b(a anyelement, inout b anyelement, inout c anyarray)
122+
LANGUAGE SQL
123+
AS $$
124+
SELECT $1, array[$1];
125+
$$;
126+
127+
CALL ptest6b(1, null, null);
128+
CALL ptest6b(1.1, null, null);
129+
112130

113131
-- collation assignment
114132

0 commit comments

Comments
 (0)