Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions mysql-test/main/sp_named_params.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#
# MDEV-38329: Named Parameters in Invocation of Stored Routines
#
# Test setup
CREATE PROCEDURE p1(a INT, b INT, c INT)
BEGIN
SELECT a, b, c;
END;
$$
# All positional (existing behavior)
CALL p1(1, 2, 3);
a b c
1 2 3
# All named
CALL p1(a => 1, b => 2, c => 3);
a b c
1 2 3
# Named in different order
CALL p1(c => 3, a => 1, b => 2);
a b c
1 2 3
# Mixed positional and named
CALL p1(1, b => 2, c => 3);
a b c
1 2 3
# Mixed: first two positional, last named
CALL p1(1, 2, c => 3);
a b c
1 2 3
# Positional after named should fail
CALL p1(a => 1, 2, 3);
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ' 3)' at line 1
# Unknown parameter name
CALL p1(a => 1, b => 2, x => 3);
ERROR 42000: Undeclared variable: x
# Duplicate parameter name
CALL p1(a => 1, a => 2, b => 3);
ERROR 42000: Duplicate parameter: a
# Positional fills 'a', then 'a' again by name
CALL p1(1, a => 2, c => 3);
ERROR 42000: Duplicate parameter: a
DROP PROCEDURE p1;
#
# Test with default values
#
CREATE PROCEDURE p2(a INT, b INT DEFAULT 20, c INT DEFAULT 30)
BEGIN
SELECT a, b, c;
END;
$$
# Skip middle param (use default for b)
CALL p2(a => 1, c => 3);
a b c
1 20 3
# Only required param
CALL p2(a => 1);
a b c
1 20 30
# All named with defaults overridden
CALL p2(a => 10, b => 20, c => 30);
a b c
10 20 30
# Missing required param should fail
CALL p2(b => 2, c => 3);
ERROR 42000: Incorrect number of arguments for PROCEDURE test.p2; expected 3, got 2
DROP PROCEDURE p2;
#
# Stored functions with named parameters via AS syntax
#
CREATE FUNCTION f1(a INT, b INT, c INT) RETURNS INT
BEGIN
RETURN a * 10000 + b * 100 + c;
END;
$$
# Positional function call
SELECT f1(1, 2, 3);
f1(1, 2, 3)
10203
# All named via AS
SELECT f1(1 AS a, 2 AS b, 3 AS c);
f1(1 AS a, 2 AS b, 3 AS c)
10203
# Named in different order via AS
SELECT f1(3 AS c, 1 AS a, 2 AS b);
f1(3 AS c, 1 AS a, 2 AS b)
10203
# Mixed positional and named
SELECT f1(1, 2 AS b, 3 AS c);
f1(1, 2 AS b, 3 AS c)
10203
# Unknown parameter name
SELECT f1(1 AS a, 2 AS b, 3 AS x);
ERROR 42000: Undeclared variable: x
# Duplicate parameter name
SELECT f1(1 AS a, 2 AS a, 3 AS b);
ERROR 42000: Duplicate parameter: a
DROP FUNCTION f1;
#
# Stored function with default values
#
CREATE FUNCTION f2(a INT, b INT DEFAULT 20, c INT DEFAULT 30) RETURNS INT
BEGIN
RETURN a * 10000 + b * 100 + c;
END;
$$
# Skip middle param (use default for b)
SELECT f2(1 AS a, 3 AS c);
f2(1 AS a, 3 AS c)
12003
# Only required param
SELECT f2(1 AS a);
f2(1 AS a)
12030
# Missing required param should fail
SELECT f2(2 AS b, 3 AS c);
ERROR 42000: Incorrect number of arguments for FUNCTION test.f2; expected 3, got 2
DROP FUNCTION f2;
#
# End of tests
#
131 changes: 131 additions & 0 deletions mysql-test/main/sp_named_params.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
--echo #
--echo # MDEV-38329: Named Parameters in Invocation of Stored Routines
--echo #

--echo # Test setup
delimiter $$;
CREATE PROCEDURE p1(a INT, b INT, c INT)
BEGIN
SELECT a, b, c;
END;
$$
delimiter ;$$

--echo # All positional (existing behavior)
CALL p1(1, 2, 3);

--echo # All named
CALL p1(a => 1, b => 2, c => 3);

--echo # Named in different order
CALL p1(c => 3, a => 1, b => 2);

--echo # Mixed positional and named
CALL p1(1, b => 2, c => 3);

--echo # Mixed: first two positional, last named
CALL p1(1, 2, c => 3);

--echo # Positional after named should fail
--error ER_PARSE_ERROR
CALL p1(a => 1, 2, 3);

--echo # Unknown parameter name
--error ER_SP_UNDECLARED_VAR
CALL p1(a => 1, b => 2, x => 3);

--echo # Duplicate parameter name
--error ER_SP_DUP_PARAM
CALL p1(a => 1, a => 2, b => 3);

--echo # Positional fills 'a', then 'a' again by name
--error ER_SP_DUP_PARAM
CALL p1(1, a => 2, c => 3);

DROP PROCEDURE p1;

