diff options
Diffstat (limited to 'sql/sp.cc')
-rw-r--r-- | sql/sp.cc | 157 |
1 files changed, 115 insertions, 42 deletions
diff --git a/sql/sp.cc b/sql/sp.cc index e6cf43c73a8..283db3539c9 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -27,7 +27,7 @@ #include "sql_acl.h" // SUPER_ACL #include "sp_head.h" #include "sp_cache.h" -#include "lock.h" // lock_routine_name +#include "lock.h" // lock_object_name #include <my_user.h> @@ -441,7 +441,7 @@ static TABLE *open_proc_table_for_update(THD *thd) { TABLE_LIST table_list; TABLE *table; - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_proc_table_for_update"); table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE); @@ -924,6 +924,8 @@ sp_create_routine(THD *thd, int type, sp_head *sp) TABLE *table; char definer[USER_HOST_BUFF_SIZE]; ulong saved_mode= thd->variables.sql_mode; + MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? + MDL_key::FUNCTION : MDL_key::PROCEDURE; CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str); @@ -943,8 +945,7 @@ sp_create_routine(THD *thd, int type, sp_head *sp) type == TYPE_ENUM_FUNCTION); /* Grab an exclusive MDL lock. */ - if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION, - sp->m_db.str, sp->m_name.str)) + if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str)) DBUG_RETURN(SP_OPEN_TABLE_FAILED); /* Reset sql_mode during data dictionary operations. */ @@ -1192,6 +1193,8 @@ sp_drop_routine(THD *thd, int type, sp_name *name) TABLE *table; int ret; bool save_binlog_row_based; + MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? + MDL_key::FUNCTION : MDL_key::PROCEDURE; DBUG_ENTER("sp_drop_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s", type, (int) name->m_name.length, name->m_name.str)); @@ -1200,8 +1203,7 @@ sp_drop_routine(THD *thd, int type, sp_name *name) 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)) + if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str)) DBUG_RETURN(SP_DELETE_ROW_FAILED); if (!(table= open_proc_table_for_update(thd))) @@ -1272,6 +1274,8 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) TABLE *table; int ret; bool save_binlog_row_based; + MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? + MDL_key::FUNCTION : MDL_key::PROCEDURE; DBUG_ENTER("sp_update_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s", type, (int) name->m_name.length, name->m_name.str)); @@ -1280,8 +1284,7 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) 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)) + if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str)) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if (!(table= open_proc_table_for_update(thd))) @@ -1357,6 +1360,108 @@ err: /** + This internal handler is used to trap errors from opening mysql.proc. +*/ + +class Lock_db_routines_error_handler : public Internal_error_handler +{ +public: + bool handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + MYSQL_ERROR::enum_warning_level level, + const char* msg, + MYSQL_ERROR ** cond_hdl) + { + if (sql_errno == ER_NO_SUCH_TABLE || + sql_errno == ER_CANNOT_LOAD_FROM_TABLE || + sql_errno == ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE || + sql_errno == ER_COL_COUNT_DOESNT_MATCH_CORRUPTED) + return true; + return false; + } +}; + + +/** + Acquires exclusive metadata lock on all stored routines in the + given database. + + @note Will also return false (=success) if mysql.proc can't be opened + or is outdated. This allows DROP DATABASE to continue in these + cases. + */ + +bool lock_db_routines(THD *thd, char *db) +{ + TABLE *table; + uint key_len; + int nxtres= 0; + Open_tables_backup open_tables_state_backup; + MDL_request_list mdl_requests; + Lock_db_routines_error_handler err_handler; + DBUG_ENTER("lock_db_routines"); + + /* + mysql.proc will be re-opened during deletion, so we can ignore + errors when opening the table here. The error handler is + used to avoid getting the same warning twice. + */ + thd->push_internal_handler(&err_handler); + table= open_proc_table_for_read(thd, &open_tables_state_backup); + thd->pop_internal_handler(); + if (!table) + { + /* + DROP DATABASE should not fail even if mysql.proc does not exist + or is outdated. We therefore only abort mysql_rm_db() if we + have errors not handled by the error handler. + */ + DBUG_RETURN(thd->is_error() || thd->killed); + } + + table->field[MYSQL_PROC_FIELD_DB]->store(db, strlen(db), system_charset_info); + key_len= table->key_info->key_part[0].store_length; + table->file->ha_index_init(0, 1); + + if (! table->file->index_read_map(table->record[0], + table->field[MYSQL_PROC_FIELD_DB]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT)) + { + do + { + char *sp_name= get_field(thd->mem_root, + table->field[MYSQL_PROC_FIELD_NAME]); + longlong sp_type= table->field[MYSQL_PROC_MYSQL_TYPE]->val_int(); + MDL_request *mdl_request= new (thd->mem_root) MDL_request; + mdl_request->init(sp_type == TYPE_ENUM_FUNCTION ? + MDL_key::FUNCTION : MDL_key::PROCEDURE, + db, sp_name, MDL_EXCLUSIVE, MDL_TRANSACTION); + mdl_requests.push_front(mdl_request); + } while (! (nxtres= table->file->index_next_same(table->record[0], + table->field[MYSQL_PROC_FIELD_DB]->ptr, + key_len))); + } + table->file->ha_index_end(); + if (nxtres != 0 && nxtres != HA_ERR_END_OF_FILE) + { + table->file->print_error(nxtres, MYF(0)); + close_system_tables(thd, &open_tables_state_backup); + DBUG_RETURN(true); + } + close_system_tables(thd, &open_tables_state_backup); + + /* We should already hold a global IX lock and a schema X lock. */ + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "", + MDL_INTENTION_EXCLUSIVE) && + thd->mdl_context.is_lock_owner(MDL_key::SCHEMA, db, "", + MDL_EXCLUSIVE)); + DBUG_RETURN(thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)); +} + + +/** Drop all routines in database 'db' @note Close the thread tables, the calling code might want to @@ -1369,7 +1474,7 @@ sp_drop_db_routines(THD *thd, char *db) TABLE *table; int ret; uint key_len; - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); uchar keybuf[MAX_KEY_LENGTH]; DBUG_ENTER("sp_drop_db_routines"); DBUG_PRINT("enter", ("db: %s", db)); @@ -1640,38 +1745,6 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any) } -/** - Check if a routine exists in the mysql.proc table, without actually - parsing the definition. (Used for dropping). - - @param thd thread context - @param name name of procedure - - @retval - 0 Success - @retval - non-0 Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND -*/ - -int -sp_routine_exists_in_table(THD *thd, int type, sp_name *name) -{ - TABLE *table; - int ret; - Open_tables_backup open_tables_state_backup; - - if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) - ret= SP_OPEN_TABLE_FAILED; - else - { - if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) - ret= SP_KEY_NOT_FOUND; - close_system_tables(thd, &open_tables_state_backup); - } - return ret; -} - - extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, my_bool first) { @@ -1727,7 +1800,7 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry)); if (!rn) // OOM. Error will be reported using fatal_error(). return FALSE; - rn->mdl_request.init(key, MDL_SHARED); + rn->mdl_request.init(key, MDL_SHARED, MDL_TRANSACTION); if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn)) return FALSE; prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next); |