diff options
Diffstat (limited to 'sql/sp.cc')
-rw-r--r-- | sql/sp.cc | 578 |
1 files changed, 236 insertions, 342 deletions
diff --git a/sql/sp.cc b/sql/sp.cc index aae825c6c0d..39a8b6a009f 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -17,7 +17,6 @@ #include "sp.h" #include "sp_head.h" #include "sp_cache.h" -#include "sql_trigger.h" #include <my_user.h> @@ -392,12 +391,13 @@ static Proc_table_intact proc_table_intact; \# Pointer to TABLE object of mysql.proc */ -TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) +TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup) { + TABLE_LIST table; + DBUG_ENTER("open_proc_table_for_read"); - TABLE_LIST table; - table.init_one_table("mysql", "proc", TL_READ); + table.init_one_table("mysql", 5, "proc", 4, "proc", TL_READ); if (open_system_tables_for_read(thd, &table, backup)) DBUG_RETURN(NULL); @@ -427,11 +427,11 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) static TABLE *open_proc_table_for_update(THD *thd) { + TABLE_LIST table_list; + TABLE *table; DBUG_ENTER("open_proc_table_for_update"); - TABLE *table; - TABLE_LIST table_list; - table_list.init_one_table("mysql", "proc", TL_WRITE); + table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE); if (!(table= open_system_table_for_update(thd, &table_list))) DBUG_RETURN(NULL); @@ -442,6 +442,7 @@ static TABLE *open_proc_table_for_update(THD *thd) close_thread_tables(thd); DBUG_RETURN(NULL); + } @@ -527,7 +528,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) String str(buff, sizeof(buff), &my_charset_bin); bool saved_time_zone_used= thd->time_zone_used; ulong sql_mode, saved_mode= thd->variables.sql_mode; - Open_tables_state open_tables_state_backup; + Open_tables_backup open_tables_state_backup; Stored_program_creation_ctx *creation_ctx; DBUG_ENTER("db_find_routine"); @@ -930,6 +931,11 @@ sp_create_routine(THD *thd, int type, sp_head *sp) if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) thd->clear_current_stmt_binlog_format_row(); + /* Grab an exclusive MDL lock. */ + if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION, + sp->m_db.str, sp->m_name.str)) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + saved_count_cuted_fields= thd->count_cuted_fields; thd->count_cuted_fields= CHECK_FIELD_WARN; @@ -1096,7 +1102,10 @@ sp_create_routine(THD *thd, int type, sp_head *sp) ret= SP_OK; if (table->file->ha_write_row(table->record[0])) ret= SP_WRITE_ROW_FAILED; - else if (mysql_bin_log.is_open()) + if (ret == SP_OK) + sp_cache_invalidate(); + + if (ret == SP_OK && mysql_bin_log.is_open()) { thd->clear_error(); @@ -1127,7 +1136,6 @@ sp_create_routine(THD *thd, int type, sp_head *sp) ret= SP_INTERNAL_ERROR; thd->variables.sql_mode= 0; } - } done: @@ -1179,6 +1187,11 @@ sp_drop_routine(THD *thd, int type, sp_name *name) if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) thd->clear_current_stmt_binlog_format_row(); + /* Grab an exclusive MDL lock. */ + if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION, + name->m_db.str, name->m_name.str)) + DBUG_RETURN(SP_DELETE_ROW_FAILED); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) @@ -1192,6 +1205,20 @@ sp_drop_routine(THD *thd, int type, sp_name *name) if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) ret= SP_INTERNAL_ERROR; sp_cache_invalidate(); + + /* + A lame workaround for lack of cache flush: + make sure the routine is at least gone from the + local cache. + */ + { + sp_head *sp; + sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? + &thd->sp_func_cache : &thd->sp_proc_cache); + sp= sp_cache_lookup(spc, name); + if (sp) + sp_cache_flush_obsolete(spc, &sp); + } } close_thread_tables(thd); @@ -1232,6 +1259,12 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || type == TYPE_ENUM_FUNCTION); + + /* Grab an exclusive MDL lock. */ + if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION, + name->m_db.str, name->m_name.str)) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + /* This statement will be replicated as a statement, even when using row-based replication. The flag will be reset at the end of the @@ -1244,6 +1277,30 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) { + if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators && + mysql_bin_log.is_open() && + (chistics->daccess == SP_CONTAINS_SQL || + chistics->daccess == SP_MODIFIES_SQL_DATA)) + { + char *ptr; + bool is_deterministic; + ptr= get_field(thd->mem_root, + table->field[MYSQL_PROC_FIELD_DETERMINISTIC]); + if (ptr == NULL) + { + ret= SP_INTERNAL_ERROR; + goto err; + } + is_deterministic= ptr[0] == 'N' ? FALSE : TRUE; + if (!is_deterministic) + { + my_message(ER_BINLOG_UNSAFE_ROUTINE, + ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0)); + ret= SP_INTERNAL_ERROR; + goto err; + } + } + store_record(table,record[1]); table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); @@ -1270,7 +1327,7 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) ret= SP_INTERNAL_ERROR; sp_cache_invalidate(); } - +err: close_thread_tables(thd); /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); @@ -1358,10 +1415,7 @@ err: bool sp_show_create_routine(THD *thd, int type, sp_name *name) { - bool err_status= TRUE; sp_head *sp; - sp_cache **cache = type == TYPE_ENUM_PROCEDURE ? - &thd->sp_proc_cache : &thd->sp_func_cache; DBUG_ENTER("sp_show_create_routine"); DBUG_PRINT("enter", ("name: %.*s", @@ -1371,28 +1425,29 @@ sp_show_create_routine(THD *thd, int type, sp_name *name) DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || type == TYPE_ENUM_FUNCTION); - if (type == TYPE_ENUM_PROCEDURE) + /* + @todo: Consider using prelocking for this code as well. Currently + SHOW CREATE PROCEDURE/FUNCTION is a dirty read of the data + dictionary, i.e. takes no metadata locks. + It is "safe" to do as long as it doesn't affect the results + of the binary log or the query cache, which currently it does not. + */ + if (sp_cache_routine(thd, type, name, FALSE, &sp)) + DBUG_RETURN(TRUE); + + if (sp == NULL || sp->show_create_routine(thd, type)) { /* - SHOW CREATE PROCEDURE may require two instances of one sp_head - object when SHOW CREATE PROCEDURE is called for the procedure that - is being executed. Basically, there is no actual recursion, so we - increase the recursion limit for this statement (kind of hack). - - SHOW CREATE FUNCTION does not require this because SHOW CREATE - statements are prohibitted within stored functions. - */ - - thd->variables.max_sp_recursion_depth++; + If we have insufficient privileges, pretend the routine + does not exist. + */ + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), + type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE", + name->m_name.str); + DBUG_RETURN(TRUE); } - if ((sp= sp_find_routine(thd, type, name, cache, FALSE))) - err_status= sp->show_create_routine(thd, type); - - if (type == TYPE_ENUM_PROCEDURE) - thd->variables.max_sp_recursion_depth--; - - DBUG_RETURN(err_status); + DBUG_RETURN(FALSE); } @@ -1576,7 +1631,7 @@ sp_routine_exists_in_table(THD *thd, int type, sp_name *name) { TABLE *table; int ret; - Open_tables_state open_tables_state_backup; + Open_tables_backup open_tables_state_backup; if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) ret= SP_OPEN_TABLE_FAILED; @@ -1590,73 +1645,12 @@ sp_routine_exists_in_table(THD *thd, int type, sp_name *name) } -/** - Structure that represents element in the set of stored routines - used by statement or routine. -*/ -struct Sroutine_hash_entry; - -struct Sroutine_hash_entry -{ - /** - Set key consisting of one-byte routine type and quoted routine name. - */ - LEX_STRING key; - /** - Next element in list linking all routines in set. See also comments - for LEX::sroutine/sroutine_list and sp_head::m_sroutines. - */ - Sroutine_hash_entry *next; - /** - Uppermost view which directly or indirectly uses this routine. - 0 if routine is not used in view. Note that it also can be 0 if - statement uses routine both via view and directly. - */ - TABLE_LIST *belong_to_view; -}; - - extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, my_bool first) { Sroutine_hash_entry *rn= (Sroutine_hash_entry *)ptr; - *plen= rn->key.length; - return (uchar *)rn->key.str; -} - - -/** - Check if - - current statement (the one in thd->lex) needs table prelocking - - first routine in thd->lex->sroutines_list needs to execute its body in - prelocked mode. - - @param thd Current thread, thd->lex is the statement to be - checked. - @param[out] need_prelocking TRUE - prelocked mode should be activated - before executing the statement; - FALSE - Don't activate prelocking - @param[out] first_no_prelocking TRUE - Tables used by first routine in - thd->lex->sroutines_list should be - prelocked. FALSE - Otherwise. - - @note - This function assumes that for any "CALL proc(...)" statement routines_list - will have 'proc' as first element (it may have several, consider e.g. - "proc(sp_func(...)))". This property is currently guaranted by the parser. -*/ - -void sp_get_prelocking_info(THD *thd, bool *need_prelocking, - bool *first_no_prelocking) -{ - Sroutine_hash_entry *routine; - routine= (Sroutine_hash_entry*)thd->lex->sroutines_list.first; - - DBUG_ASSERT(routine); - bool first_is_procedure= (routine->key.str[0] == TYPE_ENUM_PROCEDURE); - - *first_no_prelocking= first_is_procedure; - *need_prelocking= !first_is_procedure || test(routine->next); + *plen= rn->mdl_request.key.length(); + return (uchar *)rn->mdl_request.key.ptr(); } @@ -1666,11 +1660,11 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking, In case when statement uses stored routines but does not need prelocking (i.e. it does not use any tables) we will access the - elements of LEX::sroutines set on prepared statement re-execution. - Because of this we have to allocate memory for both hash element - and copy of its key in persistent arena. + elements of Query_tables_list::sroutines set on prepared statement + re-execution. Because of this we have to allocate memory for both + hash element and copy of its key in persistent arena. - @param lex LEX representing statement + @param prelocking_ctx Prelocking context of the statement @param arena Arena in which memory for new element will be allocated @param key Key for the hash representing set @@ -1678,7 +1672,7 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking, (0 if routine is not used by view) @note - Will also add element to end of 'LEX::sroutines_list' list. + Will also add element to end of 'Query_tables_list::sroutines_list' list. @todo When we will got rid of these accesses on re-executions we will be @@ -1693,28 +1687,25 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking, the set). */ -static bool add_used_routine(LEX *lex, Query_arena *arena, - const LEX_STRING *key, - TABLE_LIST *belong_to_view) +bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, + const MDL_key *key, TABLE_LIST *belong_to_view) { - my_hash_init_opt(&lex->sroutines, system_charset_info, + my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info, Query_tables_list::START_SROUTINES_HASH_SIZE, 0, 0, sp_sroutine_key, 0, 0); - if (!my_hash_search(&lex->sroutines, (uchar *)key->str, key->length)) + if (!my_hash_search(&prelocking_ctx->sroutines, key->ptr(), key->length())) { Sroutine_hash_entry *rn= - (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) + - key->length + 1); + (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry)); if (!rn) // OOM. Error will be reported using fatal_error(). return FALSE; - rn->key.length= key->length; - rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry); - memcpy(rn->key.str, key->str, key->length + 1); - if (my_hash_insert(&lex->sroutines, (uchar *)rn)) + rn->mdl_request.init(key, MDL_SHARED); + if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn)) return FALSE; - lex->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next); + prelocking_ctx->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next); rn->belong_to_view= belong_to_view; + rn->m_sp_cache_version= 0; return TRUE; } return FALSE; @@ -1728,24 +1719,27 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, To be friendly towards prepared statements one should pass persistent arena as second argument. - @param lex LEX representing statement - @param arena arena in which memory for new element of the set - will be allocated - @param rt routine name - @param rt_type routine type (one of TYPE_ENUM_PROCEDURE/...) + @param prelocking_ctx Prelocking context of the statement + @param arena Arena in which memory for new element of the set + will be allocated + @param rt Routine name + @param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...) @note - Will also add element to end of 'LEX::sroutines_list' list (and will - take into account that this is explicitly used routine). + Will also add element to end of 'Query_tables_list::sroutines_list' list + (and will take into account that this is an explicitly used routine). */ -void sp_add_used_routine(LEX *lex, Query_arena *arena, +void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, sp_name *rt, char rt_type) { - rt->set_routine_type(rt_type); - (void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0); - lex->sroutines_list_own_last= lex->sroutines_list.next; - lex->sroutines_list_own_elements= lex->sroutines_list.elements; + MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_key::FUNCTION : + MDL_key::PROCEDURE, + rt->m_db.str, rt->m_name.str); + (void)sp_add_used_routine(prelocking_ctx, arena, &key, 0); + prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next; + prelocking_ctx->sroutines_list_own_elements= + prelocking_ctx->sroutines_list.elements; } @@ -1753,13 +1747,14 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena, Remove routines which are only indirectly used by statement from the set of routines used by this statement. - @param lex LEX representing statement + @param prelocking_ctx Prelocking context of the statement */ -void sp_remove_not_own_routines(LEX *lex) +void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx) { Sroutine_hash_entry *not_own_rt, *next_rt; - for (not_own_rt= *(Sroutine_hash_entry **)lex->sroutines_list_own_last; + for (not_own_rt= + *(Sroutine_hash_entry **)prelocking_ctx->sroutines_list_own_last; not_own_rt; not_own_rt= next_rt) { /* @@ -1767,12 +1762,13 @@ void sp_remove_not_own_routines(LEX *lex) but we want to be more future-proof. */ next_rt= not_own_rt->next; - my_hash_delete(&lex->sroutines, (uchar *)not_own_rt); + my_hash_delete(&prelocking_ctx->sroutines, (uchar *)not_own_rt); } - *(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL; - lex->sroutines_list.next= lex->sroutines_list_own_last; - lex->sroutines_list.elements= lex->sroutines_list_own_elements; + *(Sroutine_hash_entry **)prelocking_ctx->sroutines_list_own_last= NULL; + prelocking_ctx->sroutines_list.next= prelocking_ctx->sroutines_list_own_last; + prelocking_ctx->sroutines_list.elements= + prelocking_ctx->sroutines_list_own_elements; } @@ -1801,7 +1797,8 @@ bool sp_update_sp_used_routines(HASH *dst, HASH *src) for (uint i=0 ; i < src->records ; i++) { Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i); - if (!my_hash_search(dst, (uchar *)rt->key.str, rt->key.length)) + if (!my_hash_search(dst, (uchar *)rt->mdl_request.key.ptr(), + rt->mdl_request.key.length())) { if (my_hash_insert(dst, (uchar *)rt)) return TRUE; @@ -1816,23 +1813,24 @@ bool sp_update_sp_used_routines(HASH *dst, HASH *src) routines used by statement. @param thd Thread context - @param lex LEX representing statement + @param prelocking_ctx Prelocking context of the statement @param src Hash representing set from which routines will be added @param belong_to_view Uppermost view which uses these routines, 0 if none - @note - It will also add elements to end of 'LEX::sroutines_list' list. + @note It will also add elements to end of + 'Query_tables_list::sroutines_list' list. */ -static void -sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src, - TABLE_LIST *belong_to_view) +void +sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, + HASH *src, TABLE_LIST *belong_to_view) { for (uint i=0 ; i < src->records ; i++) { Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i); - (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view); + (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + &rt->mdl_request.key, belong_to_view); } } @@ -1842,243 +1840,139 @@ sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src, routines used by statement. @param thd Thread context - @param lex LEX representing statement + @param prelocking_ctx Prelocking context of the statement @param src List representing set from which routines will be added @param belong_to_view Uppermost view which uses these routines, 0 if none - @note - It will also add elements to end of 'LEX::sroutines_list' list. + @note It will also add elements to end of + 'Query_tables_list::sroutines_list' list. */ -static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src, - TABLE_LIST *belong_to_view) +void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, + SQL_LIST *src, TABLE_LIST *belong_to_view) { for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first; rt; rt= rt->next) - (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view); + (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + &rt->mdl_request.key, belong_to_view); } /** - Cache sub-set of routines used by statement, add tables used by these - routines to statement table list. Do the same for all routines used - by these routines. - - @param thd thread context - @param lex LEX representing statement - @param start first routine from the list of routines to be cached - (this list defines mentioned sub-set). - @param first_no_prelock If true, don't add tables or cache routines used by - the body of the first routine (i.e. *start) - will be executed in non-prelocked mode. - @param tabs_changed Set to TRUE some tables were added, FALSE otherwise - - @note - If some function is missing this won't be reported here. - Instead this fact will be discovered during query execution. - - @retval - 0 success - @retval - non-0 failure + A helper wrapper around sp_cache_routine() to use from + prelocking until 'sp_name' is eradicated as a class. */ -static int -sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, - Sroutine_hash_entry *start, - bool first_no_prelock) +int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, + bool lookup_only, sp_head **sp) { - int ret= 0; - bool first= TRUE; - DBUG_ENTER("sp_cache_routines_and_add_tables_aux"); - - for (Sroutine_hash_entry *rt= start; rt; rt= rt->next) - { - sp_name name(thd, rt->key.str, rt->key.length); - int type= rt->key.str[0]; - sp_head *sp; - - if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ? - &thd->sp_func_cache : &thd->sp_proc_cache), - &name))) - { - switch ((ret= db_find_routine(thd, type, &name, &sp))) - { - case SP_OK: - { - if (type == TYPE_ENUM_FUNCTION) - sp_cache_insert(&thd->sp_func_cache, sp); - else - sp_cache_insert(&thd->sp_proc_cache, sp); - } - break; - case SP_KEY_NOT_FOUND: - ret= SP_OK; - break; - default: - /* Query might have been killed, don't set error. */ - if (thd->killed) - break; - /* - Any error when loading an existing routine is either some problem - with the mysql.proc table, or a parse error because the contents - has been tampered with (in which case we clear that error). - */ - if (ret == SP_PARSE_ERROR) - thd->clear_error(); - /* - If we cleared the parse error, or when db_find_routine() flagged - an error with it's return value without calling my_error(), we - set the generic "mysql.proc table corrupt" error here. - */ - if (! thd->is_error()) - { - /* - SP allows full NAME_LEN chars thus he have to allocate enough - size in bytes. Otherwise there is stack overrun could happen - if multibyte sequence is `name`. `db` is still safe because the - rest of the server checks agains NAME_LEN bytes and not chars. - Hence, the overrun happens only if the name is in length > 32 and - uses multibyte (cyrillic, greek, etc.) - */ - char n[NAME_LEN*2+2]; - - /* m_qname.str is not always \0 terminated */ - memcpy(n, name.m_qname.str, name.m_qname.length); - n[name.m_qname.length]= '\0'; - my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret); - } - break; - } - } - if (sp) - { - if (!(first && first_no_prelock)) - { - sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines, - rt->belong_to_view); - (void)sp->add_used_tables_to_table_list(thd, &lex->query_tables_last, - rt->belong_to_view); - } - sp->propagate_attributes(lex); - } - first= FALSE; - } - DBUG_RETURN(ret); -} - - -/** - Cache all routines from the set of used by statement, add tables used - by those routines to statement table list. Do the same for all routines - used by those routines. - - @param thd thread context - @param lex LEX representing statement - @param first_no_prelock If true, don't add tables or cache routines used by - the body of the first routine (i.e. *start) + char qname_buff[NAME_LEN*2+1+1]; + sp_name name(&rt->mdl_request.key, qname_buff); + MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace(); + int type= ((mdl_type == MDL_key::FUNCTION) ? + TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE); - @retval - 0 success - @retval - non-0 failure -*/ + /* + Check that we have an MDL lock on this routine, unless it's a top-level + CALL. The assert below should be unambiguous: the first element + in sroutines_list has an MDL lock unless it's a top-level call, or a + trigger, but triggers can't occur here (see the preceding assert). + */ + DBUG_ASSERT(rt->mdl_request.ticket || + rt == (Sroutine_hash_entry*) thd->lex->sroutines_list.first); -int -sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock) -{ - return sp_cache_routines_and_add_tables_aux(thd, lex, - (Sroutine_hash_entry *)lex->sroutines_list.first, - first_no_prelock); + return sp_cache_routine(thd, type, &name, lookup_only, sp); } /** - Add all routines used by view to the set of routines used by - statement. - - Add tables used by those routines to statement table list. Do the same - for all routines used by these routines. - - @param thd Thread context - @param lex LEX representing statement - @param view Table list element representing view - - @retval - 0 success - @retval - non-0 failure + Ensure that routine is present in cache by loading it from the mysql.proc + table if needed. If the routine is present but old, reload it. + Emit an appropriate error if there was a problem during + loading. + + @param[in] thd Thread context. + @param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE). + @param[in] name Name of routine. + @param[in] lookup_only Only check that the routine is in the cache. + If it's not, don't try to load. If it is present, + but old, don't try to reload. + @param[out] sp Pointer to sp_head object for routine, NULL if routine was + not found. + + @retval 0 Either routine is found and was succesfully loaded into cache + or it does not exist. + @retval non-0 Error while loading routine from mysql,proc table. */ -int -sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, TABLE_LIST *view) +int sp_cache_routine(THD *thd, int type, sp_name *name, + bool lookup_only, sp_head **sp) { - Sroutine_hash_entry **last_cached_routine_ptr= - (Sroutine_hash_entry **)lex->sroutines_list.next; - sp_update_stmt_used_routines(thd, lex, &view->view->sroutines_list, - view->top_table()); - return sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, FALSE); -} - - -/** - Add triggers for table to the set of routines used by statement. - Add tables used by them to statement table list. Do the same for - all implicitly used routines. + int ret= 0; + sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? + &thd->sp_func_cache : &thd->sp_proc_cache); - @param thd thread context - @param lex LEX respresenting statement - @param table Table list element for table with trigger + DBUG_ENTER("sp_cache_routine"); - @retval - 0 success - @retval - non-0 failure -*/ + DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE); -int -sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, - TABLE_LIST *table) -{ - int ret= 0; - DBUG_ENTER("sp_cache_routines_and_add_tables_for_triggers"); + *sp= sp_cache_lookup(spc, name); - Sroutine_hash_entry **last_cached_routine_ptr= - (Sroutine_hash_entry **)lex->sroutines_list.next; + if (lookup_only) + DBUG_RETURN(SP_OK); - if (static_cast<int>(table->lock_type) >= - static_cast<int>(TL_WRITE_ALLOW_WRITE)) + if (*sp) { - for (int i= 0; i < (int)TRG_EVENT_MAX; i++) - { - if (table->trg_event_map & - static_cast<uint8>(1 << static_cast<int>(i))) + sp_cache_flush_obsolete(spc, sp); + if (*sp) + DBUG_RETURN(SP_OK); + } + + switch ((ret= db_find_routine(thd, type, name, sp))) + { + case SP_OK: + sp_cache_insert(spc, *sp); + break; + case SP_KEY_NOT_FOUND: + ret= SP_OK; + break; + default: + /* Query might have been killed, don't set error. */ + if (thd->killed) + break; + /* + Any error when loading an existing routine is either some problem + with the mysql.proc table, or a parse error because the contents + has been tampered with (in which case we clear that error). + */ + if (ret == SP_PARSE_ERROR) + thd->clear_error(); + /* + If we cleared the parse error, or when db_find_routine() flagged + an error with it's return value without calling my_error(), we + set the generic "mysql.proc table corrupt" error here. + */ + if (! thd->is_error()) { - for (int j= 0; j < (int)TRG_ACTION_MAX; j++) - { - /* We can have only one trigger per action type currently */ - sp_head *trigger= table->table->triggers->bodies[i][j]; - if (trigger && - add_used_routine(lex, thd->stmt_arena, &trigger->m_sroutines_key, - table->belong_to_view)) - { - trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last, - table->belong_to_view); - trigger->propagate_attributes(lex); - sp_update_stmt_used_routines(thd, lex, - &trigger->m_sroutines, - table->belong_to_view); - } - } + /* + SP allows full NAME_LEN chars thus he have to allocate enough + size in bytes. Otherwise there is stack overrun could happen + if multibyte sequence is `name`. `db` is still safe because the + rest of the server checks agains NAME_LEN bytes and not chars. + Hence, the overrun happens only if the name is in length > 32 and + uses multibyte (cyrillic, greek, etc.) + */ + char n[NAME_LEN*2+2]; + + /* m_qname.str is not always \0 terminated */ + memcpy(n, name->m_qname.str, name->m_qname.length); + n[name->m_qname.length]= '\0'; + my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret); } - } + break; } - ret= sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, - FALSE); DBUG_RETURN(ret); } |