Skip to content
Open
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
66 changes: 66 additions & 0 deletions mysql-test/main/sp_named_params.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#
# 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: Undeclared variable: 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;
#
# End of tests
#
71 changes: 71 additions & 0 deletions mysql-test/main/sp_named_params.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
--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_UNDECLARED_VAR
CALL p1(a => 1, a => 2, b => 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 # End of tests
--echo #
79 changes: 77 additions & 2 deletions sql/sp_head.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2177,14 +2177,89 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (m_parent && m_parent->instantiate_if_needed(thd))
DBUG_RETURN(true);

if (args->elements < (params - default_params) ||
args->elements > params)
if (!thd->lex->has_named_call_param &&
(args->elements < (params - default_params) ||
args->elements > params))
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
ErrConvDQName(this).ptr(), params, args->elements);
DBUG_RETURN(TRUE);
}

/* Reorder named arguments to match formal parameter positions */
List<Item> reordered_args;
if (thd->lex->has_named_call_param)
{
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);

List_iterator<Item> it(*args);
uint positional_count= 0;
Item *item;

while ((item= it++))
{
if (item->is_explicit_name())
{
bool found= false;
for (uint j= 0; j < params; j++)
{
sp_variable *spvar= m_pcont->get_context_variable(j);
if (spvar->name.streq(item->name))
{
if (param_assigned[j])
{
my_error(ER_SP_UNDECLARED_VAR, 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 (param_assigned[positional_count])
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0),
m_pcont->get_context_variable(positional_count)->name.str);
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= m_pcont->get_context_variable(j);
if (!spvar->default_value)
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
ErrConvDQName(this).ptr(), params, args->elements);
DBUG_RETURN(TRUE);
}
arg_array[j]= spvar->default_value;
}
}

for (uint j= 0; j < params; j++)
reordered_args.push_back(arg_array[j], thd->mem_root);
args= &reordered_args;
}

save_spcont= octx= thd->spcont;
if (! octx)
{
Expand Down
2 changes: 2 additions & 0 deletions sql/sql_lex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10274,6 +10274,7 @@ bool LEX::call_statement_start(THD *thd, sp_name *name)
const Sp_handler *sph= &sp_handler_procedure;
sql_command= SQLCOM_CALL;
value_list.empty();
has_named_call_param= false;

thd->variables.path.resolve(thd, sphead, name, &sph, &pkgname);

Expand Down Expand Up @@ -10313,6 +10314,7 @@ bool LEX::call_statement_start(THD *thd,
Identifier_chain2 q_pkg_proc(*pkg, *proc);
sp_name *spname;
value_list.empty();
has_named_call_param= false;
sql_command= SQLCOM_CALL;

const Lex_ident_db_normalized dbn= thd->to_ident_db_normalized_with_error(*db);
Expand Down
1 change: 1 addition & 0 deletions sql/sql_lex.h
Original file line number Diff line number Diff line change
Expand Up @@ -3438,6 +3438,7 @@ struct LEX: public Query_tables_list
bool verbose:1, no_write_to_binlog:1;
bool safe_to_cache_query:1;
bool ignore:1;
bool has_named_call_param:1;
bool next_is_main:1; // use "main" SELECT_LEX for nrxt allocation;
bool next_is_down:1; // use "main" SELECT_LEX for nrxt allocation;
/*
Expand Down
24 changes: 22 additions & 2 deletions sql/sql_yacc.yy
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
boolean_test
predicate bit_expr parenthesized_expr
table_wild simple_expr column_default_non_parenthesized_expr udf_expr
sp_cparam
primary_expr string_factor_expr mysql_concatenation_expr
select_sublist_qualified_asterisk
expr_or_ignore expr_or_ignore_or_default
Expand Down Expand Up @@ -3498,16 +3499,35 @@ opt_sp_cparams:
;

sp_cparams:
sp_cparams ',' expr
sp_cparams ',' sp_cparam
{
($$= $1)->push_back($3, thd->mem_root);
}
| expr
| sp_cparam
{
($$= &Lex->value_list)->push_back($1, thd->mem_root);
}
;

sp_cparam:
expr
{
if (Lex->has_named_call_param)
{
thd->parse_error();
MYSQL_YYABORT;
}
$$= $1;
}
| ident ARROW_SYM expr
{
Lex->has_named_call_param= true;
$3->base_flags|= item_base_t::IS_EXPLICIT_NAME;
$3->set_name(thd, $1);
$$= $3;
}
;

/* Stored FUNCTION parameter declaration list */
sp_fdparam_list:
/* Empty */
Expand Down
Loading