mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
Fixed BUG#9598: stored procedure call within stored procedure
overwrites IN variable and added error checking of variables for [IN]OUT parameters while rewriting the out parameter handling. mysql-test/r/sp-error.result: New test case for non-variable argument for [IN]OUT parameters. (And changed to qualified names in some other error messages.) mysql-test/r/sp.result: New test case for BUG#9598. mysql-test/t/sp-error.test: New test case for non-variable argument for [IN]OUT parameters. mysql-test/t/sp.test: New test case for BUG#9598. sql/item.h: Need to distinguish between SP local variable items and other items, for error checking and [IN]OUT parameter handling. sql/share/errmsg.txt: New error message for non-variable arguments for [IN]OUT parameters in stored procedures. sql/sp_head.cc: Rewrote the [IN]OUT parameter handling in procedure invokation, to make it work properly when using user variables in sub-calls. Also added error checking for non-variable arguments for such parameters (and changed to qualified names for wrong number of arg. errors). sql/sp_rcontext.cc: No need to keep track on the out index for an [IN]OUT parameter any more. sql/sp_rcontext.h: No need to keep track on the out index for an [IN]OUT parameter any more.
This commit is contained in:
@@ -125,13 +125,13 @@ set @x = x|
|
|||||||
create function f(x int) returns int
|
create function f(x int) returns int
|
||||||
return x+42|
|
return x+42|
|
||||||
call p()|
|
call p()|
|
||||||
ERROR 42000: Incorrect number of arguments for PROCEDURE p; expected 1, got 0
|
ERROR 42000: Incorrect number of arguments for PROCEDURE test.p; expected 1, got 0
|
||||||
call p(1, 2)|
|
call p(1, 2)|
|
||||||
ERROR 42000: Incorrect number of arguments for PROCEDURE p; expected 1, got 2
|
ERROR 42000: Incorrect number of arguments for PROCEDURE test.p; expected 1, got 2
|
||||||
select f()|
|
select f()|
|
||||||
ERROR 42000: Incorrect number of arguments for FUNCTION f; expected 1, got 0
|
ERROR 42000: Incorrect number of arguments for FUNCTION test.f; expected 1, got 0
|
||||||
select f(1, 2)|
|
select f(1, 2)|
|
||||||
ERROR 42000: Incorrect number of arguments for FUNCTION f; expected 1, got 2
|
ERROR 42000: Incorrect number of arguments for FUNCTION test.f; expected 1, got 2
|
||||||
drop procedure p|
|
drop procedure p|
|
||||||
drop function f|
|
drop function f|
|
||||||
create procedure p(val int, out res int)
|
create procedure p(val int, out res int)
|
||||||
@@ -318,6 +318,24 @@ select field from t1;
|
|||||||
label L1;
|
label L1;
|
||||||
end|
|
end|
|
||||||
ERROR HY000: GOTO is not allowed in a stored procedure handler
|
ERROR HY000: GOTO is not allowed in a stored procedure handler
|
||||||
|
drop procedure if exists p|
|
||||||
|
create procedure p(in x int, inout y int, out z int)
|
||||||
|
begin
|
||||||
|
set y = x+y;
|
||||||
|
set z = x+y;
|
||||||
|
end|
|
||||||
|
set @tmp_x = 42|
|
||||||
|
set @tmp_y = 3|
|
||||||
|
set @tmp_z = 0|
|
||||||
|
call p(@tmp_x, @tmp_y, @tmp_z)|
|
||||||
|
select @tmp_x, @tmp_y, @tmp_z|
|
||||||
|
@tmp_x @tmp_y @tmp_z
|
||||||
|
42 45 87
|
||||||
|
call p(42, 43, @tmp_z)|
|
||||||
|
ERROR 42000: OUT or INOUT argument 2 for routine test.p is not a variable
|
||||||
|
call p(42, @tmp_y, 43)|
|
||||||
|
ERROR 42000: OUT or INOUT argument 3 for routine test.p is not a variable
|
||||||
|
drop procedure p|
|
||||||
create procedure bug1965()
|
create procedure bug1965()
|
||||||
begin
|
begin
|
||||||
declare c cursor for select val from t1 order by valname;
|
declare c cursor for select val from t1 order by valname;
|
||||||
|
@@ -2908,4 +2908,32 @@ v/10
|
|||||||
10.00000
|
10.00000
|
||||||
drop procedure bug9674_1|
|
drop procedure bug9674_1|
|
||||||
drop procedure bug9674_2|
|
drop procedure bug9674_2|
|
||||||
|
drop procedure if exists bug9598_1|
|
||||||
|
drop procedure if exists bug9598_2|
|
||||||
|
create procedure bug9598_1(in var_1 char(16),
|
||||||
|
out var_2 integer, out var_3 integer)
|
||||||
|
begin
|
||||||
|
set var_2 = 50;
|
||||||
|
set var_3 = 60;
|
||||||
|
end|
|
||||||
|
create procedure bug9598_2(in v1 char(16),
|
||||||
|
in v2 integer,
|
||||||
|
in v3 integer,
|
||||||
|
in v4 integer,
|
||||||
|
in v5 integer)
|
||||||
|
begin
|
||||||
|
select v1,v2,v3,v4,v5;
|
||||||
|
call bug9598_1(v1,@tmp1,@tmp2);
|
||||||
|
select v1,v2,v3,v4,v5;
|
||||||
|
end|
|
||||||
|
call bug9598_2('Test',2,3,4,5)|
|
||||||
|
v1 v2 v3 v4 v5
|
||||||
|
Test 2 3 4 5
|
||||||
|
v1 v2 v3 v4 v5
|
||||||
|
Test 2 3 4 5
|
||||||
|
select @tmp1, @tmp2|
|
||||||
|
@tmp1 @tmp2
|
||||||
|
50 60
|
||||||
|
drop procedure bug9598_1|
|
||||||
|
drop procedure bug9598_2|
|
||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
|
@@ -410,6 +410,31 @@ begin
|
|||||||
label L1;
|
label L1;
|
||||||
end|
|
end|
|
||||||
|
|
||||||
|
# Check in and inout arguments.
|
||||||
|
--disable_warnings
|
||||||
|
drop procedure if exists p|
|
||||||
|
--enable_warnings
|
||||||
|
create procedure p(in x int, inout y int, out z int)
|
||||||
|
begin
|
||||||
|
set y = x+y;
|
||||||
|
set z = x+y;
|
||||||
|
end|
|
||||||
|
|
||||||
|
set @tmp_x = 42|
|
||||||
|
set @tmp_y = 3|
|
||||||
|
set @tmp_z = 0|
|
||||||
|
# For reference: this is ok
|
||||||
|
call p(@tmp_x, @tmp_y, @tmp_z)|
|
||||||
|
select @tmp_x, @tmp_y, @tmp_z|
|
||||||
|
|
||||||
|
--error ER_SP_NOT_VAR_ARG
|
||||||
|
call p(42, 43, @tmp_z)|
|
||||||
|
--error ER_SP_NOT_VAR_ARG
|
||||||
|
call p(42, @tmp_y, 43)|
|
||||||
|
|
||||||
|
drop procedure p|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# BUG#1965
|
# BUG#1965
|
||||||
#
|
#
|
||||||
|
@@ -3571,6 +3571,38 @@ drop procedure bug9674_1|
|
|||||||
drop procedure bug9674_2|
|
drop procedure bug9674_2|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUG#9598: stored procedure call within stored procedure overwrites IN variable
|
||||||
|
#
|
||||||
|
--disable_warnings
|
||||||
|
drop procedure if exists bug9598_1|
|
||||||
|
drop procedure if exists bug9598_2|
|
||||||
|
--enable_warnings
|
||||||
|
create procedure bug9598_1(in var_1 char(16),
|
||||||
|
out var_2 integer, out var_3 integer)
|
||||||
|
begin
|
||||||
|
set var_2 = 50;
|
||||||
|
set var_3 = 60;
|
||||||
|
end|
|
||||||
|
|
||||||
|
create procedure bug9598_2(in v1 char(16),
|
||||||
|
in v2 integer,
|
||||||
|
in v3 integer,
|
||||||
|
in v4 integer,
|
||||||
|
in v5 integer)
|
||||||
|
begin
|
||||||
|
select v1,v2,v3,v4,v5;
|
||||||
|
call bug9598_1(v1,@tmp1,@tmp2);
|
||||||
|
select v1,v2,v3,v4,v5;
|
||||||
|
end|
|
||||||
|
|
||||||
|
call bug9598_2('Test',2,3,4,5)|
|
||||||
|
select @tmp1, @tmp2|
|
||||||
|
|
||||||
|
drop procedure bug9598_1|
|
||||||
|
drop procedure bug9598_2|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# BUG#NNNN: New bug synopsis
|
# BUG#NNNN: New bug synopsis
|
||||||
#
|
#
|
||||||
|
@@ -545,6 +545,8 @@ public:
|
|||||||
cleanup();
|
cleanup();
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool is_splocal() { return 0; } /* Needed for error checking */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -564,6 +566,8 @@ public:
|
|||||||
Item::maybe_null= TRUE;
|
Item::maybe_null= TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_splocal() { return 1; } /* Needed for error checking */
|
||||||
|
|
||||||
Item *this_item();
|
Item *this_item();
|
||||||
Item *this_const_item() const;
|
Item *this_const_item() const;
|
||||||
|
|
||||||
|
@@ -5340,3 +5340,5 @@ ER_TABLE_DEF_CHANGED
|
|||||||
eng "Table definition has changed, please retry transaction"
|
eng "Table definition has changed, please retry transaction"
|
||||||
ER_SP_DUP_HANDLER 42000
|
ER_SP_DUP_HANDLER 42000
|
||||||
eng "Duplicate handler declared in the same block"
|
eng "Duplicate handler declared in the same block"
|
||||||
|
ER_SP_NOT_VAR_ARG 42000
|
||||||
|
eng "OUT or INOUT argument %d for routine %s is not a variable"
|
||||||
|
@@ -638,7 +638,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
|
|||||||
// Need to use my_printf_error here, or it will not terminate the
|
// Need to use my_printf_error here, or it will not terminate the
|
||||||
// invoking query properly.
|
// invoking query properly.
|
||||||
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0),
|
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0),
|
||||||
"FUNCTION", m_name.str, params, argcount);
|
"FUNCTION", m_qname.str, params, argcount);
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,6 +691,19 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
|
|||||||
DBUG_RETURN(ret);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Item_func_get_user_var *
|
||||||
|
item_is_user_var(Item *it)
|
||||||
|
{
|
||||||
|
if (it->type() == Item::FUNC_ITEM)
|
||||||
|
{
|
||||||
|
Item_func *fi= static_cast<Item_func*>(it);
|
||||||
|
|
||||||
|
if (fi->functype() == Item_func::GUSERVAR_FUNC)
|
||||||
|
return static_cast<Item_func_get_user_var*>(fi);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
sp_head::execute_procedure(THD *thd, List<Item> *args)
|
sp_head::execute_procedure(THD *thd, List<Item> *args)
|
||||||
{
|
{
|
||||||
@@ -708,7 +721,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||||||
if (args->elements != params)
|
if (args->elements != params)
|
||||||
{
|
{
|
||||||
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
|
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
|
||||||
m_name.str, params, args->elements);
|
m_qname.str, params, args->elements);
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,12 +741,19 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||||||
// QQ: Should do type checking?
|
// QQ: Should do type checking?
|
||||||
for (i = 0 ; (it= li++) && i < params ; i++)
|
for (i = 0 ; (it= li++) && i < params ; i++)
|
||||||
{
|
{
|
||||||
sp_pvar_t *pvar = m_pcont->find_pvar(i);
|
sp_pvar_t *pvar= m_pcont->find_pvar(i);
|
||||||
|
|
||||||
if (! pvar)
|
if (pvar)
|
||||||
nctx->set_oindex(i, -1); // Shouldn't happen
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
if (pvar->mode != sp_param_in)
|
||||||
|
{
|
||||||
|
if (!it->is_splocal() && !item_is_user_var(it))
|
||||||
|
{
|
||||||
|
my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str);
|
||||||
|
ret= -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (pvar->mode == sp_param_out)
|
if (pvar->mode == sp_param_out)
|
||||||
{
|
{
|
||||||
if (! nit)
|
if (! nit)
|
||||||
@@ -742,7 +762,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Item *it2= sp_eval_func_item(thd, it,pvar->type);
|
Item *it2= sp_eval_func_item(thd, it, pvar->type);
|
||||||
|
|
||||||
if (it2)
|
if (it2)
|
||||||
nctx->push_item(it2); // IN or INOUT
|
nctx->push_item(it2); // IN or INOUT
|
||||||
@@ -752,13 +772,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note: If it's OUT or INOUT, it must be a variable.
|
|
||||||
// QQ: We can check for global variables here, or should we do it
|
|
||||||
// while parsing?
|
|
||||||
if (pvar->mode == sp_param_in)
|
|
||||||
nctx->set_oindex(i, -1); // IN
|
|
||||||
else // OUT or INOUT
|
|
||||||
nctx->set_oindex(i, static_cast<Item_splocal *>(it)->get_offset());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -786,27 +799,21 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||||||
// set global user variables
|
// set global user variables
|
||||||
for (uint i = 0 ; (it= li++) && i < params ; i++)
|
for (uint i = 0 ; (it= li++) && i < params ; i++)
|
||||||
{
|
{
|
||||||
int oi = nctx->get_oindex(i);
|
sp_pvar_t *pvar= m_pcont->find_pvar(i);
|
||||||
|
|
||||||
if (oi >= 0)
|
if (pvar->mode != sp_param_in)
|
||||||
{
|
{
|
||||||
if (! tmp_octx)
|
if (it->is_splocal())
|
||||||
octx->set_item(nctx->get_oindex(i), nctx->get_item(i));
|
octx->set_item(static_cast<Item_splocal *>(it)->get_offset(),
|
||||||
|
nctx->get_item(i));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// QQ Currently we just silently ignore non-user-variable arguments.
|
Item_func_get_user_var *guv= item_is_user_var(it);
|
||||||
// We should check this during parsing, when setting up the call
|
|
||||||
// above
|
|
||||||
if (it->type() == Item::FUNC_ITEM)
|
|
||||||
{
|
|
||||||
Item_func *fi= static_cast<Item_func*>(it);
|
|
||||||
|
|
||||||
if (fi->functype() == Item_func::GUSERVAR_FUNC)
|
if (guv)
|
||||||
{ // A global user variable
|
{
|
||||||
Item *item= nctx->get_item(i);
|
Item *item= nctx->get_item(i);
|
||||||
Item_func_set_user_var *suv;
|
Item_func_set_user_var *suv;
|
||||||
Item_func_get_user_var *guv=
|
|
||||||
static_cast<Item_func_get_user_var*>(fi);
|
|
||||||
|
|
||||||
suv= new Item_func_set_user_var(guv->get_name(), item);
|
suv= new Item_func_set_user_var(guv->get_name(), item);
|
||||||
/*
|
/*
|
||||||
@@ -822,7 +829,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp_octx)
|
if (tmp_octx)
|
||||||
octx= NULL;
|
octx= NULL;
|
||||||
|
@@ -34,7 +34,6 @@ sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax)
|
|||||||
{
|
{
|
||||||
in_handler= FALSE;
|
in_handler= FALSE;
|
||||||
m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
|
m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
|
||||||
m_outs= (int *)sql_alloc(fsize * sizeof(int));
|
|
||||||
m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
|
m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
|
||||||
m_hstack= (uint *)sql_alloc(hmax * sizeof(uint));
|
m_hstack= (uint *)sql_alloc(hmax * sizeof(uint));
|
||||||
m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *));
|
m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *));
|
||||||
|
@@ -82,18 +82,6 @@ class sp_rcontext : public Sql_alloc
|
|||||||
return m_frame[idx];
|
return m_frame[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
|
||||||
set_oindex(uint idx, int oidx)
|
|
||||||
{
|
|
||||||
m_outs[idx] = oidx;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int
|
|
||||||
get_oindex(uint idx)
|
|
||||||
{
|
|
||||||
return m_outs[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
set_result(Item *it)
|
set_result(Item *it)
|
||||||
{
|
{
|
||||||
@@ -187,7 +175,6 @@ private:
|
|||||||
uint m_count;
|
uint m_count;
|
||||||
uint m_fsize;
|
uint m_fsize;
|
||||||
Item **m_frame;
|
Item **m_frame;
|
||||||
int *m_outs;
|
|
||||||
|
|
||||||
Item *m_result; // For FUNCTIONs
|
Item *m_result; // For FUNCTIONs
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user