čt 1. 11. 2018 v 14:31 odesílatel Pavel Stehule <[email protected]>
napsal:
> Cleaned patch with regress tests
>
>
minor cleaning
Regards
Pavel
diff --git a/src/pl/plpgsql/src/expected/plpgsql_call.out b/src/pl/plpgsql/src/expected/plpgsql_call.out
index 547ca22a55..8762e1335c 100644
--- a/src/pl/plpgsql/src/expected/plpgsql_call.out
+++ b/src/pl/plpgsql/src/expected/plpgsql_call.out
@@ -276,3 +276,43 @@ DROP PROCEDURE test_proc1;
DROP PROCEDURE test_proc3;
DROP PROCEDURE test_proc4;
DROP TABLE test1;
+CREATE TABLE test_call_proc(key serial, name text);
+CREATE OR REPLACE PROCEDURE p1(v_cnt int, v_ResultSet inout refcursor = NULL)
+AS $$
+BEGIN
+ INSERT INTO test_call_proc(name) VALUES('name test');
+ OPEN v_ResultSet FOR SELECT * FROM test_call_proc;
+END
+$$ LANGUAGE plpgsql;
+DO $$
+DECLARE
+ v_ResultSet refcursor;
+ v_cnt integer;
+BEGIN
+ CALL p1(v_cnt:=v_cnt, v_ResultSet := v_ResultSet);
+ RAISE NOTICE '%', v_ResultSet;
+END;
+$$;
+NOTICE: <unnamed portal 1>
+DO $$
+DECLARE
+ v_ResultSet refcursor;
+ v_cnt integer;
+BEGIN
+ CALL p1(10, v_ResultSet := v_ResultSet);
+ RAISE NOTICE '%', v_ResultSet;
+END;
+$$;
+NOTICE: <unnamed portal 2>
+DO $$
+DECLARE
+ v_ResultSet refcursor;
+ v_cnt integer;
+BEGIN
+ CALL p1(v_ResultSet := v_ResultSet, v_cnt:=v_cnt);
+ RAISE NOTICE '%', v_ResultSet;
+END;
+$$;
+NOTICE: <unnamed portal 3>
+DROP PROCEDURE p1;
+DROP TABLE test_call_proc;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 45526383f2..c737a66c54 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -2168,10 +2168,10 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
*/
if (!stmt->target)
{
+ Form_pg_proc funcform;
Node *node;
ListCell *lc;
FuncExpr *funcexpr;
- int i;
HeapTuple tuple;
Oid *argtypes;
char **argnames;
@@ -2179,6 +2179,7 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
MemoryContext oldcontext;
PLpgSQL_row *row;
int nfields;
+ int pronargs;
/*
* Get the original CallStmt
@@ -2196,6 +2197,8 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", funcexpr->funcid);
get_func_arg_info(tuple, &argtypes, &argnames, &argmodes);
+ funcform = (Form_pg_proc) GETSTRUCT(tuple);
+ pronargs = funcform->pronargs;
ReleaseSysCache(tuple);
/*
@@ -2210,45 +2213,74 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
row->varnos = palloc(sizeof(int) * FUNC_MAX_ARGS);
nfields = 0;
- i = 0;
- foreach(lc, funcexpr->args)
+
+ /*
+ * The argmodes can be in different order than funcexpr->args due
+ * named args. When we should to check INOUT parameters and prepare
+ * target variable, we should to reorder a arguments first. This is
+ * similar code to reorder_function_arguments. In this part a default
+ * values are not necessary.
+ */
+ if (argmodes)
{
- Node *n = lfirst(lc);
+ Node *argarray[FUNC_MAX_ARGS];
+ int nargsprovided = list_length(funcexpr->args);
+ int i = 0;
+
+ MemSet(argarray, 0, pronargs * sizeof(Node *));
- if (argmodes && argmodes[i] == PROARGMODE_INOUT)
+ foreach(lc, funcexpr->args)
{
- if (IsA(n, Param))
- {
- Param *param = castNode(Param, n);
+ Node *n = lfirst(lc);
- /* paramid is offset by 1 (see make_datum_param()) */
- row->varnos[nfields++] = param->paramid - 1;
- }
- else if (IsA(n, NamedArgExpr))
+ if (IsA(n, NamedArgExpr))
{
NamedArgExpr *nexpr = castNode(NamedArgExpr, n);
- Param *param;
+ argarray[nexpr->argnumber] = (Node *) nexpr->arg;
+ }
+ else
+ argarray[i++] = n;
+ }
- if (!IsA(nexpr->arg, Param))
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("argument %d is an output argument but is not writable", i + 1)));
+ Assert(nargsprovided <= pronargs);
- param = castNode(Param, nexpr->arg);
+ for (i = 0; i < pronargs; i++)
+ {
+ Node *n = argarray[i];
+ if (argmodes[i] == PROARGMODE_INOUT)
+ {
/*
- * Named arguments must be after positional arguments,
- * so we can increase nfields.
+ * Empty positions are related to default values. The INOUT defaults
+ * are allowed, only if after are not any other parameter.
*/
- row->varnos[nexpr->argnumber] = param->paramid - 1;
- nfields++;
+ if (!n)
+ {
+ int j;
+
+ for (j = i + 1; j < pronargs; j++)
+ {
+ if (argarray[j])
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("argument %i with default values is output argument but it is not writeable", i + 1)));
+
+ /* there are not any other parameter */
+ break;
+ }
+ else if (IsA(n, Param))
+ {
+ Param *param = castNode(Param, n);
+
+ /* paramid is offset by 1 (see make_datum_param()) */
+ row->varnos[nfields++] = param->paramid - 1;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("argument %d is an output argument but is not writable", i + 1)));
}
- else
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("argument %d is an output argument but is not writable", i + 1)));
}
- i++;
}
row->nfields = nfields;
diff --git a/src/pl/plpgsql/src/sql/plpgsql_call.sql b/src/pl/plpgsql/src/sql/plpgsql_call.sql
index 29e85803e7..2a5070c8f6 100644
--- a/src/pl/plpgsql/src/sql/plpgsql_call.sql
+++ b/src/pl/plpgsql/src/sql/plpgsql_call.sql
@@ -251,3 +251,48 @@ DROP PROCEDURE test_proc3;
DROP PROCEDURE test_proc4;
DROP TABLE test1;
+
+
+CREATE TABLE test_call_proc(key serial, name text);
+
+CREATE OR REPLACE PROCEDURE p1(v_cnt int, v_ResultSet inout refcursor = NULL)
+AS $$
+BEGIN
+ INSERT INTO test_call_proc(name) VALUES('name test');
+ OPEN v_ResultSet FOR SELECT * FROM test_call_proc;
+END
+$$ LANGUAGE plpgsql;
+
+DO $$
+DECLARE
+ v_ResultSet refcursor;
+ v_cnt integer;
+BEGIN
+ CALL p1(v_cnt:=v_cnt, v_ResultSet := v_ResultSet);
+ RAISE NOTICE '%', v_ResultSet;
+END;
+$$;
+
+DO $$
+DECLARE
+ v_ResultSet refcursor;
+ v_cnt integer;
+BEGIN
+ CALL p1(10, v_ResultSet := v_ResultSet);
+ RAISE NOTICE '%', v_ResultSet;
+END;
+$$;
+
+DO $$
+DECLARE
+ v_ResultSet refcursor;
+ v_cnt integer;
+BEGIN
+ CALL p1(v_ResultSet := v_ResultSet, v_cnt:=v_cnt);
+ RAISE NOTICE '%', v_ResultSet;
+END;
+$$;
+
+DROP PROCEDURE p1;
+
+DROP TABLE test_call_proc;