Re: BUG #18463: Possible bug in stored procedures with polymorphic OUT parameters
От | Tom Lane |
---|---|
Тема | Re: BUG #18463: Possible bug in stored procedures with polymorphic OUT parameters |
Дата | |
Msg-id | 1631943.1715731231@sss.pgh.pa.us обсуждение исходный текст |
Ответ на | Re: BUG #18463: Possible bug in stored procedures with polymorphic OUT parameters (Tom Lane <tgl@sss.pgh.pa.us>) |
Ответы |
Re: BUG #18463: Possible bug in stored procedures with polymorphic OUT parameters
|
Список | pgsql-bugs |
I wrote: > Some experimentation showed that we need to return the correct > output column names in this tupdesc, so continuing to use > build_function_result_tupdesc_t seems like the easiest path for that. > However, stmt->outargs does hold nodes of the correct resolved data > types, so overwriting the atttypid's from that produces a nicely > small patch, as attached. Bleah ... that works fine back to v14, but in 12 and 13 it falls down because there's no outargs list in CallStmt. We can look at stmt->funcexpr->args instead, but (a) we have to rediscover which elements of that are output args, and (b) that list hasn't been run through expand_function_arguments, so we have to do that an extra time. (b) is pretty annoying, since the work is 100% duplicative of what's about to happen in ExecuteCallStmt. I thought briefly about moving the expand_function_arguments call from execution to transformCallStmt, the way it's done in v14 and later. I'm afraid to do that though, because it seems just barely possible that there's third-party code that knows that that list hasn't been expanded in these old branches. I fear we just have to eat the additional cycles. They're not that much compared to what will happen to run a user-defined procedure, but still, bleah. So I end with the attached modification for 12/13. regards, tom lane diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index c24a85dc8c..6fa414c5fa 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -55,6 +55,7 @@ #include "executor/executor.h" #include "funcapi.h" #include "miscadmin.h" +#include "nodes/nodeFuncs.h" #include "optimizer/optimizer.h" #include "parser/parse_coerce.h" #include "parser/parse_collate.h" @@ -2327,6 +2328,78 @@ CallStmtResultDesc(CallStmt *stmt) tupdesc = build_function_result_tupdesc_t(tuple); + /* + * The result of build_function_result_tupdesc_t has the right column + * names, but it just has the declared output argument types, which is the + * wrong thing in polymorphic cases. Get the correct types by examining + * the procedure's resolved argument expressions. We intentionally keep + * the atttypmod as -1 and the attcollation as the type's default, since + * that's always the appropriate thing for function outputs; there's no + * point in considering any additional info available from outargs. Note + * that tupdesc is null if there are no outargs. + */ + if (tupdesc) + { + Datum proargmodes; + bool isnull; + ArrayType *arr; + char *argmodes; + int nargs, + noutargs; + ListCell *lc; + + /* + * Expand named arguments, defaults, etc. We do not want to scribble + * on the passed-in CallStmt parse tree, so first flat-copy fexpr, + * allowing us to replace its args field. (Note that + * expand_function_arguments will not modify any of the passed-in data + * structure.) + */ + { + FuncExpr *nexpr = makeNode(FuncExpr); + + memcpy(nexpr, fexpr, sizeof(FuncExpr)); + fexpr = nexpr; + } + + fexpr->args = expand_function_arguments(fexpr->args, + fexpr->funcresulttype, + tuple); + + /* + * If we're here, build_function_result_tupdesc_t already validated + * that the procedure has non-null proargmodes that is the right kind + * of array, so it seems unnecessary to check again. + */ + proargmodes = SysCacheGetAttr(PROCOID, tuple, + Anum_pg_proc_proargmodes, + &isnull); + Assert(!isnull); + arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ + argmodes = (char *) ARR_DATA_PTR(arr); + + nargs = noutargs = 0; + foreach(lc, fexpr->args) + { + Node *arg = (Node *) lfirst(lc); + Form_pg_attribute att = TupleDescAttr(tupdesc, noutargs); + char argmode = argmodes[nargs++]; + + /* ignore non-out arguments */ + if (argmode == PROARGMODE_IN || + argmode == PROARGMODE_VARIADIC) + continue; + + TupleDescInitEntry(tupdesc, + ++noutargs, + NameStr(att->attname), + exprType(arg), + -1, + 0); + } + Assert(tupdesc->natts == noutargs); + } + ReleaseSysCache(tuple); return tupdesc; diff --git a/src/pl/plpgsql/src/expected/plpgsql_call.out b/src/pl/plpgsql/src/expected/plpgsql_call.out index f92e108366..83d5973135 100644 --- a/src/pl/plpgsql/src/expected/plpgsql_call.out +++ b/src/pl/plpgsql/src/expected/plpgsql_call.out @@ -302,6 +302,40 @@ END $$; ERROR: procedure parameter "c" is an output parameter but corresponding argument is not writable CONTEXT: PL/pgSQL function inline_code_block line 5 at CALL +-- polymorphic OUT arguments +CREATE PROCEDURE test_proc12(a anyelement, INOUT b anyelement, INOUT c anyarray) +LANGUAGE plpgsql +AS $$ +BEGIN + RAISE NOTICE 'a: %', a; + b := a; + c := array[a]; +END; +$$; +DO $$ +DECLARE _a int; _b int; _c int[]; +BEGIN + _a := 10; + CALL test_proc12(_a, _b, _c); + RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c; +END +$$; +NOTICE: a: 10 +NOTICE: _a: 10, _b: 10, _c: {10} +DO $$ +DECLARE _a int; _b int; _c text[]; +BEGIN + _a := 10; + CALL test_proc12(_a, _b, _c); -- error + RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c; +END +$$; +ERROR: procedure test_proc12(integer, integer, text[]) does not exist +LINE 1: CALL test_proc12(_a, _b, _c) + ^ +HINT: No procedure matches the given name and argument types. You might need to add explicit type casts. +QUERY: CALL test_proc12(_a, _b, _c) +CONTEXT: PL/pgSQL function inline_code_block line 5 at CALL -- transition variable assignment TRUNCATE test1; CREATE FUNCTION triggerfunc1() RETURNS trigger diff --git a/src/pl/plpgsql/src/sql/plpgsql_call.sql b/src/pl/plpgsql/src/sql/plpgsql_call.sql index d3194e44c0..6825f59dbc 100644 --- a/src/pl/plpgsql/src/sql/plpgsql_call.sql +++ b/src/pl/plpgsql/src/sql/plpgsql_call.sql @@ -278,6 +278,36 @@ BEGIN END $$; +-- polymorphic OUT arguments + +CREATE PROCEDURE test_proc12(a anyelement, INOUT b anyelement, INOUT c anyarray) +LANGUAGE plpgsql +AS $$ +BEGIN + RAISE NOTICE 'a: %', a; + b := a; + c := array[a]; +END; +$$; + +DO $$ +DECLARE _a int; _b int; _c int[]; +BEGIN + _a := 10; + CALL test_proc12(_a, _b, _c); + RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c; +END +$$; + +DO $$ +DECLARE _a int; _b int; _c text[]; +BEGIN + _a := 10; + CALL test_proc12(_a, _b, _c); -- error + RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c; +END +$$; + -- transition variable assignment diff --git a/src/test/regress/expected/create_procedure.out b/src/test/regress/expected/create_procedure.out index 215abcc8c7..8bf5f9a362 100644 --- a/src/test/regress/expected/create_procedure.out +++ b/src/test/regress/expected/create_procedure.out @@ -151,6 +151,40 @@ AS $$ SELECT NULL::int; $$; CALL ptest6(1, 2); +CREATE PROCEDURE ptest6a(inout a anyelement, inout b anyelement) +LANGUAGE SQL +AS $$ +SELECT $1, $1; +$$; +CALL ptest6a(1, null); + a | b +---+--- + 1 | 1 +(1 row) + +CALL ptest6a(1.1, null); + a | b +-----+----- + 1.1 | 1.1 +(1 row) + +CREATE PROCEDURE ptest6b(a anyelement, inout b anyelement, inout c anyarray) +LANGUAGE SQL +AS $$ +SELECT $1, array[$1]; +$$; +CALL ptest6b(1, null, null); + b | c +---+----- + 1 | {1} +(1 row) + +CALL ptest6b(1.1, null, null); + b | c +-----+------- + 1.1 | {1.1} +(1 row) + -- collation assignment CREATE PROCEDURE ptest7(a text, b text) LANGUAGE SQL diff --git a/src/test/regress/sql/create_procedure.sql b/src/test/regress/sql/create_procedure.sql index 127120278c..ec32656c15 100644 --- a/src/test/regress/sql/create_procedure.sql +++ b/src/test/regress/sql/create_procedure.sql @@ -109,6 +109,24 @@ $$; CALL ptest6(1, 2); +CREATE PROCEDURE ptest6a(inout a anyelement, inout b anyelement) +LANGUAGE SQL +AS $$ +SELECT $1, $1; +$$; + +CALL ptest6a(1, null); +CALL ptest6a(1.1, null); + +CREATE PROCEDURE ptest6b(a anyelement, inout b anyelement, inout c anyarray) +LANGUAGE SQL +AS $$ +SELECT $1, array[$1]; +$$; + +CALL ptest6b(1, null, null); +CALL ptest6b(1.1, null, null); + -- collation assignment
В списке pgsql-bugs по дате отправления:
Предыдущее
От: Melanie PlagemanДата:
Сообщение: Re: relfrozenxid may disagree with row XIDs after 1ccc1e05ae