diff options
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r-- | sql/sql_base.cc | 155 |
1 files changed, 88 insertions, 67 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 790898baae6..7a59fefdddd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1402,6 +1402,9 @@ void close_thread_tables(THD *thd) DEBUG_SYNC(thd, "before_close_thread_tables"); #endif + DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt || + (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); + /* Detach MERGE children after every statement. Even under LOCK TABLES. */ for (table= thd->open_tables; table; table= table->next) { @@ -1446,28 +1449,6 @@ void close_thread_tables(THD *thd) Mark all temporary tables used by this statement as free for reuse. */ mark_temp_tables_as_free_for_reuse(thd); - /* - Let us commit transaction for statement. Since in 5.0 we only have - one statement transaction and don't allow several nested statement - transactions this call will do nothing if we are inside of stored - function or trigger (i.e. statement transaction is already active and - does not belong to statement for which we do close_thread_tables()). - TODO: This should be fixed in later releases. - */ - if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) - { - thd->stmt_da->can_overwrite_status= TRUE; - thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; - - /* - Reset transaction state, but only if we're not inside a - sub-statement of a prelocked statement. - */ - if (thd->locked_tables_mode <= LTM_LOCK_TABLES || - thd->lex->requires_prelocking()) - thd->transaction.stmt.reset(); - } if (thd->locked_tables_mode) { @@ -1528,26 +1509,6 @@ void close_thread_tables(THD *thd) if (thd->open_tables) close_open_tables(thd); - /* - - If inside a multi-statement transaction, - defer the release of metadata locks until the current - transaction is either committed or rolled back. This prevents - other statements from modifying the table for the entire - duration of this transaction. This provides commit ordering - and guarantees serializability across multiple transactions. - - If closing a system table, defer the release of metadata locks - to the caller. We have no sentinel in MDL subsystem to guard - transactional locks from system tables locks, so don't know - which locks are which here. - - If in autocommit mode, or outside a transactional context, - automatically release metadata locks of the current statement. - */ - if (! thd->in_multi_stmt_transaction_mode() && - ! (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) - { - thd->mdl_context.release_transactional_locks(); - } - DBUG_VOID_RETURN; } @@ -1562,7 +1523,14 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) DBUG_ASSERT(table->key_read == 0); DBUG_ASSERT(!table->file || table->file->inited == handler::NONE); mysql_mutex_assert_not_owner(&LOCK_open); - + /* + The metadata lock must be released after giving back + the table to the table cache. + */ + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table->s->db.str, + table->s->table_name.str, + MDL_SHARED)); table->mdl_ticket= NULL; mysql_mutex_lock(&thd->LOCK_thd_data); @@ -3188,6 +3156,7 @@ Locked_tables_list::init_locked_tables(THD *thd) return FALSE; } + /** Leave LTM_LOCK_TABLES mode if it's been entered. @@ -3224,7 +3193,12 @@ Locked_tables_list::unlock_locked_tables(THD *thd) } thd->leave_locked_tables_mode(); + DBUG_ASSERT(thd->transaction.stmt.is_empty()); close_thread_tables(thd); + /* + We rely on the caller to implicitly commit the + transaction and release transactional locks. + */ } /* After closing tables we can free memory used for storing lock @@ -3810,9 +3784,7 @@ Open_table_context::Open_table_context(THD *thd, uint flags) LONG_TIMEOUT : thd->variables.lock_wait_timeout), m_flags(flags), m_action(OT_NO_ACTION), - m_has_locks((thd->in_multi_stmt_transaction_mode() && - thd->mdl_context.has_locks()) || - thd->mdl_context.trans_sentinel()) + m_has_locks(thd->mdl_context.has_locks()) {} @@ -5264,6 +5236,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, table= 0; end: + if (table == NULL) + close_thread_tables(thd); thd_proc_info(thd, 0); DBUG_RETURN(table); } @@ -5282,7 +5256,8 @@ end: should work for this statement. @note - The lock will automaticaly be freed by close_thread_tables() + The thr_lock locks will automatically be freed by + close_thread_tables(). @retval FALSE OK. @retval TRUE Error @@ -5293,11 +5268,12 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, Prelocking_strategy *prelocking_strategy) { uint counter; + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_and_lock_tables"); DBUG_PRINT("enter", ("derived handling: %d", derived)); if (open_tables(thd, &tables, &counter, flags, prelocking_strategy)) - DBUG_RETURN(TRUE); + goto err; DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", { const char *old_proc_info= thd->proc_info; @@ -5306,15 +5282,22 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, thd->proc_info= old_proc_info;}); if (lock_tables(thd, tables, counter, flags)) - DBUG_RETURN(TRUE); + goto err; if (derived && (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || (thd->fill_derived_tables() && mysql_handle_derived(thd->lex, &mysql_derived_filling)))) - DBUG_RETURN(TRUE); /* purecov: inspected */ + goto err; DBUG_RETURN(FALSE); +err: + if (! thd->in_sub_stmt) + trans_rollback_stmt(thd); /* Necessary if derived handling failed. */ + close_thread_tables(thd); + /* Don't keep locks for a failed statement. */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + DBUG_RETURN(TRUE); } @@ -5340,13 +5323,24 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) { + DML_prelocking_strategy prelocking_strategy; uint counter; + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_normal_and_derived_tables"); DBUG_ASSERT(!thd->fill_derived_tables()); - if (open_tables(thd, &tables, &counter, flags) || + if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) || mysql_handle_derived(thd->lex, &mysql_derived_prepare)) - DBUG_RETURN(TRUE); /* purecov: inspected */ + goto end; + DBUG_RETURN(0); +end: + /* No need to rollback statement transaction, it's not started. */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + close_thread_tables(thd); + /* Don't keep locks for a failed statement. */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + + DBUG_RETURN(TRUE); /* purecov: inspected */ } @@ -5607,6 +5601,14 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, /* We have to cleanup translation tables of views. */ tmp->cleanup_items(); } + /* + No need to commit/rollback the statement transaction: it's + either not started or we're filling in an INFORMATION_SCHEMA + table on the fly, and thus mustn't manipulate with the + transaction of the enclosing statement. + */ + DBUG_ASSERT(thd->transaction.stmt.is_empty() || + (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); close_thread_tables(thd); thd->mdl_context.rollback_to_savepoint(start_of_statement_svp); } @@ -9034,7 +9036,8 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, MYSQL_LOCK_IGNORE_TIMEOUT)) { lex->restore_backup_query_tables_list(&query_tables_list_backup); - goto error; + thd->restore_backup_open_tables_state(backup); + DBUG_RETURN(TRUE); } for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global) @@ -9045,11 +9048,6 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, lex->restore_backup_query_tables_list(&query_tables_list_backup); DBUG_RETURN(FALSE); - -error: - close_system_tables(thd, backup); - - DBUG_RETURN(TRUE); } @@ -9072,6 +9070,38 @@ close_system_tables(THD *thd, Open_tables_backup *backup) } +/** + A helper function to close a mysql.* table opened + in an auxiliary THD during bootstrap or in the main + connection, when we know that there are no locks + held by the connection due to a preceding implicit + commit. + + This function assumes that there is no + statement transaction started for the operation + itself, since mysql.* tables are not transactional + and when they are used the binlog is off (DDL + binlogging is always statement-based. + + We need this function since we'd like to not + just close the system table, but also release + the metadata lock on it. + + Note, that in LOCK TABLES mode this function + does not release the metadata lock. But in this + mode the table can be opened only if it is locked + explicitly with LOCK TABLES. +*/ + +void +close_mysql_tables(THD *thd) +{ + /* No need to commit/rollback statement transaction, it's not started. */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); +} + /* Open and lock one system table for update. @@ -9143,16 +9173,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup) table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; } else - { - /* - If error in mysql_lock_tables(), open_ltable doesn't close the - table. Thread kill during mysql_lock_tables() is such error. But - open tables cannot be accepted when restoring the open tables - state. - */ - close_thread_tables(thd); thd->restore_backup_open_tables_state(backup); - } thd->utime_after_lock= save_utime_after_lock; DBUG_RETURN(table); |