diff options
Diffstat (limited to 'sql/sp_head.cc')
-rw-r--r-- | sql/sp_head.cc | 657 |
1 files changed, 411 insertions, 246 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 02c006d01ee..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); } @@ -574,13 +605,22 @@ sp_head::execute(THD *thd) sp_rcontext *ctx; int ret= 0; 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)) { @@ -626,6 +666,8 @@ sp_head::execute(THD *thd) old_query_id= thd->query_id; old_derived_tables= thd->derived_tables; thd->derived_tables= 0; + save_sql_mode= thd->variables.sql_mode; + thd->variables.sql_mode= m_sql_mode; /* It is also more efficient to save/restore current thd->lex once when do it in each instruction @@ -647,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; @@ -656,7 +710,9 @@ sp_head::execute(THD *thd) if (i == NULL) break; DBUG_PRINT("execute", ("Instruction %u", ip)); - thd->set_time(); // Make current_time() et al work + /* Don't change NOW() in FUNCTION or TRIGGER */ + if (!thd->in_sub_stmt) + thd->set_time(); // Make current_time() et al work /* We have to set thd->current_arena before executing the instruction to store in the instruction free_list all new items, created @@ -665,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- @@ -675,23 +732,30 @@ sp_head::execute(THD *thd) cleanup_items(i->free_list); i->state= Query_arena::EXECUTED; - // 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 - // errors don't return a non-zero value. - // We also have to check even if thd->killed != 0, since some - // errors return with this even when a handler has been found - // (e.g. "bad data"). + /* 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 + errors don't return a non-zero value. + We also have to check even if thd->killed != 0, since some + errors return with this even when a handler has been found + (e.g. "bad data"). + */ if (ctx) { uint hf; - switch (ctx->found_handler(&hip, &hf)) - { + switch (ctx->found_handler(&hip, &hf)) { 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; @@ -705,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()); @@ -715,6 +782,7 @@ sp_head::execute(THD *thd) thd->query_id= old_query_id; DBUG_ASSERT(!thd->derived_tables); thd->derived_tables= old_derived_tables; + thd->variables.sql_mode= save_sql_mode; thd->current_arena= old_arena; state= EXECUTED; @@ -730,7 +798,7 @@ sp_head::execute(THD *thd) if (dbchanged) { if (! thd->killed) - ret= sp_change_db(thd, olddb, 0); + ret= mysql_change_db(thd, olddb, 0); } m_is_invoked= FALSE; DBUG_RETURN(ret); @@ -750,28 +818,25 @@ 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) { - // Need to use my_printf_error here, or it will not terminate the - // invoking query properly. + /* + Need to use my_printf_error here, or it will not terminate the + invoking query properly. + */ my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "FUNCTION", m_qname.str, params, argcount); 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); @@ -780,9 +845,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) DBUG_RETURN(-1); } } - // The rest of the frame are local variables which are all IN. - // Default all variables to null (those with default clauses will - // be set by an set instruction). + /* + The rest of the frame are local variables which are all IN. + Default all variables to null (those with default clauses will + be set by an set instruction). + */ { Item_null *nit= NULL; // Re-use this, and only create if needed for (; i < csize ; i++) @@ -793,24 +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); @@ -819,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); } @@ -853,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) { @@ -864,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) { @@ -873,13 +937,10 @@ 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? + DBUG_PRINT("info",(" %.*s: eval args", m_name.length, m_name.str)); for (i = 0 ; (it= li++) && i < params ; i++) { sp_pvar_t *pvar= m_pcont->find_pvar(i); @@ -903,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 @@ -916,32 +977,50 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) } } - // The rest of the frame are local variables which are all IN. - // Default all variables to null (those with default clauses will - // be set by an set instruction). + /* + Okay, got values for all arguments. Close tables that might be used by + arguments evaluation. If arguments evaluation required prelocking mode, + we'll leave it here. + */ + if (!thd->in_sub_stmt) + close_thread_tables(thd, 0, 0); + + DBUG_PRINT("info",(" %.*s: eval args done", m_name.length, m_name.str)); + /* + The rest of the frame are local variables which are all IN. + Default all variables to null (those with default clauses will + be set by an set instruction). + */ for (; i < csize ; i++) { if (! nit) 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) { List_iterator<Item> li(*args); Item *it; - // Copy back all OUT or INOUT values to the previous frame, or - // set global user variables + /* + Copy back all OUT or INOUT values to the previous frame, or + set global user variables + */ for (uint i = 0 ; (it= li++) && i < params ; i++) { sp_pvar_t *pvar= m_pcont->find_pvar(i); @@ -956,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; @@ -971,9 +1058,11 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) octx->set_item(offset, copy); if (orig && copy == orig) { - // 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; + /* + A reused item slot, where the constructor put it in the + free_list, so we have to restore the list. + */ + thd->spcont->callers_arena->free_list= o_free_list; copy->next= o_item_next; } } @@ -1001,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); } @@ -1245,8 +1333,6 @@ sp_head::show_create_procedure(THD *thd) String buffer(buff, sizeof(buff), system_charset_info); int res; List<Item> field_list; - ulong old_sql_mode; - sys_var *sql_mode_var; byte *sql_mode_str; ulong sql_mode_len; bool full_access; @@ -1258,19 +1344,13 @@ sp_head::show_create_procedure(THD *thd) if (check_show_routine_access(thd, this, &full_access)) return 1; - - old_sql_mode= thd->variables.sql_mode; - thd->variables.sql_mode= m_sql_mode; - sql_mode_var= find_sys_var("SQL_MODE", 8); - if (sql_mode_var) - { - sql_mode_str= sql_mode_var->value_ptr(thd, OPT_SESSION, 0); - sql_mode_len= strlen((char*) sql_mode_str); - } + sql_mode_str= + sys_var_thd_sql_mode::symbolic_mode_representation(thd, + m_sql_mode, + &sql_mode_len); field_list.push_back(new Item_empty_string("Procedure", NAME_LEN)); - if (sql_mode_var) - field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); + field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); // 1024 is for not to confuse old clients field_list.push_back(new Item_empty_string("Create Procedure", max(buffer.length(), 1024))); @@ -1282,15 +1362,13 @@ sp_head::show_create_procedure(THD *thd) } protocol->prepare_for_resend(); protocol->store(m_name.str, m_name.length, system_charset_info); - if (sql_mode_var) - protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); + protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); if (full_access) protocol->store(m_defstr.str, m_defstr.length, system_charset_info); res= protocol->write(); send_eof(thd); done: - thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(res); } @@ -1326,8 +1404,6 @@ sp_head::show_create_function(THD *thd) String buffer(buff, sizeof(buff), system_charset_info); int res; List<Item> field_list; - ulong old_sql_mode; - sys_var *sql_mode_var; byte *sql_mode_str; ulong sql_mode_len; bool full_access; @@ -1339,18 +1415,12 @@ sp_head::show_create_function(THD *thd) if (check_show_routine_access(thd, this, &full_access)) return 1; - old_sql_mode= thd->variables.sql_mode; - thd->variables.sql_mode= m_sql_mode; - sql_mode_var= find_sys_var("SQL_MODE", 8); - if (sql_mode_var) - { - sql_mode_str= sql_mode_var->value_ptr(thd, OPT_SESSION, 0); - sql_mode_len= strlen((char*) sql_mode_str); - } - + sql_mode_str= + sys_var_thd_sql_mode::symbolic_mode_representation(thd, + m_sql_mode, + &sql_mode_len); field_list.push_back(new Item_empty_string("Function",NAME_LEN)); - if (sql_mode_var) - field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); + field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); field_list.push_back(new Item_empty_string("Create Function", max(buffer.length(),1024))); if (protocol->send_fields(&field_list, @@ -1361,15 +1431,13 @@ sp_head::show_create_function(THD *thd) } protocol->prepare_for_resend(); protocol->store(m_name.str, m_name.length, system_charset_info); - if (sql_mode_var) - protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); + protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); if (full_access) protocol->store(m_defstr.str, m_defstr.length, system_charset_info); res= protocol->write(); send_eof(thd); done: - thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(res); } @@ -1424,8 +1492,6 @@ sp_head::opt_mark(uint ip) ip= i->opt_mark(this); } -// ------------------------------------------------------------------ - /* Prepare LEX and thread for execution of instruction, if requested open @@ -1480,8 +1546,27 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, implemented at the same time as ability not to store LEX for instruction if it is not really used. */ - reinit_stmt_before_use(thd, m_lex); + if (thd->prelocked_mode == NON_PRELOCKED) + { + /* + This statement will enter/leave prelocked mode on its own. + Entering prelocked mode changes table list and related members + of LEX, so we'll need to restore them. + */ + if (lex_query_tables_own_last) + { + /* + We've already entered/left prelocked mode with this statement. + Attach the list of tables that need to be prelocked and mark m_lex + as having such list attached. + */ + *lex_query_tables_own_last= prelocking_tables; + m_lex->mark_as_requiring_prelocking(lex_query_tables_own_last); + } + } + + reinit_stmt_before_use(thd, m_lex); /* If requested check whenever we have access to tables in LEX's table list and open and lock them before executing instructtions core function. @@ -1498,7 +1583,28 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, thd->proc_info="closing tables"; close_thread_tables(thd); + thd->proc_info= 0; + if (m_lex->query_tables_own_last) + { + /* + We've entered and left prelocking mode when executing statement + stored in m_lex. + m_lex->query_tables(->next_global)* list now has a 'tail' - a list + of tables that are added for prelocking. (If this is the first + execution, the 'tail' was added by open_tables(), otherwise we've + attached it above in this function). + Now we'll save the 'tail', and detach it. + */ + DBUG_ASSERT(!lex_query_tables_own_last || + lex_query_tables_own_last == m_lex->query_tables_own_last && + prelocking_tables == *(m_lex->query_tables_own_last)); + + lex_query_tables_own_last= m_lex->query_tables_own_last; + prelocking_tables= *lex_query_tables_own_last; + *lex_query_tables_own_last= NULL; + m_lex->mark_as_requiring_prelocking(NULL); + } thd->rollback_item_tree_changes(); /* @@ -1514,9 +1620,10 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, } -// -// sp_instr -// +/* + sp_instr class functions +*/ + int sp_instr::exec_core(THD *thd, uint *nextp) { DBUG_ASSERT(0); @@ -1524,9 +1631,10 @@ int sp_instr::exec_core(THD *thd, uint *nextp) } -// -// sp_instr_stmt -// +/* + sp_instr_stmt class functions +*/ + int sp_instr_stmt::execute(THD *thd, uint *nextp) { @@ -1571,9 +1679,11 @@ sp_instr_stmt::exec_core(THD *thd, uint *nextp) return res; } -// -// sp_instr_set -// + +/* + sp_instr_set class functions +*/ + int sp_instr_set::execute(THD *thd, uint *nextp) { @@ -1583,6 +1693,7 @@ sp_instr_set::execute(THD *thd, uint *nextp) DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this)); } + int sp_instr_set::exec_core(THD *thd, uint *nextp) { @@ -1603,9 +1714,10 @@ sp_instr_set::print(String *str) } -// -// sp_instr_set_trigger_field -// +/* + sp_instr_set_trigger_field class functions +*/ + int sp_instr_set_trigger_field::execute(THD *thd, uint *nextp) { @@ -1636,9 +1748,11 @@ sp_instr_set_trigger_field::print(String *str) value->print(str); } -// -// sp_instr_jump -// + +/* + sp_instr_jump class functions +*/ + int sp_instr_jump::execute(THD *thd, uint *nextp) { @@ -1697,9 +1811,10 @@ sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp) m_ip= dst; } -// -// sp_instr_jump_if -// + +/* + sp_instr_jump_if class functions +*/ int sp_instr_jump_if::execute(THD *thd, uint *nextp) @@ -1755,9 +1870,11 @@ sp_instr_jump_if::opt_mark(sp_head *sp) return m_ip+1; } -// -// sp_instr_jump_if_not -// + +/* + sp_instr_jump_if_not class functions +*/ + int sp_instr_jump_if_not::execute(THD *thd, uint *nextp) { @@ -1788,6 +1905,7 @@ sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp) return res; } + void sp_instr_jump_if_not::print(String *str) { @@ -1798,6 +1916,7 @@ sp_instr_jump_if_not::print(String *str) m_expr->print(str); } + uint sp_instr_jump_if_not::opt_mark(sp_head *sp) { @@ -1813,9 +1932,10 @@ sp_instr_jump_if_not::opt_mark(sp_head *sp) return m_ip+1; } -// -// sp_instr_freturn -// + +/* + sp_instr_freturn class functions +*/ int sp_instr_freturn::execute(THD *thd, uint *nextp) @@ -1831,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 @@ -1854,9 +1974,10 @@ sp_instr_freturn::print(String *str) m_value->print(str); } -// -// sp_instr_hpush_jump -// +/* + sp_instr_hpush_jump class functions +*/ + int sp_instr_hpush_jump::execute(THD *thd, uint *nextp) { @@ -1900,9 +2021,11 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp) return m_ip+1; } -// -// sp_instr_hpop -// + +/* + sp_instr_hpop class functions +*/ + int sp_instr_hpop::execute(THD *thd, uint *nextp) { @@ -1927,9 +2050,10 @@ sp_instr_hpop::backpatch(uint dest, sp_pcontext *dst_ctx) } -// -// sp_instr_hreturn -// +/* + sp_instr_hreturn class functions +*/ + int sp_instr_hreturn::execute(THD *thd, uint *nextp) { @@ -1945,6 +2069,7 @@ sp_instr_hreturn::execute(THD *thd, uint *nextp) DBUG_RETURN(0); } + void sp_instr_hreturn::print(String *str) { @@ -1955,6 +2080,7 @@ sp_instr_hreturn::print(String *str) str->qs_append(m_dest); } + uint sp_instr_hreturn::opt_mark(sp_head *sp) { @@ -1968,27 +2094,45 @@ sp_instr_hreturn::opt_mark(sp_head *sp) } -// -// sp_instr_cpush -// +/* + sp_instr_cpush class functions +*/ + 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); } + void sp_instr_cpush::print(String *str) { str->append("cpush"); } -// -// sp_instr_cpop -// + +/* + sp_instr_cpop class functions +*/ + int sp_instr_cpop::execute(THD *thd, uint *nextp) { @@ -1998,6 +2142,7 @@ sp_instr_cpop::execute(THD *thd, uint *nextp) DBUG_RETURN(0); } + void sp_instr_cpop::print(String *str) { @@ -2012,9 +2157,11 @@ sp_instr_cpop::backpatch(uint dest, sp_pcontext *dst_ctx) m_count= m_ctx->diff_cursors(dst_ctx); } -// -// sp_instr_copen -// + +/* + sp_instr_copen class functions +*/ + int sp_instr_copen::execute(THD *thd, uint *nextp) { @@ -2082,9 +2229,11 @@ sp_instr_copen::print(String *str) str->qs_append(m_cursor); } -// -// sp_instr_cclose -// + +/* + sp_instr_cclose class functions +*/ + int sp_instr_cclose::execute(THD *thd, uint *nextp) { @@ -2100,6 +2249,7 @@ sp_instr_cclose::execute(THD *thd, uint *nextp) DBUG_RETURN(res); } + void sp_instr_cclose::print(String *str) { @@ -2108,24 +2258,35 @@ sp_instr_cclose::print(String *str) str->qs_append(m_cursor); } -// -// sp_instr_cfetch -// + +/* + sp_instr_cfetch class functions +*/ + int 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); } + void sp_instr_cfetch::print(String *str) { @@ -2143,9 +2304,11 @@ sp_instr_cfetch::print(String *str) } } -// -// sp_instr_error -// + +/* + sp_instr_error class functions +*/ + int sp_instr_error::execute(THD *thd, uint *nextp) { @@ -2156,6 +2319,7 @@ sp_instr_error::execute(THD *thd, uint *nextp) DBUG_RETURN(-1); } + void sp_instr_error::print(String *str) { @@ -2164,12 +2328,12 @@ sp_instr_error::print(String *str) str->qs_append(m_errcode); } -/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ -// -// Security context swapping -// +/* + Security context swapping +*/ #ifndef NO_EMBEDDED_ACCESS_CHECKS void @@ -2418,11 +2582,12 @@ sp_head::add_used_tables_to_table_list(THD *thd, DBUG_RETURN(result); } + /* - * Simple function for adding an explicetly named (systems) table to - * the global table list, e.g. "mysql", "proc". - * - */ + Simple function for adding an explicetly named (systems) table to + the global table list, e.g. "mysql", "proc". +*/ + TABLE_LIST * sp_add_to_query_tables(THD *thd, LEX *lex, const char *db, const char *name, |