Cleaned patch with regress tests
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..8589c62ce1 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,80 @@ 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;
+							bool	found = false;
+
+							for (j = i + 1; j < pronargs; j++)
+								if (argarray[j])
+								{
+									found = true;
+									break;
+								}
+
+							if (found)
+								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;

Reply via email to