--echo #
--echo # Test with default values
--echo #
delimiter $$;
CREATE PROCEDURE p2(a INT, b INT DEFAULT 20, c INT DEFAULT 30)
BEGIN
SELECT a, b, c;
END;
$$
delimiter ;$$

--echo # Skip middle param (use default for b)
CALL p2(a => 1, c => 3);

--echo # Only required param
CALL p2(a => 1);

--echo # All named with defaults overridden
CALL p2(a => 10, b => 20, c => 30);

--echo # Missing required param should fail
--error ER_SP_WRONG_NO_OF_ARGS
CALL p2(b => 2, c => 3);

DROP PROCEDURE p2;

--echo #
--echo # Stored functions with named parameters via AS syntax
--echo #
delimiter $$;
CREATE FUNCTION f1(a INT, b INT, c INT) RETURNS INT
BEGIN
RETURN a * 10000 + b * 100 + c;
END;
$$
delimiter ;$$

--echo # Positional function call
SELECT f1(1, 2, 3);

--echo # All named via AS
SELECT f1(1 AS a, 2 AS b, 3 AS c);

--echo # Named in different order via AS
SELECT f1(3 AS c, 1 AS a, 2 AS b);

--echo # Mixed positional and named
SELECT f1(1, 2 AS b, 3 AS c);

--echo # Unknown parameter name
--error ER_SP_UNDECLARED_VAR
SELECT f1(1 AS a, 2 AS b, 3 AS x);

--echo # Duplicate parameter name
--error ER_SP_DUP_PARAM
SELECT f1(1 AS a, 2 AS a, 3 AS b);

DROP FUNCTION f1;

--echo #
--echo # Stored function with default values
--echo #
delimiter $$;
CREATE FUNCTION f2(a INT, b INT DEFAULT 20, c INT DEFAULT 30) RETURNS INT
BEGIN
RETURN a * 10000 + b * 100 + c;
END;
$$
delimiter ;$$

--echo # Skip middle param (use default for b)
SELECT f2(1 AS a, 3 AS c);

--echo # Only required param
SELECT f2(1 AS a);

--echo # Missing required param should fail
--error ER_SP_WRONG_NO_OF_ARGS
SELECT f2(2 AS b, 3 AS c);

DROP FUNCTION f2;

--echo #
--echo # End of tests
--echo #
15 changes: 0 additions & 15 deletions sql/item_create.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2996,21 +2996,6 @@ Create_sp_func::create_with_db(THD *thd,
const Sp_handler *sph= &sp_handler_function;
Database_qualified_name pkgname;

if (unlikely(has_named_parameters(item_list)))
{
/*
The syntax "db.foo(expr AS p1, expr AS p2, ...) is invalid,
and has been rejected during syntactic parsing already,
because a stored function call may not have named parameters.

The syntax "foo(expr AS p1, expr AS p2, ...)" is correct,
because it can refer to a User Defined Function call.
For a Stored Function however, this has no semantic.
*/
my_error(ER_WRONG_PARAMETERS_TO_STORED_FCT, MYF(0), name.str);
return NULL;
}

if (item_list != NULL)
arg_count= item_list->elements;

Expand Down
71 changes: 71 additions & 0 deletions sql/item_func.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6884,6 +6884,77 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
DBUG_RETURN(TRUE);
}

if (arg_count && args[0]->is_explicit_name())
{
sp_pcontext *pcont= m_sp->get_parse_context();
uint params= pcont->context_var_count();
Item **arg_array= (Item**) thd->calloc(sizeof(Item*) * params);
bool *param_assigned= (bool*) thd->calloc(sizeof(bool) * params);
if (!arg_array || !param_assigned)
DBUG_RETURN(TRUE);

uint positional_count= 0;
for (uint i= 0; i < arg_count; i++)
{
Item *item= args[i];
if (item->is_explicit_name())
{
bool found= false;
for (uint j= 0; j < params; j++)
{
sp_variable *spvar= pcont->get_context_variable(j);
if (spvar->name.streq(item->name))
{
if (param_assigned[j])
{
my_error(ER_SP_DUP_PARAM, MYF(0), item->name.str);
DBUG_RETURN(TRUE);
}
arg_array[j]= item;
param_assigned[j]= true;
found= true;
break;
}
}
if (!found)
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), item->name.str);
DBUG_RETURN(TRUE);
}
}
else
{
if (positional_count >= params)
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "FUNCTION",
ErrConvDQName(m_sp).ptr(), params, arg_count);
DBUG_RETURN(TRUE);
}
arg_array[positional_count]= item;
param_assigned[positional_count]= true;
positional_count++;
}
}

for (uint j= 0; j < params; j++)
{
if (!param_assigned[j])
{
sp_variable *spvar= pcont->get_context_variable(j);
if (!spvar->default_value)
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "FUNCTION",
ErrConvDQName(m_sp).ptr(), params, arg_count);
DBUG_RETURN(TRUE);
}
arg_array[j]= spvar->default_value;
}
}

args= arg_array;
arg_count= params;
}

Query_arena *arena, backup;
/*
Allocation an instance of Item_func_sp used for initialization of
Expand Down
Loading
Loading