From f3985c649d65c2d932a6f74d22de3d2e6624d525 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Tue, 14 Jul 2009 21:31:19 +0200 Subject: BUG#39934: Slave stops for engine that only support row-based logging General overview: The logic for switching to row format when binlog_format=MIXED had numerous flaws. The underlying problem was the lack of a consistent architecture. General purpose of this changeset: This changeset introduces an architecture for switching to row format when binlog_format=MIXED. It enforces the architecture where it has to. It leaves some bugs to be fixed later. It adds extensive tests to verify that unsafe statements work as expected and that appropriate errors are produced by problems with the selection of binlog format. It was not practical to split this into smaller pieces of work. Problem 1: To determine the logging mode, the code has to take several parameters into account (namely: (1) the value of binlog_format; (2) the capabilities of the engines; (3) the type of the current statement: normal, unsafe, or row injection). These parameters may conflict in several ways, namely: - binlog_format=STATEMENT for a row injection - binlog_format=STATEMENT for an unsafe statement - binlog_format=STATEMENT for an engine only supporting row logging - binlog_format=ROW for an engine only supporting statement logging - statement is unsafe and engine does not support row logging - row injection in a table that does not support statement logging - statement modifies one table that does not support row logging and one that does not support statement logging Several of these conflicts were not detected, or were detected with an inappropriate error message. The problem of BUG#39934 was that no appropriate error message was written for the case when an engine only supporting row logging executed a row injection with binlog_format=ROW. However, all above cases must be handled. Fix 1: Introduce new error codes (sql/share/errmsg.txt). Ensure that all conditions are detected and handled in decide_logging_format() Problem 2: The binlog format shall be determined once per statement, in decide_logging_format(). It shall not be changed before or after that. Before decide_logging_format() is called, all information necessary to determine the logging format must be available. This principle ensures that all unsafe statements are handled in a consistent way. However, this principle is not followed: thd->set_current_stmt_binlog_row_based_if_mixed() is called in several places, including from code executing UPDATE..LIMIT, INSERT..SELECT..LIMIT, DELETE..LIMIT, INSERT DELAYED, and SET @@binlog_format. After Problem 1 was fixed, that caused inconsistencies where these unsafe statements would not print the appropriate warnings or errors for some of the conflicts. Fix 2: Remove calls to THD::set_current_stmt_binlog_row_based_if_mixed() from code executed after decide_logging_format(). Compensate by calling the set_current_stmt_unsafe() at parse time. This way, all unsafe statements are detected by decide_logging_format(). Problem 3: INSERT DELAYED is not unsafe: it is logged in statement format even if binlog_format=MIXED, and no warning is printed even if binlog_format=STATEMENT. This is BUG#45825. Fix 3: Made INSERT DELAYED set itself to unsafe at parse time. This allows decide_logging_format() to detect that a warning should be printed or the binlog_format changed. Problem 4: LIMIT clause were not marked as unsafe when executed inside stored functions/triggers/views/prepared statements. This is BUG#45785. Fix 4: Make statements containing the LIMIT clause marked as unsafe at parse time, instead of at execution time. This allows propagating unsafe-ness to the view. --- sql/sql_delete.cc | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 277269f3b0d..facf1992f82 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -131,7 +131,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!using_limit && const_cond_result && !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && (thd->lex->sql_command == SQLCOM_TRUNCATE || - (!thd->current_stmt_binlog_row_based && + (!thd->is_current_stmt_binlog_format_row() && !(table->triggers && table->triggers->has_delete_triggers())))) { /* Update the table->file->stats.records number */ @@ -452,19 +452,6 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) DBUG_ENTER("mysql_prepare_delete"); List all_fields; - /* - Statement-based replication of DELETE ... LIMIT is not safe as order of - rows is not defined, so in mixed mode we go to row-based. - - Note that we may consider a statement as safe if ORDER BY primary_key - is present. However it may confuse users to see very similiar statements - replicated differently. - */ - if (thd->lex->current_select->select_limit) - { - thd->lex->set_stmt_unsafe(); - thd->set_current_stmt_binlog_row_based_if_mixed(); - } thd->lex->allow_sum_func= 0; if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, @@ -1023,7 +1010,7 @@ bool multi_delete::send_eof() static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) { - bool error, save_binlog_row_based= thd->current_stmt_binlog_row_based; + bool error, save_binlog_row_based= thd->is_current_stmt_binlog_format_row(); DBUG_ENTER("mysql_truncate_by_delete"); table_list->lock_type= TL_WRITE; mysql_init_select(thd->lex); @@ -1031,7 +1018,8 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE); ha_autocommit_or_rollback(thd, error); end_trans(thd, error ? ROLLBACK : COMMIT); - thd->current_stmt_binlog_row_based= save_binlog_row_based; + if (save_binlog_row_based) + thd->set_current_stmt_binlog_row_based(); DBUG_RETURN(error); } -- cgit v1.2.1 From 60d4662496c0831753085b172d70b39d98164892 Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Tue, 3 Nov 2009 19:02:56 +0000 Subject: WL#2687 WL#5072 BUG#40278 BUG#47175 Non-transactional updates that take place inside a transaction present problems for logging because they are visible to other clients before the transaction is committed, and they are not rolled back even if the transaction is rolled back. It is not always possible to log correctly in statement format when both transactional and non-transactional tables are used in the same transaction. In the current patch, we ensure that such scenario is completely safe under the ROW and MIXED modes. --- sql/sql_delete.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 7fa7e39e5bf..c851ffdcc09 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -385,7 +385,8 @@ cleanup: transactional_table= table->file->has_transactions(); if (!transactional_table && deleted > 0) - thd->transaction.stmt.modified_non_trans_table= TRUE; + thd->transaction.stmt.modified_non_trans_table= + thd->transaction.all.modified_non_trans_table= TRUE; /* See similar binlogging code in sql_update.cc, for comments */ if ((error < 0) || thd->transaction.stmt.modified_non_trans_table) @@ -414,15 +415,13 @@ cleanup: */ int log_result= thd->binlog_query(query_type, thd->query, thd->query_length, - is_trans, FALSE, errcode); + is_trans, FALSE, FALSE, errcode); if (log_result) { error=1; } } - if (thd->transaction.stmt.modified_non_trans_table) - thd->transaction.all.modified_non_trans_table= TRUE; } DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table); free_underlaid_joins(thd, select_lex); @@ -808,6 +807,9 @@ void multi_delete::abort() if (deleted) query_cache_invalidate3(thd, delete_tables, 1); + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; + /* If rows from the first table only has been deleted and it is transactional, just do rollback. @@ -838,9 +840,8 @@ void multi_delete::abort() int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - transactional_tables, FALSE, errcode); + transactional_tables, FALSE, FALSE, errcode); } - thd->transaction.all.modified_non_trans_table= true; } DBUG_VOID_RETURN; } @@ -967,6 +968,9 @@ bool multi_delete::send_eof() /* reset used flags */ thd_proc_info(thd, "end"); + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; + /* We must invalidate the query cache before binlog writing and ha_autocommit_... @@ -986,14 +990,12 @@ bool multi_delete::send_eof() errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - transactional_tables, FALSE, errcode) && + transactional_tables, FALSE, FALSE, errcode) && !normal_tables) { local_error=1; // Log write failed: roll back the SQL statement } } - if (thd->transaction.stmt.modified_non_trans_table) - thd->transaction.all.modified_non_trans_table= TRUE; } if (local_error != 0) error_handled= TRUE; // to force early leave from ::send_error() -- cgit v1.2.1 From 9b65f5782e110bed71cc3ed782029d73b1faa297 Mon Sep 17 00:00:00 2001 From: He Zhenxing Date: Sat, 21 Nov 2009 12:28:01 +0800 Subject: BUG#37148 Most callers of mysql_bin_log.write ignore the return result This is the non-ndb part of the patch. The return value of mysql_bin_log.write was ignored by most callers, which may lead to inconsistent on master and slave if the transaction was committed while the binlog was not correctly written. If my_error() is call in mysql_bin_log.write, this could also lead to assertion issue if my_ok() or my_error() is called after. This fixed the problem by let the caller to check and handle the return value of mysql_bin_log.write. This patch only adresses the simple cases. --- sql/sql_delete.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 6b59cbc14c1..23470b4abc9 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -849,9 +849,10 @@ void multi_delete::abort() if (mysql_bin_log.is_open()) { int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); - thd->binlog_query(THD::ROW_QUERY_TYPE, - thd->query(), thd->query_length(), - transactional_tables, FALSE, errcode); + /* possible error of writing binary log is ignored deliberately */ + (void) thd->binlog_query(THD::ROW_QUERY_TYPE, + thd->query(), thd->query_length(), + transactional_tables, FALSE, errcode); } thd->transaction.all.modified_non_trans_table= true; } @@ -1181,8 +1182,9 @@ end: TRUNCATE must always be statement-based binlogged (not row-based) so we don't test current_stmt_binlog_row_based. */ - write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - my_ok(thd); // This should return record count + error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); + if (!error) + my_ok(thd); // This should return record count } VOID(pthread_mutex_lock(&LOCK_open)); unlock_table_name(thd, table_list); -- cgit v1.2.1 From 4cff617c2541279a53b92acbd4e4d8716dc54873 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 24 Nov 2009 16:54:59 +0300 Subject: Backport of: ---------------------------------------------------------------------- ChangeSet@1.2571, 2008-04-08 12:30:06+02:00, vvaintroub@wva. +122 -0 Bug#32082 : definition of VOID in my_global.h conflicts with Windows SDK headers VOID macro is now removed. Its usage is replaced with void cast. In some cases, where cast does not make much sense (pthread_*, printf, hash_delete, my_seek), cast is ommited. --- sql/sql_delete.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 6b59cbc14c1..d8aa27c9695 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1166,10 +1166,10 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) // crashes, replacement works. *(path + path_length - reg_ext_length)= // '\0'; path[path_length - reg_ext_length] = 0; - VOID(pthread_mutex_lock(&LOCK_open)); + pthread_mutex_lock(&LOCK_open); error= ha_create_table(thd, path, table_list->db, table_list->table_name, &create_info, 1); - VOID(pthread_mutex_unlock(&LOCK_open)); + pthread_mutex_unlock(&LOCK_open); query_cache_invalidate3(thd, table_list, 0); end: @@ -1184,15 +1184,15 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - VOID(pthread_mutex_lock(&LOCK_open)); + pthread_mutex_lock(&LOCK_open); unlock_table_name(thd, table_list); - VOID(pthread_mutex_unlock(&LOCK_open)); + pthread_mutex_unlock(&LOCK_open); } else if (error) { - VOID(pthread_mutex_lock(&LOCK_open)); + pthread_mutex_lock(&LOCK_open); unlock_table_name(thd, table_list); - VOID(pthread_mutex_unlock(&LOCK_open)); + pthread_mutex_unlock(&LOCK_open); } DBUG_RETURN(error); -- cgit v1.2.1 From 69b9761f2913dfa9fa2989ef7f3e01b8d2d3e334 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 18:55:03 +0300 Subject: Initial import of WL#3726 "DDL locking for all metadata objects". Backport of: ------------------------------------------------------------ revno: 2630.4.1 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Fri 2008-05-23 17:54:03 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. ------------------------------------------------------------ This is the first patch in series. It transforms the metadata locking subsystem to use a dedicated module (mdl.h,cc). No significant changes in the locking protocol. The import passes the test suite with the exception of deprecated/removed 6.0 features, and MERGE tables. The latter are subject to a fix by WL#4144. Unfortunately, the original changeset comments got lost in a merge, thus this import has its own (largely insufficient) comments. This patch fixes Bug#25144 "replication / binlog with view breaks". Warning: this patch introduces an incompatible change: Under LOCK TABLES, it's no longer possible to FLUSH a table that was not locked for WRITE. Under LOCK TABLES, it's no longer possible to DROP a table or VIEW that was not locked for WRITE. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.2 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 14:03:45 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.3 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 14:08:51 +0400 message: WL#3726 "DDL locking for all metadata objects" Fixed failing Windows builds by adding mdl.cc to the lists of files needed to build server/libmysqld on Windows. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.4 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 21:57:58 +0400 message: WL#3726 "DDL locking for all metadata objects". Fix for assert failures in kill.test which occured when one tried to kill ALTER TABLE statement on merge table while it was waiting in wait_while_table_is_used() for other connections to close this table. These assert failures stemmed from the fact that cleanup code in this case assumed that temporary table representing new version of table was open with adding to THD::temporary_tables list while code which were opening this temporary table wasn't always fulfilling this. This patch changes code that opens new version of table to always do this linking in. It also streamlines cleanup process for cases when error occurs while we have new version of table open. ****** WL#3726 "DDL locking for all metadata objects" Add libmysqld/mdl.cc to .bzrignore. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.6 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sun 2008-05-25 00:33:22 +0400 message: WL#3726 "DDL locking for all metadata objects". Addition to the fix of assert failures in kill.test caused by changes for this worklog. Make sure we close the new table only once. --- sql/sql_delete.cc | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d8aa27c9695..fb48f32660b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1089,12 +1089,13 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; + MDL_LOCK *mdl_lock= 0; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); /* Remove tables from the HANDLER's hash. */ - mysql_ha_rm_tables(thd, table_list, FALSE); + mysql_ha_rm_tables(thd, table_list); /* If it is a temporary table, close and regenerate it */ if (!dont_send_ok && (table= find_temporary_table(thd, table_list))) @@ -1158,8 +1159,20 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - if (lock_and_wait_for_table_name(thd, table_list)) + /* + FIXME: Actually code of TRUNCATE breaks meta-data locking protocol since + tries to get table enging and therefore accesses table in some way + without holding any kind of meta-data lock. + */ + mdl_lock= mdl_alloc_lock(0, table_list->db, table_list->table_name, + thd->mem_root); + mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock); + if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(TRUE); + pthread_mutex_lock(&LOCK_open); + expel_table_from_cache(0, table_list->db, table_list->table_name); + pthread_mutex_unlock(&LOCK_open); } // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this @@ -1184,15 +1197,13 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); + if (mdl_lock) + mdl_release_lock(&thd->mdl_context, mdl_lock); } else if (error) { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); + if (mdl_lock) + mdl_release_lock(&thd->mdl_context, mdl_lock); } DBUG_RETURN(error); -- cgit v1.2.1 From a9dbad1afd452ca10116a0bbf83ab3d98b33723e Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 01:33:22 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.4.17 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Thu 2008-05-29 16:52:56 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. "The great correction of names". Renamed MDL_LOCK and MDL_LOCK_DATA classes to make usage of these names in metadata locking subsystem consistent with other parts of server (i.e. thr_lock.cc). Now we MDL_LOCK_DATA corresponds to request for a lock and MDL_LOCK to the lock itself. Adjusted code in MDL subsystem and other places using these classes accordingly. Did similar thing for GLOBAL_MDL_LOCK_DATA class and also changed name of its members to correspond to names of MDL_LOCK_DATA members. Finally got rid of usage of one letter variables in MDL code since it makes code harder to search in (according to reviewer). --- sql/sql_delete.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index fb48f32660b..f5c6dfd8986 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1089,7 +1089,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; - MDL_LOCK *mdl_lock= 0; + MDL_LOCK_DATA *mdl_lock_data= 0; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -1164,10 +1164,10 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) tries to get table enging and therefore accesses table in some way without holding any kind of meta-data lock. */ - mdl_lock= mdl_alloc_lock(0, table_list->db, table_list->table_name, - thd->mem_root); - mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock); + mdl_lock_data= mdl_alloc_lock(0, table_list->db, table_list->table_name, + thd->mem_root); + mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); + mdl_add_lock(&thd->mdl_context, mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_open); @@ -1197,13 +1197,13 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - if (mdl_lock) - mdl_release_lock(&thd->mdl_context, mdl_lock); + if (mdl_lock_data) + mdl_release_lock(&thd->mdl_context, mdl_lock_data); } else if (error) { - if (mdl_lock) - mdl_release_lock(&thd->mdl_context, mdl_lock); + if (mdl_lock_data) + mdl_release_lock(&thd->mdl_context, mdl_lock_data); } DBUG_RETURN(error); -- cgit v1.2.1 From 386b95dfef19d0ca248a72783e14dda5a925ec17 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 16:51:50 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.8.3 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w4 timestamp: Thu 2008-06-05 10:48:36 +0400 message: WL#3726 "DDL locking for all metadata objects". After-review fixes in progress. Adjust some comments that were using old terminology (name locks instead of exclusive metadata locks), brought some of them up-to-date with current situation in code. --- sql/sql_delete.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f5c6dfd8986..612f9d1954d 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1079,7 +1079,8 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) normally can't safely do this. - We don't want an ok to be sent to the end user. - We don't want to log the truncate command - - If we want to have a name lock on the table on exit without errors. + - If we want to keep exclusive metadata lock on the table (obtained by + caller) on exit without errors. */ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) -- cgit v1.2.1 From cf4a4ba6fdce6e37c0a97bcf9b6653456a5ff374 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 22:13:01 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.9.3 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w3 timestamp: Wed 2008-06-11 08:33:36 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Changed close_cached_tables() not to flush all unused TABLE instances when flushing individual table. Renamed expel_table_from_cache() to tdc_remove_table() and added enum parameter to be able more explicitly specify type of removal, rewrote its code to be more efficient. ****** Backport of: ------------------------------------------------------------ revno: 2630.9.4 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w3 timestamp: Wed 2008-06-11 15:53:53 +0400 message: WL#3726 "DDL locking for all metadata objects". After-review fixes in progress. Minor changes in order to improve code readability and simplify debugging. --- sql/sql_delete.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 612f9d1954d..630cf73076c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1172,7 +1172,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) if (mdl_acquire_exclusive_locks(&thd->mdl_context)) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_open); - expel_table_from_cache(0, table_list->db, table_list->table_name); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, + table_list->table_name); pthread_mutex_unlock(&LOCK_open); } -- cgit v1.2.1 From 0a49fd92d90843f9cd49f98bb23a513b3f82b1b9 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 19:15:40 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.4.32 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w2 timestamp: Thu 2008-06-19 16:39:58 +0400 message: WL#3726 "DDL locking for all metadata objects". After-review fixes in progress. Ensure that metadata locking subsystem properly handles out-of-memory conditions. Clarified MDL interface by separating release of locks and removal of lock requests from the context. --- sql/sql_delete.cc | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 630cf73076c..4827d48e1c5 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1170,7 +1170,10 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); mdl_add_lock(&thd->mdl_context, mdl_lock_data); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + { + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); DBUG_RETURN(TRUE); + } pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, table_list->table_name); @@ -1200,12 +1203,18 @@ end: my_ok(thd); // This should return record count } if (mdl_lock_data) + { mdl_release_lock(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + } } else if (error) { if (mdl_lock_data) + { mdl_release_lock(&thd->mdl_context, mdl_lock_data); + mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + } } DBUG_RETURN(error); -- cgit v1.2.1 From 4ae05129dc51f87f3ff3528640465d2122db96f3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 18:47:20 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.13.16 committer: Davi Arnaut branch nick: WL#4284 timestamp: Sat 2008-07-26 13:38:20 -0300 message: WL#4284: Transactional DDL locking SQL statements' effect on transactions. Currently the MySQL server and its storage engines are not capable of rolling back operations that define or modify data structures (also known as DDL statements) or operations that alter any of the system tables (the mysql database). Allowing these group of statements to participate in transactions is unfeasible at this time (since rollback has no effect whatsoever on them) and goes against the design of our metadata locking subsystem. The solution is to issue implicit commits before and after those statements execution. This effectively confines each of those statements to its own special transaction and ensures that metadata locks taken during this special transaction are not leaked into posterior statements/transactions. --- sql/sql_delete.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 4827d48e1c5..c0162605e2f 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1062,9 +1062,18 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) table_list->lock_type= TL_WRITE; mysql_init_select(thd->lex); thd->clear_current_stmt_binlog_row_based(); + /* Delete all rows from table */ error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE); - ha_autocommit_or_rollback(thd, error); - end_trans(thd, error ? ROLLBACK : COMMIT); + /* + All effects of a TRUNCATE TABLE operation are rolled back if a row by row + deletion fails. Otherwise, operation is automatically committed at the end. + */ + if (error) + { + DBUG_ASSERT(thd->stmt_da->is_error()); + ha_autocommit_or_rollback(thd, TRUE); + end_active_trans(thd); + } thd->current_stmt_binlog_row_based= save_binlog_row_based; DBUG_RETURN(error); } -- cgit v1.2.1 From c43f894c51bb7b091dd668f069fb5e5e44dcb0b1 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 21:37:38 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.22.3 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Thu 2008-08-07 22:33:43 -0300 message: WL#4284: Transactional DDL locking Make transaction management more modular through a new interface. The overall objective of this change is to provide groundwork for the design of transactional DDL locking by cleaning up the transaction high level API to better distinguish operations implicit and explicit, and single statement transaction from operations on the normal transaction. Having a a high-level interface for transaction management provides a better base for implementing transactional concepts that are not always tied to storage engines and also makes it easier to interect with other higher level modules of the server. --- sql/sql_delete.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c0162605e2f..13c331af95c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -23,6 +23,7 @@ #include "sql_select.h" #include "sp_head.h" #include "sql_trigger.h" +#include "transaction.h" /** Implement DELETE SQL word. @@ -1071,8 +1072,8 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) if (error) { DBUG_ASSERT(thd->stmt_da->is_error()); - ha_autocommit_or_rollback(thd, TRUE); - end_active_trans(thd); + trans_rollback_stmt(thd); + trans_rollback(thd); } thd->current_stmt_binlog_row_based= save_binlog_row_based; DBUG_RETURN(error); -- cgit v1.2.1 From 911c673edf45616137d71f43f472be410ecb7511 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 02:29:40 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.23.18 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Mon 2009-03-02 18:18:26 -0300 message: Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order WL#4284: Transactional DDL locking This is a prerequisite patch: These changes are intended to split lock requests from granted locks and to allow the memory and lifetime of granted locks to be managed within the MDL subsystem. Furthermore, tickets can now be shared and therefore are used to satisfy multiple lock requests, but only shared locks can be recursive. The problem is that the MDL subsystem morphs lock requests into granted locks locks but does not manage the memory and lifetime of lock requests, and hence, does not manage the memory of granted locks either. This can be problematic because it puts the burden of tracking references on the users of the subsystem and it can't be easily done in transactional contexts where the locks have to be kept around for the duration of a transaction. Another issue is that recursive locks (when the context trying to acquire a lock already holds a lock on the same object) requires that each time the lock is granted, a unique lock request/granted lock structure structure must be kept around until the lock is released. This can lead to memory leaks in transactional contexts as locks taken during the transaction should only be released at the end of the transaction. This also leads to unnecessary wake ups (broadcasts) in the MDL subsystem if the context still holds a equivalent of the lock being released. These issues are exacerbated due to the fact that WL#4284 low-level design says that the implementation should "2) Store metadata locks in transaction memory root, rather than statement memory root" but this is not possible because a memory root, as implemented in mysys, requires all objects allocated from it to be freed all at once. This patch combines review input and significant code contributions from Konstantin Osipov (kostja) and Dmitri Lenev (dlenev). --- sql/sql_delete.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 13c331af95c..c96594a8032 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; - MDL_LOCK_DATA *mdl_lock_data= 0; + MDL_LOCK_REQUEST *mdl_lock_request= NULL; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -1175,13 +1175,13 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) tries to get table enging and therefore accesses table in some way without holding any kind of meta-data lock. */ - mdl_lock_data= mdl_alloc_lock(0, table_list->db, table_list->table_name, - thd->mem_root); - mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock_data); + mdl_lock_request= mdl_request_alloc(0, table_list->db, + table_list->table_name, thd->mem_root); + mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); + mdl_request_add(&thd->mdl_context, mdl_lock_request); if (mdl_acquire_exclusive_locks(&thd->mdl_context)) { - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); DBUG_RETURN(TRUE); } pthread_mutex_lock(&LOCK_open); @@ -1212,18 +1212,18 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - if (mdl_lock_data) + if (mdl_lock_request) { - mdl_release_lock(&thd->mdl_context, mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); } } else if (error) { - if (mdl_lock_data) + if (mdl_lock_request) { - mdl_release_lock(&thd->mdl_context, mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); } } DBUG_RETURN(error); -- cgit v1.2.1 From a3a23ec4d3b9d9c22b8bdbc9adf2cd0f157c6a3f Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 02:52:05 +0300 Subject: Backport of: ---------------------------------------------------------- revno: 2617.23.20 committer: Konstantin Osipov branch nick: mysql-6.0-runtime timestamp: Wed 2009-03-04 16:31:31 +0300 message: WL#4284 "Transactional DDL locking" Review comments: "Objectify" the MDL API. MDL_request and MDL_context still need manual construction and destruction, since they are used in environment that is averse to constructors/destructors. --- sql/sql_delete.cc | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c96594a8032..fc92dbee0a1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; - MDL_LOCK_REQUEST *mdl_lock_request= NULL; + MDL_request *mdl_request= NULL; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -1175,13 +1175,13 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) tries to get table enging and therefore accesses table in some way without holding any kind of meta-data lock. */ - mdl_lock_request= mdl_request_alloc(0, table_list->db, - table_list->table_name, thd->mem_root); - mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); - mdl_request_add(&thd->mdl_context, mdl_lock_request); - if (mdl_acquire_exclusive_locks(&thd->mdl_context)) + mdl_request= MDL_request::create(0, table_list->db, + table_list->table_name, thd->mem_root); + mdl_request->set_type(MDL_EXCLUSIVE); + thd->mdl_context.add_request(mdl_request); + if (thd->mdl_context.acquire_exclusive_locks()) { - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.remove_request(mdl_request); DBUG_RETURN(TRUE); } pthread_mutex_lock(&LOCK_open); @@ -1212,18 +1212,18 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - if (mdl_lock_request) + if (mdl_request) { - mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.release_lock(mdl_request->ticket); + thd->mdl_context.remove_request(mdl_request); } } else if (error) { - if (mdl_lock_request) + if (mdl_request) { - mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); - mdl_request_remove(&thd->mdl_context, mdl_lock_request); + thd->mdl_context.release_lock(mdl_request->ticket); + thd->mdl_context.remove_request(mdl_request); } } DBUG_RETURN(error); -- cgit v1.2.1 From a66a2608ae4d70a3f9b3d41e38cfb97eb616bed3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 12:57:07 +0300 Subject: Backport of: ---------------------------------------------------------- revno: 2617.69.20 committer: Konstantin Osipov branch nick: 5.4-4284-1-assert timestamp: Thu 2009-08-13 18:29:55 +0400 message: WL#4284 "Transactional DDL locking" A review fix. Since WL#4284 implementation separated MDL_request and MDL_ticket, MDL_request becamse a utility object necessary only to get a ticket. Store it by-value in TABLE_LIST with the intent to merge MDL_request::key with table_list->table_name and table_list->db in future. Change the MDL subsystem to not require MDL_requests to stay around till close_thread_tables(). Remove the list of requests from the MDL context. Requests for shared metadata locks acquired in open_tables() are only used as a list in recover_from_failed_open_table_attempt(), which calls mdl_context.wait_for_locks() for this list. To keep such list for recover_from_failed_open_table_attempt(), introduce a context class (Open_table_context), that collects all requests. A lot of minor cleanups and simplications that became possible with this change. --- sql/sql_delete.cc | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index fc92dbee0a1..c3e205848de 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error; uint path_length; - MDL_request *mdl_request= NULL; + MDL_request mdl_request; + bool has_mdl_lock= FALSE; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -1145,6 +1146,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) if (!dont_send_ok) { enum legacy_db_type table_type; + /* + FIXME: Code of TRUNCATE breaks the meta-data + locking protocol since it tries to find out the table storage + engine and therefore accesses table in some way without holding + any kind of meta-data lock. + */ mysql_frm_type(thd, path, &table_type); if (table_type == DB_TYPE_UNKNOWN) { @@ -1170,20 +1177,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - /* - FIXME: Actually code of TRUNCATE breaks meta-data locking protocol since - tries to get table enging and therefore accesses table in some way - without holding any kind of meta-data lock. - */ - mdl_request= MDL_request::create(0, table_list->db, - table_list->table_name, thd->mem_root); - mdl_request->set_type(MDL_EXCLUSIVE); - thd->mdl_context.add_request(mdl_request); - if (thd->mdl_context.acquire_exclusive_locks()) - { - thd->mdl_context.remove_request(mdl_request); + mdl_request.init(0, table_list->db, table_list->table_name, MDL_EXCLUSIVE); + if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) DBUG_RETURN(TRUE); - } + + has_mdl_lock= TRUE; + pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, table_list->table_name); @@ -1212,19 +1211,13 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - if (mdl_request) - { - thd->mdl_context.release_lock(mdl_request->ticket); - thd->mdl_context.remove_request(mdl_request); - } + if (has_mdl_lock) + thd->mdl_context.release_lock(mdl_request.ticket); } else if (error) { - if (mdl_request) - { - thd->mdl_context.release_lock(mdl_request->ticket); - thd->mdl_context.remove_request(mdl_request); - } + if (has_mdl_lock) + thd->mdl_context.release_lock(mdl_request.ticket); } DBUG_RETURN(error); -- cgit v1.2.1 From 4592dd2d8147d54422432afcf0e87530df9a3d59 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 09:51:20 +0100 Subject: Backport of revno: 2617.69.40 A pre-requisite patch for Bug#30977 "Concurrent statement using stored function and DROP FUNCTION breaks SBR". This patch changes the MDL API by introducing a namespace for lock keys: MDL_TABLE for tables and views and MDL_PROCEDURE for stored procedures and functions. The latter is needed for the fix for Bug#30977. --- sql/sql_delete.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c3e205848de..03be0382150 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1177,7 +1177,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - mdl_request.init(0, table_list->db, table_list->table_name, MDL_EXCLUSIVE); + mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) DBUG_RETURN(TRUE); -- cgit v1.2.1 From e33a8b2a1a254271d99b6c5f00097fd0d89adb90 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Wed, 9 Dec 2009 20:19:51 -0700 Subject: WL#2360 Performance schema Part III: mysys instrumentation --- sql/sql_delete.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d8aa27c9695..17f1036d70e 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1166,10 +1166,10 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) // crashes, replacement works. *(path + path_length - reg_ext_length)= // '\0'; path[path_length - reg_ext_length] = 0; - pthread_mutex_lock(&LOCK_open); + mysql_mutex_lock(&LOCK_open); error= ha_create_table(thd, path, table_list->db, table_list->table_name, &create_info, 1); - pthread_mutex_unlock(&LOCK_open); + mysql_mutex_unlock(&LOCK_open); query_cache_invalidate3(thd, table_list, 0); end: @@ -1184,15 +1184,15 @@ end: write_bin_log(thd, TRUE, thd->query(), thd->query_length()); my_ok(thd); // This should return record count } - pthread_mutex_lock(&LOCK_open); + mysql_mutex_lock(&LOCK_open); unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); + mysql_mutex_unlock(&LOCK_open); } else if (error) { - pthread_mutex_lock(&LOCK_open); + mysql_mutex_lock(&LOCK_open); unlock_table_name(thd, table_list); - pthread_mutex_unlock(&LOCK_open); + mysql_mutex_unlock(&LOCK_open); } DBUG_RETURN(error); -- cgit v1.2.1 From 2e73ea7ea8a56765982ba5c8642ed5b14ef39fde Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 10 Dec 2009 11:21:38 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.68.25 committer: Dmitry Lenev branch nick: mysql-next-bg-pre2-2 timestamp: Wed 2009-09-16 18:26:50 +0400 message: Follow-up for one of pre-requisite patches for fixing bug #30977 "Concurrent statement using stored function and DROP FUNCTION breaks SBR". Made enum_mdl_namespace enum part of MDL_key class and removed MDL_ prefix from the names of enum members. In order to do the latter changed name of PROCEDURE symbol to PROCEDURE_SYM (otherwise macro which was automatically generated for this symbol conflicted with MDL_key::PROCEDURE enum member). --- sql/sql_delete.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 03be0382150..1f3621de502 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1177,7 +1177,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); + mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) DBUG_RETURN(TRUE); -- cgit v1.2.1 From 700a361a6a6044f8ef49b6aca7db5810c2641e54 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 11 Dec 2009 15:24:23 +0300 Subject: Backport of: ------------------------------------------------------------ 2599.161.3 Ingo Struewing 2009-07-21 Bug#20667 - Truncate table fails for a write locked table TRUNCATE TABLE was not allowed under LOCK TABLES. The patch removes this restriction. mysql_truncate() does now handle that case. --- sql/sql_delete.cc | 75 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 19 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1f3621de502..26478b31290 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1098,9 +1098,15 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) HA_CREATE_INFO create_info; char path[FN_REFLEN + 1]; TABLE *table; - bool error; + bool error= TRUE; uint path_length; MDL_request mdl_request; + /* + Is set if we're under LOCK TABLES, and used + to downgrade the exclusive lock after the + table was truncated. + */ + MDL_ticket *mdl_ticket= NULL; bool has_mdl_lock= FALSE; DBUG_ENTER("mysql_truncate"); @@ -1119,7 +1125,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) goto trunc_by_del; table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); - + close_temporary_table(thd, table, 0, 0); // Don't free share ha_create_table(thd, share->normalized_path.str, share->db.str, share->table_name.str, &create_info, 1); @@ -1177,21 +1183,47 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; - mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); - if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) - DBUG_RETURN(TRUE); - - has_mdl_lock= TRUE; - pthread_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, - table_list->table_name); - pthread_mutex_unlock(&LOCK_open); + if (thd->locked_tables_mode) + { + if (!(table= find_write_locked_table(thd->open_tables, table_list->db, + table_list->table_name))) + DBUG_RETURN(TRUE); + mdl_ticket= table->mdl_ticket; + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + goto end; + close_all_tables_for_name(thd, table->s, FALSE); + } + else + { + /* + Even though we could use the previous execution branch + here just as well, we must not try to open the table: + MySQL manual documents that TRUNCATE can be used to + repair a damaged table, i.e. a table that can not be + fully "opened". In particular MySQL manual says: + + As long as the table format file tbl_name.frm is valid, + the table can be re-created as an empty table with TRUNCATE + TABLE, even if the data or index files have become corrupted. + */ + mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, + MDL_EXCLUSIVE); + if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) + DBUG_RETURN(TRUE); + has_mdl_lock= TRUE; + pthread_mutex_lock(&LOCK_open); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, + table_list->table_name); + pthread_mutex_unlock(&LOCK_open); + } } - // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this - // crashes, replacement works. *(path + path_length - reg_ext_length)= - // '\0'; + /* + Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this + crashes, replacement works. *(path + path_length - reg_ext_length)= + '\0'; + */ path[path_length - reg_ext_length] = 0; pthread_mutex_lock(&LOCK_open); error= ha_create_table(thd, path, table_list->db, table_list->table_name, @@ -1202,6 +1234,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) end: if (!dont_send_ok) { + if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd)) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + /* + Even if we failed to reopen some tables, + the operation itself succeeded, write the binlog. + */ if (!error) { /* @@ -1213,12 +1251,11 @@ end: } if (has_mdl_lock) thd->mdl_context.release_lock(mdl_request.ticket); + if (mdl_ticket) + mdl_ticket->downgrade_exclusive_lock(); } - else if (error) - { - if (has_mdl_lock) - thd->mdl_context.release_lock(mdl_request.ticket); - } + + DBUG_PRINT("exit", ("error: %d", error)); DBUG_RETURN(error); trunc_by_del: -- cgit v1.2.1 From eadf9532fdee35e86544e13911549475af979f98 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Fri, 11 Dec 2009 12:45:44 -0700 Subject: Merge cleanup --- sql/sql_delete.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d0988ea46f8..1b54f2a76e4 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (C) 2000 MySQL AB, 2008-2009 Sun Microsystems, Inc This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -- cgit v1.2.1 From efaa28abb470495e86dff78de26b4f0019c736e2 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Tue, 15 Dec 2009 10:31:49 +0300 Subject: Post-merge fix. --- sql/sql_delete.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 8ffc7bd1bc6..c59e7dd5873 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1183,11 +1183,9 @@ end: { /* In RBR, the statement is not binlogged if the table is temporary. */ if (!is_temporary_table || !thd->current_stmt_binlog_row_based) - { error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - if (!error) - my_ok(thd); // This should return record count - } + if (!error) + my_ok(thd); // This should return record count } pthread_mutex_lock(&LOCK_open); unlock_table_name(thd, table_list); -- cgit v1.2.1 From ae2768ce9c7df45ec88ba49bf149985bf7c9308d Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 22 Dec 2009 10:35:56 +0100 Subject: WL#4738 streamline/simplify @@variable creation process Bug#16565 mysqld --help --verbose does not order variablesBug#20413 sql_slave_skip_counter is not shown in show variables Bug#20415 Output of mysqld --help --verbose is incomplete Bug#25430 variable not found in SELECT @@global.ft_max_word_len; Bug#32902 plugin variables don't know their names Bug#34599 MySQLD Option and Variable Reference need to be consistent in formatting! Bug#34829 No default value for variable and setting default does not raise error Bug#34834 ? Is accepted as a valid sql mode Bug#34878 Few variables have default value according to documentation but error occurs Bug#34883 ft_boolean_syntax cant be assigned from user variable to global var. Bug#37187 `INFORMATION_SCHEMA`.`GLOBAL_VARIABLES`: inconsistent status Bug#40988 log_output_basic.test succeeded though syntactically false. Bug#41010 enum-style command-line options are not honoured (maria.maria-recover fails) Bug#42103 Setting key_buffer_size to a negative value may lead to very large allocations Bug#44691 Some plugins configured as MYSQL_PLUGIN_MANDATORY in can be disabled Bug#44797 plugins w/o command-line options have no disabling option in --help Bug#46314 string system variables don't support expressions Bug#46470 sys_vars.max_binlog_cache_size_basic_32 is broken Bug#46586 When using the plugin interface the type "set" for options caused a crash. Bug#47212 Crash in DBUG_PRINT in mysqltest.cc when trying to print octal number Bug#48758 mysqltest crashes on sys_vars.collation_server_basic in gcov builds Bug#49417 some complaints about mysqld --help --verbose output Bug#49540 DEFAULT value of binlog_format isn't the default value Bug#49640 ambiguous option '--skip-skip-myisam' (double skip prefix) Bug#49644 init_connect and \0 Bug#49645 init_slave and multi-byte characters Bug#49646 mysql --show-warnings crashes when server dies --- sql/sql_delete.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index ab898950a1d..086aacb495e 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -92,7 +92,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } const_cond= (!conds || conds->const_item()); - safe_update=test(thd->options & OPTION_SAFE_UPDATES); + safe_update=test(thd->variables.option_bits & OPTION_SAFE_UPDATES); if (safe_update && const_cond) { my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, @@ -631,7 +631,7 @@ multi_delete::initialize_tables(JOIN *join) Unique **tempfiles_ptr; DBUG_ENTER("initialize_tables"); - if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join)) + if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && error_if_full_join(join)) DBUG_RETURN(1); table_map tables_to_delete_from=0; -- cgit v1.2.1 From a63f8480dbc862db00fb8f76c74b1fb99fa4534a Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Thu, 21 Jan 2010 23:43:03 +0300 Subject: Patch that changes metadata locking subsystem to use mutex per lock and condition variable per context instead of one mutex and one conditional variable for the whole subsystem. This should increase concurrency in this subsystem. It also opens the way for further changes which are necessary to solve such bugs as bug #46272 "MySQL 5.4.4, new MDL: unnecessary deadlock" and bug #37346 "innodb does not detect deadlock between update and alter table". Two other notable changes done by this patch: - MDL subsystem no longer implicitly acquires global intention exclusive metadata lock when per-object metadata lock is acquired. Now this has to be done by explicit calls outside of MDL subsystem. - Instead of using separate MDL_context for opening system tables/tables for purposes of I_S we now create MDL savepoint in the main context before opening tables and rollback to this savepoint after closing them. This means that it is now possible to get ER_LOCK_DEADLOCK error even not inside a transaction. This might happen in unlikely case when one runs DDL on one of system tables while also running DDL on some other tables. Cases when this ER_LOCK_DEADLOCK error is not justified will be addressed by advanced deadlock detector for MDL subsystem which we plan to implement. --- sql/sql_delete.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 26478b31290..228c001f71b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error= TRUE; uint path_length; - MDL_request mdl_request; + MDL_request mdl_global_request, mdl_request; /* Is set if we're under LOCK TABLES, and used to downgrade the exclusive lock after the @@ -1207,10 +1207,21 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) the table can be re-created as an empty table with TRUNCATE TABLE, even if the data or index files have become corrupted. */ + + mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); + if (thd->mdl_context.acquire_global_intention_exclusive_lock( + &mdl_global_request)) + DBUG_RETURN(TRUE); if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) + { + /* + We rely on that close_thread_tables() to release global lock + in this case. + */ DBUG_RETURN(TRUE); + } has_mdl_lock= TRUE; pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, @@ -1250,7 +1261,7 @@ end: my_ok(thd); // This should return record count } if (has_mdl_lock) - thd->mdl_context.release_lock(mdl_request.ticket); + thd->mdl_context.release_transactional_locks(); if (mdl_ticket) mdl_ticket->downgrade_exclusive_lock(); } -- cgit v1.2.1 From afd15c43a9103c47389835105489acd07d64e014 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Mon, 1 Feb 2010 14:43:06 +0300 Subject: Implement new type-of-operation-aware metadata locks. Add a wait-for graph based deadlock detector to the MDL subsystem. Fixes bug #46272 "MySQL 5.4.4, new MDL: unnecessary deadlock" and bug #37346 "innodb does not detect deadlock between update and alter table". The first bug manifested itself as an unwarranted abort of a transaction with ER_LOCK_DEADLOCK error by a concurrent ALTER statement, when this transaction tried to repeat use of a table, which it has already used in a similar fashion before ALTER started. The second bug showed up as a deadlock between table-level locks and InnoDB row locks, which was "detected" only after innodb_lock_wait_timeout timeout. A transaction would start using the table and modify a few rows. Then ALTER TABLE would come in, and start copying rows into a temporary table. Eventually it would stumble on the modified records and get blocked on a row lock. The first transaction would try to do more updates, and get blocked on thr_lock.c lock. This situation of circular wait would only get resolved by a timeout. Both these bugs stemmed from inadequate solutions to the problem of deadlocks occurring between different locking subsystems. In the first case we tried to avoid deadlocks between metadata locking and table-level locking subsystems, when upgrading shared metadata lock to exclusive one. Transactions holding the shared lock on the table and waiting for some table-level lock used to be aborted too aggressively. We also allowed ALTER TABLE to start in presence of transactions that modify the subject table. ALTER TABLE acquires TL_WRITE_ALLOW_READ lock at start, and that block all writes against the table (naturally, we don't want any writes to be lost when switching the old and the new table). TL_WRITE_ALLOW_READ lock, in turn, would block the started transaction on thr_lock.c lock, should they do more updates. This, again, lead to the need to abort such transactions. The second bug occurred simply because we didn't have any mechanism to detect deadlocks between the table-level locks in thr_lock.c and row-level locks in InnoDB, other than innodb_lock_wait_timeout. This patch solves both these problems by moving lock conflicts which are causing these deadlocks into the metadata locking subsystem, thus making it possible to avoid or detect such deadlocks inside MDL. To do this we introduce new type-of-operation-aware metadata locks, which allow MDL subsystem to know not only the fact that transaction has used or is going to use some object but also what kind of operation it has carried out or going to carry out on the object. This, along with the addition of a special kind of upgradable metadata lock, allows ALTER TABLE to wait until all transactions which has updated the table to go away. This solves the second issue. Another special type of upgradable metadata lock is acquired by LOCK TABLE WRITE. This second lock type allows to solve the first issue, since abortion of table-level locks in event of DDL under LOCK TABLES becomes also unnecessary. Below follows the list of incompatible changes introduced by this patch: - From now on, ALTER TABLE and CREATE/DROP TRIGGER SQL (i.e. those statements that acquire TL_WRITE_ALLOW_READ lock) wait for all transactions which has *updated* the table to complete. - From now on, LOCK TABLES ... WRITE, REPAIR/OPTIMIZE TABLE (i.e. all statements which acquire TL_WRITE table-level lock) wait for all transaction which *updated or read* from the table to complete. As a consequence, innodb_table_locks=0 option no longer applies to LOCK TABLES ... WRITE. - DROP DATABASE, DROP TABLE, RENAME TABLE no longer abort statements or transactions which use tables being dropped or renamed, and instead wait for these transactions to complete. - Since LOCK TABLES WRITE now takes a special metadata lock, not compatible with with reads or writes against the subject table and transaction-wide, thr_lock.c deadlock avoidance algorithm that used to ensure absence of deadlocks between LOCK TABLES WRITE and other statements is no longer sufficient, even for MyISAM. The wait-for graph based deadlock detector of MDL subsystem may sometimes be necessary and is involved. This may lead to ER_LOCK_DEADLOCK error produced for multi-statement transactions even if these only use MyISAM: session 1: session 2: begin; update t1 ... lock table t2 write, t1 write; -- gets a lock on t2, blocks on t1 update t2 ... (ER_LOCK_DEADLOCK) - Finally, support of LOW_PRIORITY option for LOCK TABLES ... WRITE was abandoned. LOCK TABLE ... LOW_PRIORITY WRITE from now on has the same priority as the usual LOCK TABLE ... WRITE. SELECT HIGH PRIORITY no longer trumps LOCK TABLE ... WRITE in the wait queue. - We do not take upgradable metadata locks on implicitly locked tables. So if one has, say, a view v1 that uses table t1, and issues: LOCK TABLE v1 WRITE; FLUSH TABLE t1; -- (or just 'FLUSH TABLES'), an error is produced. In order to be able to perform DDL on a table under LOCK TABLES, the table must be locked explicitly in the LOCK TABLES list. --- sql/sql_delete.cc | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 228c001f71b..87e59c0ac65 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1100,7 +1100,6 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table; bool error= TRUE; uint path_length; - MDL_request mdl_global_request, mdl_request; /* Is set if we're under LOCK TABLES, and used to downgrade the exclusive lock after the @@ -1186,8 +1185,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) if (thd->locked_tables_mode) { - if (!(table= find_write_locked_table(thd->open_tables, table_list->db, - table_list->table_name))) + if (!(table= find_table_for_mdl_upgrade(thd->open_tables, table_list->db, + table_list->table_name, FALSE))) DBUG_RETURN(TRUE); mdl_ticket= table->mdl_ticket; if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) @@ -1196,6 +1195,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) } else { + MDL_request mdl_global_request, mdl_request; + MDL_request_list mdl_requests; /* Even though we could use the previous execution branch here just as well, we must not try to open the table: @@ -1211,17 +1212,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); - if (thd->mdl_context.acquire_global_intention_exclusive_lock( - &mdl_global_request)) - DBUG_RETURN(TRUE); - if (thd->mdl_context.acquire_exclusive_lock(&mdl_request)) - { - /* - We rely on that close_thread_tables() to release global lock - in this case. - */ + mdl_requests.push_front(&mdl_request); + mdl_requests.push_front(&mdl_global_request); + + if (thd->mdl_context.acquire_locks(&mdl_requests)) DBUG_RETURN(TRUE); - } + has_mdl_lock= TRUE; pthread_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db, @@ -1263,7 +1259,7 @@ end: if (has_mdl_lock) thd->mdl_context.release_transactional_locks(); if (mdl_ticket) - mdl_ticket->downgrade_exclusive_lock(); + mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); } DBUG_PRINT("exit", ("error: %d", error)); -- cgit v1.2.1 From 06e1a73af6ca0b912153b10bb23deba8fd333974 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 4 Feb 2010 23:15:47 +0300 Subject: Merge next-mr -> next-4284. Cherry-pick a fix Bug#37148 from next-mr, to preserve file ids of the added files, and ensure that all the necessary changes have been pulled. Since initially Bug#37148 was null-merged into 6.0, the changeset that is now being cherry-picked was likewise null merged into next-4284. Now that Bug#37148 has been reapplied to 6.0, try to make it work with next-4284. This is also necessary to be able to pull other changes from 5.1-rep into next-4284. To resolve the merge issues use this changeset applied to 6.0: revid:jperkin@sun.com-20091216103628-ylhqf7s6yegui2t9 revno: 3776.1.1 committer: He Zhenxing branch nick: 6.0-codebase-bugfixing timestamp: Thu 2009-12-17 17:02:50 +0800 message: Fix merge problem with Bug#37148 --- sql/sql_delete.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index bae922a7411..dcb23add037 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -851,9 +851,10 @@ void multi_delete::abort() if (mysql_bin_log.is_open()) { int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); - thd->binlog_query(THD::ROW_QUERY_TYPE, - thd->query(), thd->query_length(), - transactional_tables, FALSE, errcode); + /* possible error of writing binary log is ignored deliberately */ + (void) thd->binlog_query(THD::ROW_QUERY_TYPE, + thd->query(), thd->query_length(), + transactional_tables, FALSE, errcode); } thd->transaction.all.modified_non_trans_table= true; } @@ -1254,8 +1255,9 @@ end: { /* In RBR, the statement is not binlogged if the table is temporary. */ if (!is_temporary_table || !thd->current_stmt_binlog_row_based) - write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - my_ok(thd); // This should return record count + error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); + if (!error) + my_ok(thd); // This should return record count } if (has_mdl_lock) thd->mdl_context.release_transactional_locks(); -- cgit v1.2.1 From c7e7a7d20cae8a22e7730b2015e08233d680ed02 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Mon, 8 Feb 2010 23:19:55 +0300 Subject: Fix for bug #50913 "Deadlock between open_and_lock_tables_derived and MDL". Concurrent execution of a multi-DELETE statement and ALTER TABLE statement which affected one of the tables used in the multi-DELETE sometimes led to deadlock. Similar deadlocks might have occured when one performed INSERT/UPDATE/DELETE on a view and concurrently executed ALTER TABLE for the view's underlying table, or when one concurrently executed TRUNCATE TABLE for InnoDB table and ALTER TABLE for the same table. These deadlocks were caused by a discrepancy between types of metadata and thr_lock.cc locks acquired by those statements. What happened was that multi-DELETE/TRUNCATE/DML-through-the- view statement in the first connection acquired SR lock on a table, then ALTER TABLE would come in in the second connection and acquire SNW metadata lock and TL_WRITE_ALLOW_READ thr_lock.c lock and then would start waiting for the first connection during lock upgrade. After that the statement in the first connection would try to acquire TL_WRITE lock on table and would start waiting for the second connection, creating a deadlock. This patch solves this problem by ensuring that we acquire SW metadata lock in all cases in which we acquiring write thr_lock.c lock. This guarantees that deadlocks like the one described above won't occur since all lock conflicts in such situation are resolved within MDL subsystem. This patch also adds assert which should guarantee that such situations won't arise in future. --- sql/sql_delete.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0f84f5e9d73..0f5d51f924d 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1052,6 +1052,7 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) bool error, save_binlog_row_based= thd->is_current_stmt_binlog_format_row(); DBUG_ENTER("mysql_truncate_by_delete"); table_list->lock_type= TL_WRITE; + table_list->mdl_request.set_type(MDL_SHARED_WRITE); mysql_init_select(thd->lex); thd->clear_current_stmt_binlog_format_row(); /* Delete all rows from table */ -- cgit v1.2.1 From 5bb67f34b864ea40c74dc729fdc960de13718ab3 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 11 Feb 2010 11:23:39 +0100 Subject: Bug #45225 Locking: hang if drop table with no timeout This patch introduces timeouts for metadata locks. The timeout is specified in seconds using the new dynamic system variable "lock_wait_timeout" which has both GLOBAL and SESSION scopes. Allowed values range from 1 to 31536000 seconds (= 1 year). The default value is 1 year. The new server parameter "lock-wait-timeout" can be used to set the default value parameter upon server startup. "lock_wait_timeout" applies to all statements that use metadata locks. These include DML and DDL operations on tables, views, stored procedures and stored functions. They also include LOCK TABLES, FLUSH TABLES WITH READ LOCK and HANDLER statements. The patch also changes thr_lock.c code (table data locks used by MyISAM and other simplistic engines) to use the same system variable. InnoDB row locks are unaffected. One exception to the handling of the "lock_wait_timeout" variable is delayed inserts. All delayed inserts are executed with a timeout of 1 year regardless of the setting for the global variable. As the connection issuing the delayed insert gets no notification of delayed insert timeouts, we want to avoid unnecessary timeouts. It's important to note that the timeout value is used for each lock acquired and that one statement can take more than one lock. A statement can therefore block for longer than the lock_wait_timeout value before reporting a timeout error. When lock timeout occurs, ER_LOCK_WAIT_TIMEOUT is reported. Test case added to lock_multi.test. --- sql/sql_delete.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0f5d51f924d..ea466da8ea1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1210,7 +1210,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) mdl_requests.push_front(&mdl_request); mdl_requests.push_front(&mdl_global_request); - if (thd->mdl_context.acquire_locks(&mdl_requests)) + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) DBUG_RETURN(TRUE); has_mdl_lock= TRUE; -- cgit v1.2.1 From dd42aab8406e70caec4f58435c0281cddc8ea2c0 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 24 Feb 2010 18:04:00 +0100 Subject: Followup to Bug#45225 Locking: hang if drop table with no timeout This patch prevents system threads and system table accesses from using user-specified values for "lock_wait_timeout". Instead all such accesses are done using the default value (1 year). This prevents background tasks (such as replication, events, accessing stored function definitions, logging, reading time-zone information, etc.) from failing in cases where the global value of "lock_wait_timeout" is set very low. The patch also simplifies the open tables API. Rather than adding another convenience function for opening and locking system tables, this patch removes most of the existing convenience functions for open_and_lock_tables_derived(). Before, open_and_lock_tables() was a convenience function that enforced derived tables handling, while open_and_lock_tables_derived() was the main function where derived tables handling was optional. Now, this convencience function is gone and the main function is renamed to open_and_lock_tables(). No test case added as it would have required the use of --sleep to check that system threads and system tables have a different timeout value from the user-specified "lock_wait_timeout" system variable. --- sql/sql_delete.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index ea466da8ea1..2c3242c10cd 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -57,7 +57,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, THD::STMT_QUERY_TYPE : THD::ROW_QUERY_TYPE; - if (open_and_lock_tables(thd, table_list)) + if (open_and_lock_tables(thd, table_list, TRUE, 0)) DBUG_RETURN(TRUE); if (!(table= table_list->table)) { -- cgit v1.2.1