diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/sp_head.cc | 343 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 10 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 8 |
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); |