Skip to content
/ server Public
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
105 changes: 105 additions & 0 deletions mysql-test/main/call_named_param.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# MDEV-38329 CALL with named parameters.
# Create procedure with 2 params
CREATE PROCEDURE p1(a INT, b INT) SELECT a, b;
# Positional call
CALL p1(1, 2);
a b
1 2
# Named call - same order as declaration
CALL p1(a => 10, b => 20);
a b
10 20
# Named call - reversed order (tests resolution)
CALL p1(b => 200, a => 100);
a b
100 200
# Mixed: positional first, then named
CALL p1(100, b => 200);
a b
100 200
# User variables as named parameters
SET @v1= 5, @v2= 10;
CALL p1(a => @v1, b => @v2);
a b
5 10
# Expressions as named parameters
CALL p1(a => 1+1, b => 3*4);
a b
2 12
# Error: unknown parameter names
CALL p1(x => 0, y => 3);
ERROR 42000: Undeclared variable: x
# 3-param procedure
CREATE PROCEDURE p2(x INT, y INT, z INT) SELECT x, y, z;
# Named call all 3
CALL p2(x => 1, y => 2, z => 3);
x y z
1 2 3
# Named out-of-order
CALL p2(z => 30, x => 10, y => 20);
x y z
10 20 30
# Mixed: 1 positional + 2 named
CALL p2(1, z => 30, y => 20);
x y z
1 20 30
DROP PROCEDURE p1;
DROP PROCEDURE p2;
# Error: unknown parameter name
CREATE PROCEDURE p3(a INT) SELECT a;
CALL p3(x => 1);
ERROR 42000: Undeclared variable: x
# Error: duplicate parameter
CALL p3(a => 1, a => 2);
ERROR 42000: Duplicate parameter: a
DROP PROCEDURE p3;
# OUT parameters: named call out-of-order, writeback to correct variables
CREATE PROCEDURE p_out(OUT a INT, OUT b INT)
BEGIN
SELECT 10, 20 INTO a, b;
END|
CALL p_out(b => @bout, a => @aout);
SELECT @aout, @bout;
@aout @bout
10 20
DROP PROCEDURE p_out;
# INOUT parameters with named call
CREATE PROCEDURE p_inout(INOUT x INT, INOUT y INT)
BEGIN
SET x= x * 10, y= y + 100;
END|
SET @xin= 1, @yin= 2;
CALL p_inout(y => @yin, x => @xin);
SELECT @xin, @yin;
@xin @yin
10 102
DROP PROCEDURE p_inout;
# Default parameters: omitted params get default value
CREATE PROCEDURE p_def(a INT, b INT DEFAULT 10) SELECT a, b;
# Positional: one arg, second gets default
CALL p_def(1);
a b
1 10
# Positional: two args
CALL p_def(1, 2);
a b
1 2
# Named: omit b, b gets default (2e)
CALL p_def(a => 5);
a b
5 10
# Named: both given
CALL p_def(a => 1, b => 20);
a b
1 20
# Named: reversed order, give both
CALL p_def(b => 100, a => 7);
a b
7 100
DROP PROCEDURE p_def;
# Error: required param omitted (no default)
CREATE PROCEDURE p_req(a INT, b INT) SELECT a, b;
CALL p_req(a => 1);
ERROR 42000: Incorrect number of arguments for PROCEDURE test.p_req; expected 2, got 1
DROP PROCEDURE p_req;
# End of 13.0 tests
101 changes: 101 additions & 0 deletions mysql-test/main/call_named_param.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# MDEV-38329 CALL with named parameters.

--echo # MDEV-38329 CALL with named parameters.
--echo # Create procedure with 2 params
CREATE PROCEDURE p1(a INT, b INT) SELECT a, b;

--echo # Positional call
CALL p1(1, 2);

--echo # Named call - same order as declaration
CALL p1(a => 10, b => 20);

--echo # Named call - reversed order (tests resolution)
CALL p1(b => 200, a => 100);

--echo # Mixed: positional first, then named
CALL p1(100, b => 200);

--echo # User variables as named parameters
SET @v1= 5, @v2= 10;
CALL p1(a => @v1, b => @v2);

--echo # Expressions as named parameters
CALL p1(a => 1+1, b => 3*4);

--echo # Error: unknown parameter names
--error ER_SP_UNDECLARED_VAR
CALL p1(x => 0, y => 3);

--echo # 3-param procedure
CREATE PROCEDURE p2(x INT, y INT, z INT) SELECT x, y, z;

--echo # Named call all 3
CALL p2(x => 1, y => 2, z => 3);

--echo # Named out-of-order
CALL p2(z => 30, x => 10, y => 20);

--echo # Mixed: 1 positional + 2 named
CALL p2(1, z => 30, y => 20);

DROP PROCEDURE p1;
DROP PROCEDURE p2;

--echo # Error: unknown parameter name
CREATE PROCEDURE p3(a INT) SELECT a;

--error ER_SP_UNDECLARED_VAR
CALL p3(x => 1);

--echo # Error: duplicate parameter
--error ER_SP_DUP_PARAM
CALL p3(a => 1, a => 2);

DROP PROCEDURE p3;

--echo # OUT parameters: named call out-of-order, writeback to correct variables
delimiter |;
CREATE PROCEDURE p_out(OUT a INT, OUT b INT)
BEGIN
SELECT 10, 20 INTO a, b;
END|
delimiter ;|
CALL p_out(b => @bout, a => @aout);
SELECT @aout, @bout;
DROP PROCEDURE p_out;

--echo # INOUT parameters with named call
delimiter |;
CREATE PROCEDURE p_inout(INOUT x INT, INOUT y INT)
BEGIN
SET x= x * 10, y= y + 100;
END|
delimiter ;|
SET @xin= 1, @yin= 2;
CALL p_inout(y => @yin, x => @xin);
SELECT @xin, @yin;
DROP PROCEDURE p_inout;

--echo # Default parameters: omitted params get default value
CREATE PROCEDURE p_def(a INT, b INT DEFAULT 10) SELECT a, b;

--echo # Positional: one arg, second gets default
CALL p_def(1);
--echo # Positional: two args
CALL p_def(1, 2);
--echo # Named: omit b, b gets default (2e)
CALL p_def(a => 5);
--echo # Named: both given
CALL p_def(a => 1, b => 20);
--echo # Named: reversed order, give both
CALL p_def(b => 100, a => 7);
DROP PROCEDURE p_def;

--echo # Error: required param omitted (no default)
CREATE PROCEDURE p_req(a INT, b INT) SELECT a, b;
--error ER_SP_WRONG_NO_OF_ARGS
CALL p_req(a => 1);
DROP PROCEDURE p_req;

--echo # End of 13.0 tests
44 changes: 44 additions & 0 deletions mysql-test/run_call_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/sh
# Run tests relevant to CALL / named params. Run from repo root.
# Usage: ./mysql-test/run_call_tests.sh [--quick|--main|--main-ps|--push]

set -e
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
MTR="$ROOT/build/mysql-test/mtr"

if [ ! -x "$MTR" ]; then
echo "Build or MTR not found. Run: cd build && ninja"
exit 1
fi

cd "$ROOT/build/mysql-test"

case "${1:-}" in
--quick)
echo "=== Quick: call_named_param (default + ps-protocol) ==="
./mtr --force call_named_param
./mtr --force --ps-protocol call_named_param
;;
--main)
echo "=== Main suite ==="
./mtr --force --suite-timeout=120 --max-test-fail=10 --retry=3 --suite=main
;;
--main-ps)
echo "=== Main suite with --ps-protocol ==="
./mtr --force --ps-protocol --suite-timeout=120 --max-test-fail=10 --retry=3 --suite=main
;;
--push)
echo "=== default.push: n_mix ==="
./mtr --timer --force --parallel=auto --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --skip-test-list=collections/disabled-per-push.list
echo "=== default.push: ps_row ==="
./mtr --timer --force --parallel=auto --comment=ps_row --vardir=var-ps_row --ps-protocol --mysqld=--binlog-format=row --skip-test-list=collections/disabled-per-push.list
;;
*)
echo "Usage: $0 --quick | --main | --main-ps | --push"
echo " --quick call_named_param only (default + ps-protocol)"
echo " --main full main suite"
echo " --main-ps main suite with prepared statements"
echo " --push first two default.push runs (n_mix, ps_row)"
exit 1
;;
esac
15 changes: 13 additions & 2 deletions sql/sp_head.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2237,10 +2237,21 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)

for (uint i= 0 ; i < params ; i++)
{
Item *arg_item= it_args++;

Item *arg_item;
if (i < args->elements)
arg_item= it_args++;
else
{
sp_variable *spvar= m_pcont->get_context_variable(i);
arg_item= spvar->default_value;
}
if (!arg_item)
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
ErrConvDQName(this).ptr(), params, args->elements);
err_status= TRUE;
break;
}

err_status= bind_input_param(thd, arg_item, i, octx, nctx, FALSE);
if (err_status)
Expand Down
8 changes: 6 additions & 2 deletions sql/sp_rcontext.cc
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,13 @@ bool Row_definition_list::
List_iterator<Item> it_args(*args);
DBUG_ASSERT(elements >= args->elements );
Spvar_definition *def;
Item *arg;
while ((def= it++) && (arg= it_args++))
uint i= 0;
while ((def= it++))
{
Item *arg= (i < args->elements) ? it_args++ : nullptr;
i++;
if (!arg)
continue;
if (def->type_handler()->adjust_spparam_type(def, arg))
return true;
}
Expand Down
20 changes: 20 additions & 0 deletions sql/sql_lex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,7 @@ void LEX::start(THD *thd_arg)
update_list.empty();
set_var_list.empty();
param_list.empty();
call_param_list.empty();
view_list.empty();
with_persistent_for_clause= FALSE;
column_list= NULL;
Expand Down Expand Up @@ -10274,6 +10275,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();
call_param_list.empty();

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

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

const Lex_ident_db_normalized dbn= thd->to_ident_db_normalized_with_error(*db);
Expand Down Expand Up @@ -10377,12 +10380,29 @@ bool LEX::call_statement_start_or_lvalue_assign(THD *thd,
}


void LEX::build_value_list_from_call_params(THD *thd)
{
if (call_param_list.elements == 0)
return;
value_list.empty();
List_iterator_fast<Call_param> it(call_param_list);
Call_param *cp;
while ((cp= it++))
value_list.push_back(cp->value, thd->mem_root);
}


bool LEX::direct_call(THD *thd, const Qualified_ident *ident,
List<Item> *args)
{
DBUG_ASSERT(ident);
if (!ident->spvar())
return false; // A procedure call
/*
Populate value_list from call_param_list so that args is ready for
SP variable method calls such as assoc_array_var.delete('key').
*/
build_value_list_from_call_params(thd);

/*
ident->part(0) is a known SP variable.
Expand Down
8 changes: 8 additions & 0 deletions sql/sql_lex.h
Original file line number Diff line number Diff line change
Expand Up @@ -3305,6 +3305,12 @@ struct LEX: public Query_tables_list
Table_type table_type; /* Used for SHOW CREATE */
List<Key_part_spec> ref_list;
List<LEX_USER> users_list;
/** One argument to CALL: optional parameter name (empty if positional) and value. */
struct Call_param {
LEX_CSTRING name; /* empty (str==NULL or length==0) if positional */
Item *value;
};
List<Call_param> call_param_list;
List<Item> *insert_list= nullptr,field_list,value_list,update_list;
List<List_item> many_values;
List<set_var_base> var_list;
Expand Down Expand Up @@ -3997,6 +4003,8 @@ struct LEX: public Query_tables_list
bool call_statement_start(THD *thd, const Qualified_ident *ident);
bool call_statement_start_or_lvalue_assign(THD *thd,
Qualified_ident *ident);
/** Build value_list from call_param_list (for execution/prepare). No-op if call_param_list is empty. */
void build_value_list_from_call_params(THD *thd);
/*
Create instructions for a direct call (without the CALL keyword):
sp1; - a schema procedure call
Expand Down
Loading