summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/sp_head.cc343
-rw-r--r--sql/sp_rcontext.cc10
-rw-r--r--sql/sp_rcontext.h8
3 files changed, 223 insertions, 138 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 11677848cb8..26d76804fca 100644
--- a/sql/sp_head.cc
+++ b/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,91 +176,100 @@ 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);
}
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index d0817e43790..748c09f56c7 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -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);
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 856beb13f6d..dedbc7bdef1 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -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);