Commit b5e7bfa9 authored by igor@rurik.mysql.com's avatar igor@rurik.mysql.com

Merge rurik.mysql.com:/home/igor/mysql-5.0

into rurik.mysql.com:/home/igor/dev/mysql-5.0-0
parents fd5461ac cb716262
...@@ -109,6 +109,7 @@ call foo4(); ...@@ -109,6 +109,7 @@ call foo4();
Got one of the listed errors Got one of the listed errors
show warnings; show warnings;
Level Code Message 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 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(); call foo3();
show warnings; show warnings;
...@@ -117,6 +118,7 @@ call foo4(); ...@@ -117,6 +118,7 @@ call foo4();
Got one of the listed errors Got one of the listed errors
show warnings; show warnings;
Level Code Message 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 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; alter procedure foo4 sql security invoker;
call foo4(); call foo4();
......
...@@ -3085,4 +3085,19 @@ column_name bug10055(t.column_name) ...@@ -3085,4 +3085,19 @@ column_name bug10055(t.column_name)
id id id id
data data data data
drop function bug10055| 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; drop table t1,t2;
...@@ -3870,6 +3870,65 @@ from information_schema.columns as t ...@@ -3870,6 +3870,65 @@ from information_schema.columns as t
where t.table_schema = 'test' and t.table_name = 't1'| where t.table_schema = 'test' and t.table_name = 't1'|
drop function bug10055| 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 # BUG#NNNN: New bug synopsis
# #
......
...@@ -126,16 +126,47 @@ sp_prepare_func_item(THD* thd, Item **it_addr) ...@@ -126,16 +126,47 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
} }
/* Evaluate a (presumed) func item. Always returns an item, the parameter /* Macro to switch arena in sp_eval_func_item */
** if nothing else. #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 * Item *
sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, 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"); DBUG_ENTER("sp_eval_func_item");
Item *it= sp_prepare_func_item(thd, it_addr); Item *it= sp_prepare_func_item(thd, it_addr);
uint rsize; uint rsize;
Query_arena backup_current_arena;
DBUG_PRINT("info", ("type: %d", type)); DBUG_PRINT("info", ("type: %d", type));
if (!it) if (!it)
...@@ -145,90 +176,99 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, ...@@ -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? */ /* QQ How do we do this? Is there some better way? */
if (type == MYSQL_TYPE_NULL) if (type == MYSQL_TYPE_NULL)
it= new(reuse, &rsize) Item_null(); goto return_null_item;
else
{
switch (sp_map_result_type(type)) {
case INT_RESULT:
{
longlong i= it->val_int();
if (it->null_value) switch (sp_map_result_type(type)) {
{ case INT_RESULT:
DBUG_PRINT("info", ("INT_RESULT: null")); {
it= new(reuse, &rsize) Item_null(); longlong i= it->val_int();
}
else if (it->null_value)
{ {
DBUG_PRINT("info", ("INT_RESULT: %d", i)); DBUG_PRINT("info", ("INT_RESULT: null"));
it= new(reuse, &rsize) Item_int(i); goto return_null_item;
}
break;
} }
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) if (it->null_value)
{ {
DBUG_PRINT("info", ("REAL_RESULT: null")); DBUG_PRINT("info", ("REAL_RESULT: null"));
it= new(reuse, &rsize) Item_null(); goto return_null_item;
}
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;
} }
case DECIMAL_RESULT: else
{ {
my_decimal value, *val= it->val_decimal(&value); /* There's some difference between Item::new_item() and the
if (it->null_value) * constructor; the former crashes, the latter works... weird. */
it= new(reuse, &rsize) Item_null(); uint8 decimals= it->decimals;
else uint32 max_length= it->max_length;
it= new(reuse, &rsize) Item_decimal(val); 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 #ifndef DBUG_OFF
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1]; char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
DBUG_PRINT("info", ("DECIMAL_RESULT: %s", dbug_decimal_as_string(dbug_buff, val))); DBUG_PRINT("info", ("DECIMAL_RESULT: %s", dbug_decimal_as_string(dbug_buff, val)));
#endif #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]; DBUG_PRINT("info",("default result: %*s",
String tmp(buffer, sizeof(buffer), it->collation.collation); s->length(), s->c_ptr_quick()));
String *s= it->val_str(&tmp); CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize)
Item_string(thd->strmake(s->ptr(),
if (it->null_value) s->length()), s->length(),
{ it->collation.collation),
DBUG_PRINT("info", ("default result: null")); use_callers_arena, &backup_current_arena);
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;
} }
case ROW_RESULT: break;
default:
DBUG_ASSERT(0);
} }
case ROW_RESULT:
default:
DBUG_ASSERT(0);
} }
it->rsize= rsize; 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); DBUG_RETURN(it);
} }
...@@ -545,23 +585,14 @@ Field * ...@@ -545,23 +585,14 @@ Field *
sp_head::make_field(uint max_length, const char *name, TABLE *dummy) sp_head::make_field(uint max_length, const char *name, TABLE *dummy)
{ {
Field *field; Field *field;
MEM_ROOT *tmp_mem_root;
THD *thd;
DBUG_ENTER("sp_head::make_field"); 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, field= ::make_field((char *)0,
!m_returns_len ? max_length : m_returns_len, !m_returns_len ? max_length : m_returns_len,
(uchar *)"", 0, m_returns_pack, m_returns, m_returns_cs, (uchar *)"", 0, m_returns_pack, m_returns, m_returns_cs,
(enum Field::geometry_type)0, Field::NONE, (enum Field::geometry_type)0, Field::NONE,
m_returns_typelib, m_returns_typelib,
name ? name : (const char *)m_name.str, dummy); name ? name : (const char *)m_name.str, dummy);
thd->mem_root= tmp_mem_root;
DBUG_RETURN(field); DBUG_RETURN(field);
} }
...@@ -576,12 +607,20 @@ sp_head::execute(THD *thd) ...@@ -576,12 +607,20 @@ sp_head::execute(THD *thd)
uint ip= 0; uint ip= 0;
ulong save_sql_mode; ulong save_sql_mode;
Query_arena *old_arena; 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; query_id_t old_query_id;
TABLE *old_derived_tables; TABLE *old_derived_tables;
LEX *old_lex; LEX *old_lex;
Item_change_list old_change_list; Item_change_list old_change_list;
String old_packet; 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 */ /* Use some extra margin for possible SP recursion and functions */
if (check_stack_overrun(thd, 4*STACK_MIN_SIZE, olddb)) if (check_stack_overrun(thd, 4*STACK_MIN_SIZE, olddb))
{ {
...@@ -650,6 +689,18 @@ sp_head::execute(THD *thd) ...@@ -650,6 +689,18 @@ sp_head::execute(THD *thd)
*/ */
old_packet.swap(thd->packet); 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 do
{ {
sp_instr *i; sp_instr *i;
...@@ -670,6 +721,7 @@ sp_head::execute(THD *thd) ...@@ -670,6 +721,7 @@ sp_head::execute(THD *thd)
*/ */
thd->current_arena= i; thd->current_arena= i;
ret= i->execute(thd, &ip); ret= i->execute(thd, &ip);
/* /*
If this SP instruction have sent eof, it has caused no_send_error to be 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- set. Clear it back to allow the next instruction to send error. (multi-
...@@ -680,6 +732,10 @@ sp_head::execute(THD *thd) ...@@ -680,6 +732,10 @@ sp_head::execute(THD *thd)
cleanup_items(i->free_list); cleanup_items(i->free_list);
i->state= Query_arena::EXECUTED; 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 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 Note: We havo to check even if ret==0, since warnings (and some
...@@ -696,8 +752,10 @@ sp_head::execute(THD *thd) ...@@ -696,8 +752,10 @@ sp_head::execute(THD *thd)
case SP_HANDLER_NONE: case SP_HANDLER_NONE:
break; break;
case SP_HANDLER_CONTINUE: case SP_HANDLER_CONTINUE:
ctx->save_variables(hf); thd->restore_backup_item_arena(&execute_arena, &execute_backup_arena);
ctx->push_hstack(ip); ctx->save_variables(hf);
thd->set_n_backup_item_arena(&execute_arena, &execute_backup_arena);
ctx->push_hstack(ip);
// Fall through // Fall through
default: default:
ip= hip; ip= hip;
...@@ -711,6 +769,9 @@ sp_head::execute(THD *thd) ...@@ -711,6 +769,9 @@ sp_head::execute(THD *thd)
} }
} while (ret == 0 && !thd->killed); } while (ret == 0 && !thd->killed);
thd->restore_backup_item_arena(&execute_arena, &execute_backup_arena);
/* Restore all saved */ /* Restore all saved */
old_packet.swap(thd->packet); old_packet.swap(thd->packet);
DBUG_ASSERT(thd->change_list.is_empty()); DBUG_ASSERT(thd->change_list.is_empty());
...@@ -757,8 +818,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) ...@@ -757,8 +818,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
sp_rcontext *nctx = NULL; sp_rcontext *nctx = NULL;
uint i; uint i;
int ret; int ret;
MEM_ROOT call_mem_root;
Query_arena call_arena(&call_mem_root, INITIALIZED_FOR_SP), backup_arena;
if (argcount != params) if (argcount != params)
{ {
...@@ -771,16 +830,13 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) ...@@ -771,16 +830,13 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
// QQ Should have some error checking here? (types, etc...) // QQ Should have some error checking here? (types, etc...)
nctx= new sp_rcontext(csize, hmax, cmax); nctx= new sp_rcontext(csize, hmax, cmax);
nctx->callers_mem_root= thd->mem_root;
for (i= 0 ; i < argcount ; i++) for (i= 0 ; i < argcount ; i++)
{ {
sp_pvar_t *pvar = m_pcont->find_pvar(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) if (it)
nctx->push_item(it); nctx->push_item(it);
...@@ -804,26 +860,16 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) ...@@ -804,26 +860,16 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
} }
} }
thd->spcont= nctx; 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); 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) if (m_type == TYPE_ENUM_FUNCTION && ret == 0)
{ {
/* We need result only in function but not in trigger */ /* We need result only in function but not in trigger */
Item *it= nctx->get_result(); Item *it= nctx->get_result();
if (it) if (it)
*resp= sp_eval_func_item(thd, &it, m_returns, NULL); *resp= sp_eval_func_item(thd, &it, m_returns, NULL, FALSE);
else else
{ {
my_error(ER_SP_NORETURNEND, MYF(0), m_name.str); 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) ...@@ -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 nctx->pop_all_cursors(); // To avoid memory leaks after an error
delete nctx;
thd->spcont= octx; 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); DBUG_RETURN(ret);
} }
...@@ -866,9 +909,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -866,9 +909,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
uint cmax = m_pcont->max_cursors(); uint cmax = m_pcont->max_cursors();
sp_rcontext *octx = thd->spcont; sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL; sp_rcontext *nctx = NULL;
my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx my_bool is_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;
if (args->elements != params) if (args->elements != params)
{ {
...@@ -877,7 +918,17 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -877,7 +918,17 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_RETURN(-1); 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) if (csize > 0 || hmax > 0 || cmax > 0)
{ {
...@@ -886,12 +937,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -886,12 +937,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
List_iterator<Item> li(*args); List_iterator<Item> li(*args);
Item *it; 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) */ /* Evaluate SP arguments (i.e. get the values passed as parameters) */
// QQ: Should do type checking? // QQ: Should do type checking?
...@@ -919,7 +964,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -919,7 +964,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
} }
else 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) if (it2)
nctx->push_item(it2); // IN or INOUT nctx->push_item(it2); // IN or INOUT
...@@ -952,15 +997,20 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -952,15 +997,20 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
nit= new Item_null(); nit= new Item_null();
nctx->push_item(nit); nctx->push_item(nit);
} }
thd->spcont= nctx;
} }
thd->spcont= nctx;
if (! ret) if (! ret)
{
thd->set_n_backup_item_arena(&call_arena, &backup_arena);
ret= execute(thd); 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) if (!ret && csize > 0)
{ {
...@@ -985,12 +1035,20 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -985,12 +1035,20 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
Item *val= nctx->get_item(i); Item *val= nctx->get_item(i);
Item *orig= octx->get_item(offset); Item *orig= octx->get_item(offset);
Item *o_item_next; 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); LINT_INIT(o_item_next);
if (orig) if (orig)
o_item_next= orig->next; 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) if (!copy)
{ {
ret= -1; ret= -1;
...@@ -1004,7 +1062,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -1004,7 +1062,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
A reused item slot, where the constructor put it in the A reused item slot, where the constructor put it in the
free_list, so we have to restore the list. 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; copy->next= o_item_next;
} }
} }
...@@ -1032,16 +1090,15 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -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; 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 nctx->pop_all_cursors(); // To avoid memory leaks after an error
call_arena.free_items(); delete nctx;
thd->lex->unit.cleanup(); thd->spcont= octx;
free_root(&call_mem_root, MYF(0));
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
...@@ -1894,7 +1951,7 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp) ...@@ -1894,7 +1951,7 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp)
Item *it; Item *it;
int res; 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) if (! it)
res= -1; res= -1;
else else
...@@ -2044,9 +2101,23 @@ sp_instr_hreturn::opt_mark(sp_head *sp) ...@@ -2044,9 +2101,23 @@ sp_instr_hreturn::opt_mark(sp_head *sp)
int int
sp_instr_cpush::execute(THD *thd, uint *nextp) sp_instr_cpush::execute(THD *thd, uint *nextp)
{ {
Query_arena backup_current_arena;
DBUG_ENTER("sp_instr_cpush::execute"); 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->spcont->push_cursor(&m_lex_keeper, this);
thd->restore_backup_item_arena(thd->spcont->callers_arena,
&backup_current_arena);
*nextp= m_ip+1; *nextp= m_ip+1;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -2197,12 +2268,20 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp) ...@@ -2197,12 +2268,20 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp)
{ {
sp_cursor *c= thd->spcont->get_cursor(m_cursor); sp_cursor *c= thd->spcont->get_cursor(m_cursor);
int res; int res;
Query_arena backup_current_arena;
DBUG_ENTER("sp_instr_cfetch::execute"); DBUG_ENTER("sp_instr_cfetch::execute");
if (! c) if (! c)
res= -1; res= -1;
else else
{
thd->set_n_backup_item_arena(thd->spcont->callers_arena,
&backup_current_arena);
res= c->fetch(thd, &m_varlist); res= c->fetch(thd, &m_varlist);
thd->restore_backup_item_arena(thd->spcont->callers_arena,
&backup_current_arena);
}
*nextp= m_ip+1; *nextp= m_ip+1;
DBUG_RETURN(res); DBUG_RETURN(res);
} }
......
...@@ -32,7 +32,6 @@ sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax) ...@@ -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_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0),
m_hfound(-1), m_ccount(0) m_hfound(-1), m_ccount(0)
{ {
callers_mem_root= NULL;
in_handler= FALSE; in_handler= FALSE;
m_frame= (Item **)sql_alloc(fsize * sizeof(Item*)); m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t)); 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, ...@@ -47,17 +46,18 @@ sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr,
enum_field_types type) enum_field_types type)
{ {
extern Item *sp_eval_func_item(THD *thd, Item **it, 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 *it;
Item *reuse_it; Item *reuse_it;
Item *old_item_next; 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; int res;
LINT_INIT(old_item_next); LINT_INIT(old_item_next);
if ((reuse_it= get_item(idx))) if ((reuse_it= get_item(idx)))
old_item_next= reuse_it->next; 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) if (! it)
res= -1; res= -1;
else else
...@@ -67,7 +67,7 @@ sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr, ...@@ -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, // A reused item slot, where the constructor put it in the free_list,
// so we have to restore the 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; it->next= old_item_next;
} }
set_item(idx, it); set_item(idx, it);
......
...@@ -48,8 +48,14 @@ class sp_rcontext : public Sql_alloc ...@@ -48,8 +48,14 @@ class sp_rcontext : public Sql_alloc
public: public:
MEM_ROOT *callers_mem_root; // Used to store result fields
bool in_handler; 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); sp_rcontext(uint fsize, uint hmax, uint cmax);
......
...@@ -2251,7 +2251,6 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution() ...@@ -2251,7 +2251,6 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution()
{ {
TABLE_LIST *cur_table_ref= this; TABLE_LIST *cur_table_ref= this;
NESTED_JOIN *cur_nested_join; NESTED_JOIN *cur_nested_join;
LINT_INIT(cur_table_ref);
if (is_leaf_for_name_resolution()) if (is_leaf_for_name_resolution())
return this; return this;
......
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