diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/lock.cc | 8 | ||||
-rw-r--r-- | sql/sql_class.h | 35 | ||||
-rw-r--r-- | sql/sql_handler.cc | 102 |
3 files changed, 86 insertions, 59 deletions
diff --git a/sql/lock.cc b/sql/lock.cc index c241e635e6b..ad0aedbee40 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -165,18 +165,12 @@ lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags) write we must own metadata lock of MDL_SHARED_WRITE or stronger type. For table to be locked for read we must own metadata lock of MDL_SHARED_READ or stronger type). - The only exception are HANDLER statements which are allowed to - lock table for read while having only MDL_SHARED lock on it. */ DBUG_ASSERT(t->s->tmp_table || thd->mdl_context.is_lock_owner(MDL_key::TABLE, t->s->db.str, t->s->table_name.str, t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE ? - MDL_SHARED_WRITE : MDL_SHARED_READ) || - (t->open_by_handler && - thd->mdl_context.is_lock_owner(MDL_key::TABLE, - t->s->db.str, t->s->table_name.str, - MDL_SHARED))); + MDL_SHARED_WRITE : MDL_SHARED_READ)); /* Prevent modifications to base tables if READ_ONLY is activated. diff --git a/sql/sql_class.h b/sql/sql_class.h index 73637f8d8eb..6b64f710176 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1535,6 +1535,41 @@ private: /** + Internal error handler to process an error from MDL_context::upgrade_lock() + and mysql_lock_tables(). Used by implementations of HANDLER READ and + LOCK TABLES LOCAL. +*/ + +class MDL_deadlock_and_lock_abort_error_handler: public Internal_error_handler +{ +public: + /** + Handle an error from MDL_context::upgrade_lock() and mysql_lock_tables(). + Ignore ER_LOCK_ABORTED and ER_LOCK_DEADLOCK errors. + */ + virtual + bool handle_condition(THD *thd, + uint sql_errno, + const char *sqlstate, + Sql_condition::enum_warning_level level, + const char* msg, + Sql_condition **cond_hdl) + { + *cond_hdl= NULL; + if (sql_errno == ER_LOCK_ABORTED || sql_errno == ER_LOCK_DEADLOCK) + m_need_reopen= true; + + return m_need_reopen; + } + + bool need_reopen() const { return m_need_reopen; }; + void init() { m_need_reopen= false; }; +private: + bool m_need_reopen; +}; + + +/** Tables that were locked with LOCK TABLES statement. Encapsulates a list of TABLE_LIST instances for tables diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 7dcc6fa0e95..e117aa593b5 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -488,56 +488,6 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) /** - A helper class to process an error from mysql_lock_tables(). - HANDLER READ statement's attempt to lock the subject table - may get aborted if there is a pending DDL. In that case - we close the table, reopen it, and try to read again. - This is implicit and obscure, since HANDLER position - is lost in the process, but it's the legacy server - behaviour we should preserve. -*/ - -class Sql_handler_lock_error_handler: public Internal_error_handler -{ -public: - virtual - bool handle_condition(THD *thd, - uint sql_errno, - const char *sqlstate, - Sql_condition::enum_warning_level level, - const char* msg, - Sql_condition **cond_hdl); - - bool need_reopen() const { return m_need_reopen; }; - void init() { m_need_reopen= FALSE; }; -private: - bool m_need_reopen; -}; - - -/** - Handle an error from mysql_lock_tables(). - Ignore ER_LOCK_ABORTED errors. -*/ - -bool -Sql_handler_lock_error_handler:: -handle_condition(THD *thd, - uint sql_errno, - const char *sqlstate, - Sql_condition::enum_warning_level level, - const char* msg, - Sql_condition **cond_hdl) -{ - *cond_hdl= NULL; - if (sql_errno == ER_LOCK_ABORTED) - m_need_reopen= TRUE; - - return m_need_reopen; -} - - -/** Finds an open HANDLER table. @params name Name of handler to open @@ -733,7 +683,8 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, int error, keyno; uint num_rows; uchar *UNINIT_VAR(key); - Sql_handler_lock_error_handler sql_handler_lock_error; + MDL_deadlock_and_lock_abort_error_handler sql_handler_lock_error; + MDL_savepoint mdl_savepoint; DBUG_ENTER("mysql_ha_read"); DBUG_PRINT("enter",("'%s'.'%s' as '%s'", tables->db, tables->table_name, tables->alias)); @@ -752,6 +703,48 @@ retry: tables->table= table; // This is used by fix_fields table->pos_in_table_list= tables; + sql_handler_lock_error.init(); + + /* + For non-temporary tables we need to acquire SR lock in order to ensure + that HANDLER READ is blocked by LOCK TABLES WRITE in other connections + for storage engines which don't use THR_LOCK locks (e.g. InnoDB). + + To simplify clean-up code we take MDL_savepoint even for temporary tables. + */ + mdl_savepoint= thd->mdl_context.mdl_savepoint(); + + if (table->s->tmp_table == NO_TMP_TABLE) + { + MDL_request read_request; + + read_request.init(&handler->mdl_request.key, MDL_SHARED_READ, + MDL_TRANSACTION); + + thd->push_internal_handler(&sql_handler_lock_error); + + error= thd->mdl_context.acquire_lock(&read_request, + thd->variables.lock_wait_timeout); + thd->pop_internal_handler(); + + if (sql_handler_lock_error.need_reopen()) + { + /* + HANDLER READ statement's attempt to upgrade lock on the subject table + may get aborted if there is a pending DDL. In that case we close the + table, reopen it, and try to read again. + This is implicit and obscure, since HANDLER position is lost in the + process, but it's the legacy server behaviour we should preserve. + */ + DBUG_ASSERT(error && !thd->is_error()); + mysql_ha_close_table(handler); + goto retry; + } + + if (error) + goto err0; + } + if (handler->lock->lock_count > 0) { int lock_error; @@ -768,7 +761,7 @@ retry: */ thd->set_open_tables(table); - sql_handler_lock_error.init(); + /* Re-use Sql_handler_lock_error instance which was initialized earlier. */ thd->push_internal_handler(&sql_handler_lock_error); lock_error= mysql_lock_tables(thd, handler->lock, @@ -797,6 +790,7 @@ retry: */ DBUG_ASSERT(! thd->transaction_rollback_request); trans_rollback_stmt(thd); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); mysql_ha_close_table(handler); if (thd->stmt_arena->is_stmt_execute()) { @@ -812,7 +806,10 @@ retry: } if (lock_error) + { + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); goto err0; // mysql_lock_tables() printed error message already + } } if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 0)) @@ -957,6 +954,7 @@ ok: */ trans_commit_stmt(thd); mysql_unlock_tables(thd, handler->lock, 0); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); my_eof(thd); DBUG_PRINT("exit",("OK")); DBUG_RETURN(FALSE); |