mirror of
https://github.com/MariaDB/server.git
synced 2025-01-31 11:01:52 +01:00
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/usersnfs/pchardin/mysql-5.0
This commit is contained in:
commit
8d593d499d
6 changed files with 304 additions and 143 deletions
|
@ -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
|
||||
#
|
||||
|
|
353
sql/sp_head.cc
353
sql/sp_head.cc
|
@ -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;
|
||||
}
|
||||
case REAL_RESULT:
|
||||
{
|
||||
double d= it->val_real();
|
||||
switch (sp_map_result_type(type)) {
|
||||
case INT_RESULT:
|
||||
{
|
||||
longlong i= it->val_int();
|
||||
|
||||
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;
|
||||
}
|
||||
case DECIMAL_RESULT:
|
||||
if (it->null_value)
|
||||
{
|
||||
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);
|
||||
#ifndef DBUG_OFF
|
||||
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
|
||||
DBUG_PRINT("info", ("DECIMAL_RESULT: %s", dbug_decimal_as_string(dbug_buff, val)));
|
||||
#endif
|
||||
break;
|
||||
DBUG_PRINT("info", ("INT_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", ("INT_RESULT: %d", i));
|
||||
CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_int(i),
|
||||
use_callers_arena, &backup_current_arena);
|
||||
}
|
||||
case ROW_RESULT:
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
case REAL_RESULT:
|
||||
{
|
||||
double d= it->val_real();
|
||||
|
||||
if (it->null_value)
|
||||
{
|
||||
DBUG_PRINT("info", ("REAL_RESULT: 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));
|
||||
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)));
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue