Commit 8d593d49 authored by unknown's avatar unknown

Merge bk-internal.mysql.com:/home/bk/mysql-5.0

into mysql.com:/usersnfs/pchardin/mysql-5.0

parents 34bf64bc fa19a9f2
......@@ -109,6 +109,7 @@ call foo4();
Got one of the listed errors
show warnings;
Level Code Message
Error 1142 INSERT command denied to user 'zedjzlcsjhd'@'localhost' for table 't1'
Warning 1417 A routine failed and has neither NO SQL nor READS SQL DATA in its declaration and binary logging is enabled; if non-transactional tables were updated, the binary log will miss their changes
call foo3();
show warnings;
......@@ -117,6 +118,7 @@ call foo4();
Got one of the listed errors
show warnings;
Level Code Message
Error 1142 INSERT command denied to user 'zedjzlcsjhd'@'localhost' for table 't1'
Warning 1417 A routine failed and has neither NO SQL nor READS SQL DATA in its declaration and binary logging is enabled; if non-transactional tables were updated, the binary log will miss their changes
alter procedure foo4 sql security invoker;
call foo4();
......
......@@ -3085,4 +3085,19 @@ column_name bug10055(t.column_name)
id id
data data
drop function bug10055|
drop function if exists f_bug11247|
drop procedure if exists p_bug11247|
create function f_bug11247(param int)
returns int
return param + 1|
create procedure p_bug11247(lim int)
begin
declare v int default 0;
while v < lim do
set v= f_bug11247(v);
end while;
end|
call p_bug11247(10)|
drop function f_bug11247|
drop procedure p_bug11247|
drop table t1,t2;
......@@ -3870,6 +3870,65 @@ from information_schema.columns as t
where t.table_schema = 'test' and t.table_name = 't1'|
drop function bug10055|
#
# Bug #12297 "SP crashes the server if data inserted inside a lon loop"
# The test for memleak bug, so actually there is no way to test it
# from the suite. The test below could be used to check SP memory
# consumption by passing large input parameter.
#
#
# Note: the test is currenly disabled because of the
# Bug #12637: SP crashes the server if it has update query with user var
# & binlog is enabled.
#
--disable_warnings
#drop procedure if exists bug12297|
--enable_warnings
#create procedure bug12297(lim int)
#begin
# set @x = 0;
# repeat
# insert into t1(id,data)
# values('aa', @x);
# set @x = @x + 1;
# until @x >= lim
# end repeat;
#end|
#call bug12297(10)|
#drop procedure bug12297|
#
# Bug #11247 "Stored procedures: Function calls in long loops leak memory"
# One more memleak bug test. One could use this test to check that the memory
# isn't leaking by increasing the input value for p_bug11247.
#
--disable_warnings
drop function if exists f_bug11247|
drop procedure if exists p_bug11247|
--enable_warnings
create function f_bug11247(param int)
returns int
return param + 1|
create procedure p_bug11247(lim int)
begin
declare v int default 0;
while v < lim do
set v= f_bug11247(v);
end while;
end|
call p_bug11247(10)|
drop function f_bug11247|
drop procedure p_bug11247|
#
# BUG#NNNN: New bug synopsis
#
......
......@@ -126,16 +126,47 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
}
/* Evaluate a (presumed) func item. Always returns an item, the parameter
** if nothing else.
/* Macro to switch arena in sp_eval_func_item */
#define CREATE_ON_CALLERS_ARENA(new_command, condition, backup_arena) do\
{\
if (condition) \
thd->set_n_backup_item_arena(thd->spcont->callers_arena,\
backup_arena);\
new_command;\
if (condition)\
thd->restore_backup_item_arena(thd->spcont->callers_arena,\
&backup_current_arena);\
} while(0)
/*
Evaluate an item and store it in the returned item
SYNOPSIS
sp_eval_func_item()
name - current thread object
it_addr - pointer to the item to evaluate
type - type of the item we evaluating
reuse - used if we would like to reuse existing item
instead of allocation of the new one
use_callers_arena - TRUE if we want to use caller's arena
rather then current one.
DESCRIPTION
We use this function to evaluate result for stored functions
and stored procedure parameters. It is also used to evaluate and
(re) allocate variables.
RETURN VALUES
Evaluated item is returned
*/
Item *
sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
Item *reuse)
Item *reuse, bool use_callers_arena)
{
DBUG_ENTER("sp_eval_func_item");
Item *it= sp_prepare_func_item(thd, it_addr);
uint rsize;
Query_arena backup_current_arena;
DBUG_PRINT("info", ("type: %d", type));
if (!it)
......@@ -145,90 +176,99 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
/* QQ How do we do this? Is there some better way? */
if (type == MYSQL_TYPE_NULL)
it= new(reuse, &rsize) Item_null();
else
{
switch (sp_map_result_type(type)) {
case INT_RESULT:
{
longlong i= it->val_int();
goto return_null_item;
if (it->null_value)
{
DBUG_PRINT("info", ("INT_RESULT: null"));
it= new(reuse, &rsize) Item_null();
}
else
{
DBUG_PRINT("info", ("INT_RESULT: %d", i));
it= new(reuse, &rsize) Item_int(i);
}
break;
switch (sp_map_result_type(type)) {
case INT_RESULT:
{
longlong i= it->val_int();
if (it->null_value)
{
DBUG_PRINT("info", ("INT_RESULT: null"));
goto return_null_item;
}
case REAL_RESULT:
else
{
double d= it->val_real();
DBUG_PRINT("info", ("INT_RESULT: %d", i));
CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_int(i),
use_callers_arena, &backup_current_arena);
}
break;
}
case REAL_RESULT:
{
double d= it->val_real();
if (it->null_value)
{
DBUG_PRINT("info", ("REAL_RESULT: null"));
it= new(reuse, &rsize) Item_null();
}
else
{
/* There's some difference between Item::new_item() and the
* constructor; the former crashes, the latter works... weird. */
uint8 decimals= it->decimals;
uint32 max_length= it->max_length;
DBUG_PRINT("info", ("REAL_RESULT: %g", d));
it= new(reuse, &rsize) Item_float(d);
it->decimals= decimals;
it->max_length= max_length;
}
break;
if (it->null_value)
{
DBUG_PRINT("info", ("REAL_RESULT: null"));
goto return_null_item;
}
case DECIMAL_RESULT:
else
{
my_decimal value, *val= it->val_decimal(&value);
if (it->null_value)
it= new(reuse, &rsize) Item_null();
else
it= new(reuse, &rsize) Item_decimal(val);
/* There's some difference between Item::new_item() and the
* constructor; the former crashes, the latter works... weird. */
uint8 decimals= it->decimals;
uint32 max_length= it->max_length;
DBUG_PRINT("info", ("REAL_RESULT: %g", d));
CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_float(d),
use_callers_arena, &backup_current_arena);
it->decimals= decimals;
it->max_length= max_length;
}
break;
}
case DECIMAL_RESULT:
{
my_decimal value, *val= it->val_decimal(&value);
if (it->null_value)
goto return_null_item;
else
CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_decimal(val),
use_callers_arena, &backup_current_arena);
#ifndef DBUG_OFF
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
DBUG_PRINT("info", ("DECIMAL_RESULT: %s", dbug_decimal_as_string(dbug_buff, val)));
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
DBUG_PRINT("info", ("DECIMAL_RESULT: %s", dbug_decimal_as_string(dbug_buff, val)));
#endif
break;
break;
}
case STRING_RESULT:
{
char buffer[MAX_FIELD_WIDTH];
String tmp(buffer, sizeof(buffer), it->collation.collation);
String *s= it->val_str(&tmp);
if (it->null_value)
{
DBUG_PRINT("info", ("default result: null"));
goto return_null_item;
}
case STRING_RESULT:
else
{
char buffer[MAX_FIELD_WIDTH];
String tmp(buffer, sizeof(buffer), it->collation.collation);
String *s= it->val_str(&tmp);
if (it->null_value)
{
DBUG_PRINT("info", ("default result: null"));
it= new(reuse, &rsize) Item_null();
}
else
{
DBUG_PRINT("info",("default result: %*s",
s->length(), s->c_ptr_quick()));
it= new(reuse, &rsize) Item_string(thd->strmake(s->ptr(),
s->length()),
s->length(),
it->collation.collation);
}
break;
DBUG_PRINT("info",("default result: %*s",
s->length(), s->c_ptr_quick()));
CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize)
Item_string(thd->strmake(s->ptr(),
s->length()), s->length(),
it->collation.collation),
use_callers_arena, &backup_current_arena);
}
case ROW_RESULT:
default:
DBUG_ASSERT(0);
break;
}
case ROW_RESULT:
default:
DBUG_ASSERT(0);
}
it->rsize= rsize;
DBUG_RETURN(it);
return_null_item:
CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_null(),
use_callers_arena, &backup_current_arena);
it->rsize= rsize;
DBUG_RETURN(it);
}
......@@ -545,23 +585,14 @@ Field *
sp_head::make_field(uint max_length, const char *name, TABLE *dummy)
{
Field *field;
MEM_ROOT *tmp_mem_root;
THD *thd;
DBUG_ENTER("sp_head::make_field");
thd= current_thd;
tmp_mem_root= thd->mem_root;
if (thd->spcont && thd->spcont->callers_mem_root)
thd->mem_root= thd->spcont->callers_mem_root;
else
thd->mem_root= &thd->main_mem_root;
field= ::make_field((char *)0,
!m_returns_len ? max_length : m_returns_len,
(uchar *)"", 0, m_returns_pack, m_returns, m_returns_cs,
(enum Field::geometry_type)0, Field::NONE,
m_returns_typelib,
name ? name : (const char *)m_name.str, dummy);
thd->mem_root= tmp_mem_root;
DBUG_RETURN(field);
}
......@@ -576,12 +607,20 @@ sp_head::execute(THD *thd)
uint ip= 0;
ulong save_sql_mode;
Query_arena *old_arena;
/* per-instruction arena */
MEM_ROOT execute_mem_root;
Query_arena execute_arena(&execute_mem_root, INITIALIZED_FOR_SP),
execute_backup_arena;
query_id_t old_query_id;
TABLE *old_derived_tables;
LEX *old_lex;
Item_change_list old_change_list;
String old_packet;
/* init per-instruction memroot */
init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
/* Use some extra margin for possible SP recursion and functions */
if (check_stack_overrun(thd, 4*STACK_MIN_SIZE, olddb))
{
......@@ -650,6 +689,18 @@ sp_head::execute(THD *thd)
*/
old_packet.swap(thd->packet);
/*
Switch to per-instruction arena here. We can do it since we cleanup
arena after every instruction.
*/
thd->set_n_backup_item_arena(&execute_arena, &execute_backup_arena);
/*
Save callers arena in order to store instruction results and out
parameters in it later during sp_eval_func_item()
*/
thd->spcont->callers_arena= &execute_backup_arena;
do
{
sp_instr *i;
......@@ -670,6 +721,7 @@ sp_head::execute(THD *thd)
*/
thd->current_arena= i;
ret= i->execute(thd, &ip);
/*
If this SP instruction have sent eof, it has caused no_send_error to be
set. Clear it back to allow the next instruction to send error. (multi-
......@@ -680,6 +732,10 @@ sp_head::execute(THD *thd)
cleanup_items(i->free_list);
i->state= Query_arena::EXECUTED;
/* we should cleanup free_list and memroot, used by instruction */
thd->free_items();
free_root(&execute_mem_root, MYF(0));
/*
Check if an exception has occurred and a handler has been found
Note: We havo to check even if ret==0, since warnings (and some
......@@ -696,8 +752,10 @@ sp_head::execute(THD *thd)
case SP_HANDLER_NONE:
break;
case SP_HANDLER_CONTINUE:
ctx->save_variables(hf);
ctx->push_hstack(ip);
thd->restore_backup_item_arena(&execute_arena, &execute_backup_arena);
ctx->save_variables(hf);
thd->set_n_backup_item_arena(&execute_arena, &execute_backup_arena);
ctx->push_hstack(ip);
// Fall through
default:
ip= hip;
......@@ -711,6 +769,9 @@ sp_head::execute(THD *thd)
}
} while (ret == 0 && !thd->killed);
thd->restore_backup_item_arena(&execute_arena, &execute_backup_arena);
/* Restore all saved */
old_packet.swap(thd->packet);
DBUG_ASSERT(thd->change_list.is_empty());
......@@ -757,8 +818,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
sp_rcontext *nctx = NULL;
uint i;
int ret;
MEM_ROOT call_mem_root;
Query_arena call_arena(&call_mem_root, INITIALIZED_FOR_SP), backup_arena;
if (argcount != params)
{
......@@ -771,16 +830,13 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
DBUG_RETURN(-1);
}
init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
// QQ Should have some error checking here? (types, etc...)
nctx= new sp_rcontext(csize, hmax, cmax);
nctx->callers_mem_root= thd->mem_root;
for (i= 0 ; i < argcount ; i++)
{
sp_pvar_t *pvar = m_pcont->find_pvar(i);
Item *it= sp_eval_func_item(thd, argp++, pvar->type, NULL);
Item *it= sp_eval_func_item(thd, argp++, pvar->type, NULL, FALSE);
if (it)
nctx->push_item(it);
......@@ -804,26 +860,16 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
}
}
thd->spcont= nctx;
thd->set_n_backup_item_arena(&call_arena, &backup_arena);
/* mem_root was moved to backup_arena */
DBUG_ASSERT(nctx->callers_mem_root == backup_arena.mem_root);
ret= execute(thd);
/*
Partially restore context now.
We still need the call mem root and free list for processing
of the result.
*/
thd->restore_backup_item_arena(&call_arena, &backup_arena);
if (m_type == TYPE_ENUM_FUNCTION && ret == 0)
{
/* We need result only in function but not in trigger */
Item *it= nctx->get_result();
if (it)
*resp= sp_eval_func_item(thd, &it, m_returns, NULL);
*resp= sp_eval_func_item(thd, &it, m_returns, NULL, FALSE);
else
{
my_error(ER_SP_NORETURNEND, MYF(0), m_name.str);
......@@ -832,12 +878,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
}
nctx->pop_all_cursors(); // To avoid memory leaks after an error
delete nctx;
thd->spcont= octx;
// Now get rid of the rest of the callee context
call_arena.free_items();
free_root(&call_mem_root, MYF(0));
DBUG_RETURN(ret);
}
......@@ -866,9 +909,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
uint cmax = m_pcont->max_cursors();
sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL;
my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx
MEM_ROOT call_mem_root;
Query_arena call_arena(&call_mem_root, INITIALIZED_FOR_SP), backup_arena;
my_bool is_tmp_octx = FALSE; // True if we have allocated a temporary octx
if (args->elements != params)
{
......@@ -877,7 +918,17 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_RETURN(-1);
}
init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
if (! octx)
{ // Create a temporary old context
octx= new sp_rcontext(csize, hmax, cmax);
is_tmp_octx= TRUE;
thd->spcont= octx;
/* set callers_arena to thd, for upper-level function to work */
thd->spcont->callers_arena= thd;
}
nctx= new sp_rcontext(csize, hmax, cmax);
if (csize > 0 || hmax > 0 || cmax > 0)
{
......@@ -886,12 +937,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
List_iterator<Item> li(*args);
Item *it;
nctx= new sp_rcontext(csize, hmax, cmax);
if (! octx)
{ // Create a temporary old context
octx= new sp_rcontext(csize, hmax, cmax);
tmp_octx= TRUE;
}
/* Evaluate SP arguments (i.e. get the values passed as parameters) */
// QQ: Should do type checking?
......@@ -919,7 +964,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
else
{
Item *it2= sp_eval_func_item(thd, li.ref(), pvar->type, NULL);
Item *it2= sp_eval_func_item(thd, li.ref(), pvar->type, NULL, FALSE);
if (it2)
nctx->push_item(it2); // IN or INOUT
......@@ -952,15 +997,20 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
nit= new Item_null();
nctx->push_item(nit);
}
thd->spcont= nctx;
}
thd->spcont= nctx;
if (! ret)
{
thd->set_n_backup_item_arena(&call_arena, &backup_arena);
ret= execute(thd);
thd->restore_backup_item_arena(&call_arena, &backup_arena);
}
/*
In the case when we weren't able to employ reuse mechanism for
OUT/INOUT paranmeters, we should reallocate memory. This
allocation should be done on the arena which will live through
all execution of calling routine.
*/
thd->spcont->callers_arena= octx->callers_arena;
if (!ret && csize > 0)
{
......@@ -985,12 +1035,20 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
Item *val= nctx->get_item(i);
Item *orig= octx->get_item(offset);
Item *o_item_next;
Item *o_free_list= thd->free_list;
/* we'll use callers_arena in sp_eval_func_item */
Item *o_free_list= thd->spcont->callers_arena->free_list;
LINT_INIT(o_item_next);
if (orig)
o_item_next= orig->next;
copy= sp_eval_func_item(thd, &val, pvar->type, orig); // Copy
/*
We might need to allocate new item if we weren't able to
employ reuse mechanism. Then we should do it on the callers arena.
*/
copy= sp_eval_func_item(thd, &val, pvar->type, orig, TRUE); // Copy
if (!copy)
{
ret= -1;
......@@ -1004,7 +1062,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
A reused item slot, where the constructor put it in the
free_list, so we have to restore the list.
*/
thd->free_list= o_free_list;
thd->spcont->callers_arena->free_list= o_free_list;
copy->next= o_item_next;
}
}
......@@ -1032,16 +1090,15 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
}
if (tmp_octx)
if (is_tmp_octx)
{
delete octx; /* call destructor */
octx= NULL;
if (nctx)
nctx->pop_all_cursors(); // To avoid memory leaks after an error
thd->spcont= octx;
}
// Now get rid of the rest of the callee context
call_arena.free_items();
thd->lex->unit.cleanup();
free_root(&call_mem_root, MYF(0));
nctx->pop_all_cursors(); // To avoid memory leaks after an error
delete nctx;
thd->spcont= octx;
DBUG_RETURN(ret);
}
......@@ -1894,7 +1951,7 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp)
Item *it;
int res;
it= sp_eval_func_item(thd, &m_value, m_type, NULL);
it= sp_eval_func_item(thd, &m_value, m_type, NULL, TRUE);
if (! it)
res= -1;
else
......@@ -2044,9 +2101,23 @@ sp_instr_hreturn::opt_mark(sp_head *sp)
int
sp_instr_cpush::execute(THD *thd, uint *nextp)
{
Query_arena backup_current_arena;
DBUG_ENTER("sp_instr_cpush::execute");
/*
We should create cursors in the callers arena, as
it could be (and usually is) used in several instructions.
*/
thd->set_n_backup_item_arena(thd->spcont->callers_arena,
&backup_current_arena);
thd->spcont->push_cursor(&m_lex_keeper, this);
thd->restore_backup_item_arena(thd->spcont->callers_arena,
&backup_current_arena);
*nextp= m_ip+1;
DBUG_RETURN(0);
}
......@@ -2197,12 +2268,20 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp)
{
sp_cursor *c= thd->spcont->get_cursor(m_cursor);
int res;
Query_arena backup_current_arena;
DBUG_ENTER("sp_instr_cfetch::execute");
if (! c)
res= -1;
else
{
thd->set_n_backup_item_arena(thd->spcont->callers_arena,
&backup_current_arena);
res= c->fetch(thd, &m_varlist);
thd->restore_backup_item_arena(thd->spcont->callers_arena,
&backup_current_arena);
}
*nextp= m_ip+1;
DBUG_RETURN(res);
}
......
......@@ -32,7 +32,6 @@ sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax)
: m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0),
m_hfound(-1), m_ccount(0)
{
callers_mem_root= NULL;
in_handler= FALSE;
m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
......@@ -47,17 +46,18 @@ sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr,
enum_field_types type)
{
extern Item *sp_eval_func_item(THD *thd, Item **it, enum_field_types type,
Item *reuse);
Item *reuse, bool use_callers_arena);
Item *it;
Item *reuse_it;
Item *old_item_next;
Item *old_free_list= thd->free_list;
/* sp_eval_func_item will use callers_arena */
Item *old_free_list= thd->spcont->callers_arena->free_list;
int res;
LINT_INIT(old_item_next);
if ((reuse_it= get_item(idx)))
old_item_next= reuse_it->next;
it= sp_eval_func_item(thd, item_addr, type, reuse_it);
it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE);
if (! it)
res= -1;
else
......@@ -67,7 +67,7 @@ sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr,
{
// A reused item slot, where the constructor put it in the free_list,
// so we have to restore the list.
thd->free_list= old_free_list;
thd->spcont->callers_arena->free_list= old_free_list;
it->next= old_item_next;
}
set_item(idx, it);
......
......@@ -48,8 +48,14 @@ class sp_rcontext : public Sql_alloc
public:
MEM_ROOT *callers_mem_root; // Used to store result fields
bool in_handler;
/*
Arena used to (re) allocate items on . E.g. reallocate INOUT/OUT
SP parameters when they don't fit into prealloced items. This
is common situation with String items. It is used mainly in
sp_eval_func_item().
*/
Query_arena *callers_arena;
sp_rcontext(uint fsize, uint hmax, uint cmax);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment