From 96f680aa6589138058a820987e5cf8600f024e81 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 29 Feb 2016 13:58:41 +0100 Subject: [PATCH 01/17] Raise version number after cloning 5.5.49 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ba54e70bc3e..58a6b369de9 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=49 +MYSQL_VERSION_PATCH=50 MYSQL_VERSION_EXTRA= From c7e68606c02b7f87a48c27eb358d4d07480f40f4 Mon Sep 17 00:00:00 2001 From: Arun Kuruvila Date: Tue, 1 Mar 2016 10:17:25 +0530 Subject: [PATCH 02/17] Bug#21920657: SSL-CA FAILS SILENTLY IF THE PATH CANNOT BE FOUND Description:- Failure during the validation of CA certificate path which is provided as an option for 'ssl-ca' returns two different errors for YaSSL and OPENSSL. Analysis:- 'ssl-ca', option used for specifying the ssl ca certificate path. Failing to validate this certificate with OPENSSL returns an error, "ERROR 2026 (HY000): SSL connection error: SSL_CTX_set_default_verify_paths failed". While YASSL returns "ERROR 2026 (HY000): SSL connection error: ASN: bad other signature confirmation". Error returned by the OPENSSL is correct since "SSL_CTX_load_verify_locations()" returns 0 (in case of OPENSSL) for the failure and sets error as "SSL_INITERR_BAD_PATHS". In case of YASSL, "SSL_CTX_load_verify_locations()" returns an error number which is less than or equal to 0 in case of error. Error numbers for YASSL is mentioned in the file, 'extra/yassl/include/openssl/ssl.h'(line no : 292). Also 'ssl-ca' does not accept tilde home directory path substitution. Fix:- The condition which checks for the error in the "SSL_CTX_load_verify_locations()" is changed in order to accommodate YASSL as well. A logic is written in "mysql_ssl_set()" in order accept the tilde home directory path substitution for all ssl options. --- mysql-test/r/ssl_ca.result | 24 ++++++++++++++++++++++++ mysql-test/t/ssl_ca.test | 35 +++++++++++++++++++++++++++++++++++ sql-common/client.c | 23 +++++++++++++++++++---- vio/viosslfactories.c | 4 ++-- 4 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 mysql-test/r/ssl_ca.result create mode 100644 mysql-test/t/ssl_ca.test diff --git a/mysql-test/r/ssl_ca.result b/mysql-test/r/ssl_ca.result new file mode 100644 index 00000000000..ffc5671f85f --- /dev/null +++ b/mysql-test/r/ssl_ca.result @@ -0,0 +1,24 @@ +# +# Bug#21920657: SSL-CA FAILS SILENTLY IF THE PATH CANNOT BE FOUND +# +# try to connect with wrong '--ssl-ca' path : should fail +ERROR 2026 (HY000): SSL connection error: SSL_CTX_set_default_verify_paths failed +# try to connect with correct '--ssl-ca' path : should connect +Variable_name Value +Ssl_cipher DHE-RSA-AES256-SHA +# +# Bug#21920678: SSL-CA DOES NOT ACCEPT ~USER TILDE HOME DIRECTORY +# PATH SUBSTITUTION +# +# try to connect with '--ssl-ca' option using tilde home directoy +# path substitution : should connect +Variable_name Value +Ssl_cipher DHE-RSA-AES256-SHA +# try to connect with '--ssl-key' option using tilde home directoy +# path substitution : should connect +Variable_name Value +Ssl_cipher DHE-RSA-AES256-SHA +# try to connect with '--ssl-cert' option using tilde home directoy +# path substitution : should connect +Variable_name Value +Ssl_cipher DHE-RSA-AES256-SHA diff --git a/mysql-test/t/ssl_ca.test b/mysql-test/t/ssl_ca.test new file mode 100644 index 00000000000..92695de4b0d --- /dev/null +++ b/mysql-test/t/ssl_ca.test @@ -0,0 +1,35 @@ +--source include/have_ssl.inc +--source include/not_embedded.inc + +--echo # +--echo # Bug#21920657: SSL-CA FAILS SILENTLY IF THE PATH CANNOT BE FOUND +--echo # + +--echo # try to connect with wrong '--ssl-ca' path : should fail +--error 1 +--exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/wrong-cacert.pem --ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem --ssl-cert=$MYSQL_TEST_DIR/std_data/client-cert.pem test -e "SHOW STATUS LIKE 'Ssl_cipher'" 2>&1 + +--echo # try to connect with correct '--ssl-ca' path : should connect +--exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem --ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem --ssl-cert=$MYSQL_TEST_DIR/std_data/client-cert.pem test -e "SHOW STATUS LIKE 'Ssl_cipher'" + +--echo # +--echo # Bug#21920678: SSL-CA DOES NOT ACCEPT ~USER TILDE HOME DIRECTORY +--echo # PATH SUBSTITUTION +--echo # + +--let $mysql_test_dir_path= `SELECT REPLACE('$MYSQL_TEST_DIR', '$HOME', '~')` + +--echo # try to connect with '--ssl-ca' option using tilde home directoy +--echo # path substitution : should connect +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--exec $MYSQL --ssl-ca=$mysql_test_dir_path/std_data/cacert.pem --ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem --ssl-cert=$MYSQL_TEST_DIR/std_data/client-cert.pem test -e "SHOW STATUS LIKE 'Ssl_cipher'" + +--echo # try to connect with '--ssl-key' option using tilde home directoy +--echo # path substitution : should connect +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem --ssl-key=$mysql_test_dir_path/std_data/client-key.pem --ssl-cert=$MYSQL_TEST_DIR/std_data/client-cert.pem test -e "SHOW STATUS LIKE 'Ssl_cipher'" + +--echo # try to connect with '--ssl-cert' option using tilde home directoy +--echo # path substitution : should connect +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem --ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem --ssl-cert=$mysql_test_dir_path/std_data/client-cert.pem test -e "SHOW STATUS LIKE 'Ssl_cipher'" diff --git a/sql-common/client.c b/sql-common/client.c index 0ef70eb7f56..cd9b6a71c53 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1204,6 +1204,21 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd) my_strdup((STR), MYF(MY_WME)) : NULL; \ } while (0) +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) +static char *set_ssl_option_unpack_path(const char *arg) +{ + char *opt_var= NULL; + if (arg) + { + char *buff= (char *)my_malloc(FN_REFLEN + 1, MYF(MY_WME)); + unpack_filename(buff, (char *)arg); + opt_var= my_strdup(buff, MYF(MY_WME)); + my_free(buff); + } + return opt_var; +} +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ + void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group) { @@ -1798,10 +1813,10 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , { DBUG_ENTER("mysql_ssl_set"); #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) - mysql->options.ssl_key= strdup_if_not_null(key); - mysql->options.ssl_cert= strdup_if_not_null(cert); - mysql->options.ssl_ca= strdup_if_not_null(ca); - mysql->options.ssl_capath= strdup_if_not_null(capath); + mysql->options.ssl_key= set_ssl_option_unpack_path(key); + mysql->options.ssl_cert= set_ssl_option_unpack_path(cert); + mysql->options.ssl_ca= set_ssl_option_unpack_path(ca); + mysql->options.ssl_capath= set_ssl_option_unpack_path(capath); mysql->options.ssl_cipher= strdup_if_not_null(cipher); #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ DBUG_RETURN(0); diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index ea46d9c0302..3aa1c873163 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -250,7 +250,7 @@ new_VioSSLFd(const char *key_file, const char *cert_file, } /* Load certs from the trusted ca */ - if (SSL_CTX_load_verify_locations(ssl_fd->ssl_context, ca_file, ca_path) == 0) + if (SSL_CTX_load_verify_locations(ssl_fd->ssl_context, ca_file, ca_path) <= 0) { DBUG_PRINT("warning", ("SSL_CTX_load_verify_locations failed")); if (ca_file || ca_path) From bb32ac1d9b6c1333316a8da32ea46105eceefa29 Mon Sep 17 00:00:00 2001 From: Venkatesh Duggirala Date: Tue, 1 Mar 2016 11:58:45 +0530 Subject: [PATCH 03/17] BUG#17018343 SLAVE CRASHES WHEN APPLYING ROW-BASED BINLOG ENTRIES IN CASCADING REPLICATION Problem: In RBR mode, merge table updates are not successfully applied on a cascading replication. Analysis & Fix: Every type of row event is preceded by one or more table_map_log_events that gives the information about all the tables that are involved in the row event. Server maintains the list in RPL_TABLE_LIST and it goes through all the tables and checks for the compatibility between master and slave. Before checking for the compatibility, it calls 'open_tables()' which takes the list of all tables that needs to be locked and opened. In RBR, because of the Table_map_log_event , we already have all the tables including base tables in the list. But the open_tables() which is generic call takes care of appending base tables if the list contains merge tables. There is an assumption in the current replication layer logic that these tables (TABLE_LIST type objects) are always added in the end of the list. Replication layer maintains the count of tables(tables_to_lock_count) that needs to be verified for compatibility check and runs through only those many tables from the list and rest of the objects in linked list can be skipped. But this assumption is wrong. open_tables()->..->add_children_to_list() adds base tables to the list immediately after seeing the merge table in the list. For eg: If the list passed to open_tables() is t1->t2->t3 where t3 is merge table (and t1 and t2 are base tables), it adds t1'->t2' to the list after t3. New table list looks like t1->t2->t3->t1'->t2'. It looks like it added at the end of the list but that is not correct. If the list passed to open_tables() is t3->t1->t2 where t3 is merge table (and t1 and t2 are base tables), the new prepared list will be t3->t1'->t2'->t1->t2. Where t1' and t2' are of TABLE_LIST objects which were added by add_children_to_list() call and replication layer should not look into them. Here tables_to_lock_count will not help as the objects are added in between the list. Fix: After investigating add_children_list() logic (which is called from open_tables()), there is no flag/logic in it to skip adding the children to the list even if the children are already included in the table list. Hence to fix the issue, a logic should be added in the replication layer to skip children in the list by checking whether 'parent_l' is non-null or not. If it is children, we will skip 'compatibility' check for that table. Also this patch is not removing 'tables_to_lock_count' logic for the performance issues if there are any children at the end of the list, those can be easily skipped directly by stopping the loop with tables_to_lock_count check. --- sql/log_event.cc | 46 +++++++++++++++++++++++++++++++++------ sql/log_event_old.cc | 52 +++++++++++++++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index accb6a28dbd..702cf1d575a 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -7930,9 +7930,6 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) /* When the open and locking succeeded, we check all tables to ensure that they still have the correct type. - - We can use a down cast here since we know that every table added - to the tables_to_lock is a RPL_TABLE_LIST. */ { @@ -7951,10 +7948,37 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) NOTE: The base tables are added here are removed when close_thread_tables is called. */ - RPL_TABLE_LIST *ptr= rli->tables_to_lock; - for (uint i= 0 ; ptr && (i < rli->tables_to_lock_count); - ptr= static_cast(ptr->next_global), i++) + TABLE_LIST *table_list_ptr= rli->tables_to_lock; + for (uint i=0 ; table_list_ptr && (i < rli->tables_to_lock_count); + table_list_ptr= table_list_ptr->next_global, i++) { + /* + Below if condition takes care of skipping base tables that + make up the MERGE table (which are added by open_tables() + call). They are added next to the merge table in the list. + For eg: If RPL_TABLE_LIST is t3->t1->t2 (where t1 and t2 + are base tables for merge table 't3'), open_tables will modify + the list by adding t1 and t2 again immediately after t3 in the + list (*not at the end of the list*). New table_to_lock list will + look like t3->t1'->t2'->t1->t2 (where t1' and t2' are TABLE_LIST + objects added by open_tables() call). There is no flag(or logic) in + open_tables() that can skip adding these base tables to the list. + So the logic here should take care of skipping them. + + tables_to_lock_count logic will take care of skipping base tables + that are added at the end of the list. + For eg: If RPL_TABLE_LIST is t1->t2->t3, open_tables will modify + the list into t1->t2->t3->t1'->t2'. t1' and t2' will be skipped + because tables_to_lock_count logic in this for loop. + */ + if (table_list_ptr->parent_l) + continue; + /* + We can use a down cast here since we know that every table added + to the tables_to_lock is a RPL_TABLE_LIST (or child table which is + skipped above). + */ + RPL_TABLE_LIST *ptr= static_cast(table_list_ptr); DBUG_ASSERT(ptr->m_tabledef_valid); TABLE *conv_table; if (!ptr->m_tabledef.compatible_with(thd, const_cast(rli), @@ -7995,7 +8019,15 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) */ TABLE_LIST *ptr= rli->tables_to_lock; for (uint i=0 ; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++) + { + /* + Please see comment in above 'for' loop to know the reason + for this if condition + */ + if (ptr->parent_l) + continue; const_cast(rli)->m_table_map.set_table(ptr->table_id, ptr->table); + } #ifdef HAVE_QUERY_CACHE query_cache.invalidate_locked_for_write(rli->tables_to_lock); diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index ed53aec6006..eb9678ddaa5 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -119,16 +119,25 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info /* When the open and locking succeeded, we check all tables to ensure that they still have the correct type. - - We can use a down cast here since we know that every table added - to the tables_to_lock is a RPL_TABLE_LIST. */ { - RPL_TABLE_LIST *ptr= rli->tables_to_lock; - for (uint i= 0 ; ptr&& (i< rli->tables_to_lock_count); - ptr= static_cast(ptr->next_global), i++) + TABLE_LIST *table_list_ptr= rli->tables_to_lock; + for (uint i=0 ; table_list_ptr&& (i< rli->tables_to_lock_count); + table_list_ptr= table_list_ptr->next_global, i++) { + /* + Please see comment in log_event.cc-Rows_log_event::do_apply_event() + function for the explanation of the below if condition + */ + if (table_list_ptr->parent_l) + continue; + /* + We can use a down cast here since we know that every table added + to the tables_to_lock is a RPL_TABLE_LIST(or child table which is + skipped above). + */ + RPL_TABLE_LIST *ptr=static_cast(table_list_ptr); DBUG_ASSERT(ptr->m_tabledef_valid); TABLE *conv_table; if (!ptr->m_tabledef.compatible_with(thd, const_cast(rli), @@ -162,7 +171,15 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info */ TABLE_LIST *ptr= rli->tables_to_lock; for (uint i=0; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++) + { + /* + Please see comment in log_event.cc-Rows_log_event::do_apply_event() + function for the explanation of the below if condition + */ + if (ptr->parent_l) + continue; const_cast(rli)->m_table_map.set_table(ptr->table_id, ptr->table); + } #ifdef HAVE_QUERY_CACHE query_cache.invalidate_locked_for_write(rli->tables_to_lock); #endif @@ -1545,16 +1562,25 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) /* When the open and locking succeeded, we check all tables to ensure that they still have the correct type. - - We can use a down cast here since we know that every table added - to the tables_to_lock is a RPL_TABLE_LIST. */ { - RPL_TABLE_LIST *ptr= rli->tables_to_lock; - for (uint i= 0 ; ptr&& (i< rli->tables_to_lock_count); - ptr= static_cast(ptr->next_global), i++) + TABLE_LIST *table_list_ptr= rli->tables_to_lock; + for (uint i=0; table_list_ptr&& (i< rli->tables_to_lock_count); + table_list_ptr= static_cast(table_list_ptr->next_global), i++) { + /* + Please see comment in log_event.cc-Rows_log_event::do_apply_event() + function for the explanation of the below if condition + */ + if (table_list_ptr->parent_l) + continue; + /* + We can use a down cast here since we know that every table added + to the tables_to_lock is a RPL_TABLE_LIST (or child table which is + skipped above). + */ + RPL_TABLE_LIST *ptr=static_cast(table_list_ptr); TABLE *conv_table; if (ptr->m_tabledef.compatible_with(thd, const_cast(rli), ptr->table, &conv_table)) From 8361151765cc5efd72ad18c5553f80aa440a1d83 Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Tue, 1 Mar 2016 12:29:51 +0530 Subject: [PATCH 04/17] Bug#20685029: SLAVE IO THREAD SHOULD STOP WHEN DISK IS FULL Bug#21753696: MAKE SHOW SLAVE STATUS NON BLOCKING IF IO THREAD WAITS FOR DISK SPACE Problem: ======== Currently SHOW SLAVE STATUS blocks if IO thread waits for disk space. This makes automation tools verifying server health block on taking relevant action. Finally this will create SHOW SLAVE STATUS piles. Analysis: ========= SHOW SLAVE STATUS hangs on mi->data_lock if relay log write is waiting for free disk space while holding mi->data_lock. mi->data_lock is needed to protect the format description event (mi->format_description_event) which is accessed by the clients running FLUSH LOGS and slave IO thread. Note relay log writes don't need to be protected by mi->data_lock, LOCK_log is used to protect relay log between IO and SQL thread (see MYSQL_BIN_LOG::append_event). The code takes mi->data_lock to protect mi->format_description_event during relay log rotate which might get triggered right after relay log write. Fix: ==== Release the data_lock just for the duration of writing into relay log. Made change to ensure the following lock order is maintained to avoid deadlocks. data_lock, LOCK_log data_lock is held during relay log rotations to protect the description event. --- mysql-test/include/assert_grep.inc | 154 ++++++++++++++++++ mysql-test/include/rpl_init.inc | 31 +++- mysql-test/include/rpl_reconnect.inc | 33 +++- mysql-test/include/start_slave_sql.inc | 39 +++++ .../r/rpl_io_thd_wait_for_disk_space.result | 15 ++ .../rpl/t/rpl_io_thd_wait_for_disk_space.test | 71 ++++++++ mysys/errors.c | 13 +- mysys/my_write.c | 10 +- sql/log.cc | 69 +++++++- sql/log.h | 7 +- sql/slave.cc | 40 +++-- sql/slave.h | 4 +- sql/sql_reload.cc | 4 +- 13 files changed, 443 insertions(+), 47 deletions(-) create mode 100644 mysql-test/include/assert_grep.inc create mode 100644 mysql-test/include/start_slave_sql.inc create mode 100644 mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result create mode 100644 mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test diff --git a/mysql-test/include/assert_grep.inc b/mysql-test/include/assert_grep.inc new file mode 100644 index 00000000000..a980a6d73b1 --- /dev/null +++ b/mysql-test/include/assert_grep.inc @@ -0,0 +1,154 @@ +# ==== Purpose ==== +# +# Grep a file for a pattern, produce a single string out of the +# matching lines, and assert that the string matches a given regular +# expression. +# +# ==== Usage ==== +# +# --let $assert_text= TEXT +# --let $assert_file= FILE +# --let $assert_select= REGEX +# [--let $assert_match= REGEX | --let $assert_count= NUMBER] +# [--let $assert_only_after= REGEX] +# --source include/assert_grep.inc +# +# Parameters: +# +# $assert_text +# Text that describes what is being checked. This text is written to +# the query log so it should not contain non-deterministic elements. +# +# $assert_file +# File to search. +# +# $assert_select +# All lines matching this text will be checked. +# +# $assert_match +# The script will find all lines that match $assert_select, +# concatenate them to a long string, and assert that it matches +# $assert_match. +# +# $assert_count +# Instead of asserting that the selected lines match +# $assert_match, assert that there were exactly $assert_count +# matching lines. +# +# $assert_only_after +# Reset all the lines matched and the counter when finding this pattern. +# It is useful for searching things in the mysqld.err log file just +# after the last server restart for example (discarding the log content +# of previous server executions). + + +if (!$assert_text) +{ + --die !!!ERROR IN TEST: you must set $assert_text +} +if (!$assert_file) +{ + --die !!!ERROR IN TEST: you must set $assert_file +} +if (!$assert_select) +{ + --die !!!ERROR IN TEST: you must set $assert_select +} +if ($assert_match == '') +{ + if ($assert_count == '') + { + --die !!!ERROR IN TEST: you must set either $assert_match or $assert_count + } +} +if ($assert_match != '') +{ + if ($assert_count != '') + { + --echo assert_text='$assert_text' assert_count='$assert_count' + --die !!!ERROR IN TEST: you must set only one of $assert_match or $assert_count + } +} + + +--let $include_filename= assert_grep.inc [$assert_text] +--source include/begin_include_file.inc + + +--let _AG_ASSERT_TEXT= $assert_text +--let _AG_ASSERT_FILE= $assert_file +--let _AG_ASSERT_SELECT= $assert_select +--let _AG_ASSERT_MATCH= $assert_match +--let _AG_ASSERT_COUNT= $assert_count +--let _AG_OUT= `SELECT CONCAT('$MYSQLTEST_VARDIR/tmp/_ag_', UUID())` +--let _AG_ASSERT_ONLY_AFTER= $assert_only_after + + +--perl + use strict; + use warnings; + my $file= $ENV{'_AG_ASSERT_FILE'}; + my $assert_select= $ENV{'_AG_ASSERT_SELECT'}; + my $assert_match= $ENV{'_AG_ASSERT_MATCH'}; + my $assert_count= $ENV{'_AG_ASSERT_COUNT'}; + my $assert_only_after= $ENV{'_AG_ASSERT_ONLY_AFTER'}; + my $out= $ENV{'_AG_OUT'}; + + my $result= ''; + my $count= 0; + open(FILE, "$file") or die("Error $? opening $file: $!\n"); + while () { + my $line = $_; + if ($assert_only_after && $line =~ /$assert_only_after/) { + $result = ""; + $count = 0; + } + if ($line =~ /$assert_select/) { + if ($assert_count ne '') { + $count++; + } + else { + $result .= $line; + } + } + } + close(FILE) or die("Error $? closing $file: $!"); + open OUT, "> $out" or die("Error $? opening $out: $!"); + if ($assert_count ne '' && ($count != $assert_count)) { + print OUT ($count) or die("Error $? writing $out: $!"); + } + elsif ($assert_count eq '' && $result !~ /$assert_match/) { + print OUT ($result) or die("Error $? writing $out: $!"); + } + else { + print OUT ("assert_grep.inc ok"); + } + close OUT or die("Error $? closing $out: $!"); +EOF + + +--let $_ag_outcome= `SELECT LOAD_FILE('$_AG_OUT')` +if ($_ag_outcome != 'assert_grep.inc ok') +{ + --source include/show_rpl_debug_info.inc + --echo include/assert_grep.inc failed! + --echo assert_text: '$assert_text' + --echo assert_file: '$assert_file' + --echo assert_select: '$assert_select' + --echo assert_match: '$assert_match' + --echo assert_count: '$assert_count' + --echo assert_only_after: '$assert_only_after' + if ($assert_match != '') + { + --echo matching lines: '$_ag_outcome' + } + if ($assert_count != '') + { + --echo number of matching lines: $_ag_outcome + } + --die assert_grep.inc failed. +} + + +--let $include_filename= include/assert_grep.inc [$assert_text] +--source include/end_include_file.inc diff --git a/mysql-test/include/rpl_init.inc b/mysql-test/include/rpl_init.inc index 2abfd901b03..820bc8e9016 100644 --- a/mysql-test/include/rpl_init.inc +++ b/mysql-test/include/rpl_init.inc @@ -43,6 +43,7 @@ # # [--let $rpl_server_count= 7] # --let $rpl_topology= 1->2->3->1->4, 2->5, 6->7 +# [--let $rpl_extra_connections_per_server= 1] # [--let $rpl_check_server_ids= 1] # [--let $rpl_skip_change_master= 1] # [--let $rpl_skip_start_slave= 1] @@ -65,6 +66,12 @@ # want to specify the empty topology (no server replicates at # all), you have to set $rpl_topology=none. # +# $rpl_extra_connections_per_server +# By default, this script creates connections server_N and +# server_N_1. If you can set this variable to a number, the +# script creates: +# server_N, server_N_1, ..., server_N_$rpl_extra_connections_per_server +# # $rpl_check_server_ids # If $rpl_check_server_ids is set, this script checks that the # @@server_id of all servers are different. This is normally @@ -139,8 +146,17 @@ if (!$SERVER_MYPORT_4) # Check that $rpl_server_count is set if (!$rpl_server_count) { - --let $_compute_rpl_server_count= `SELECT REPLACE('$rpl_topology', '->', ',')` - --let $rpl_server_count= `SELECT GREATEST($_compute_rpl_server_count)` + --let $rpl_server_count= `SELECT REPLACE('$rpl_topology', '->', ',')` + if (`SELECT LOCATE(',', '$rpl_server_count')`) + { + --let $rpl_server_count= `SELECT GREATEST($rpl_server_count)` + } +} + +--let $_rpl_extra_connections_per_server= $rpl_extra_connections_per_server +if ($_rpl_extra_connections_per_server == '') +{ + --let $_rpl_extra_connections_per_server= 1 } @@ -159,15 +175,20 @@ if (!$rpl_debug) # Create two connections to each server; reset master/slave, select # database, set autoinc variables. --let $_rpl_server= $rpl_server_count ---let $_rpl_one= _1 +--let $underscore= _ while ($_rpl_server) { # Connect. --let $rpl_server_number= $_rpl_server --let $rpl_connection_name= server_$_rpl_server --source include/rpl_connect.inc - --let $rpl_connection_name= server_$_rpl_server$_rpl_one - --source include/rpl_connect.inc + --let $_rpl_connection_number= 1 + while ($_rpl_connection_number <= $_rpl_extra_connections_per_server) + { + --let $rpl_connection_name= server_$_rpl_server$underscore$_rpl_connection_number + --source include/rpl_connect.inc + --inc $_rpl_connection_number + } # Configure server. --let $rpl_connection_name= server_$_rpl_server diff --git a/mysql-test/include/rpl_reconnect.inc b/mysql-test/include/rpl_reconnect.inc index cdbbd0a1bf1..673f382bac0 100644 --- a/mysql-test/include/rpl_reconnect.inc +++ b/mysql-test/include/rpl_reconnect.inc @@ -12,6 +12,7 @@ # ==== Usage ==== # # --let $rpl_server_number= N +# [--let $rpl_extra_connections_per_server= 1] # [--let $rpl_debug= 1] # --source include/rpl_reconnect.inc # @@ -21,7 +22,7 @@ # master server, 2 the slave server, 3 the 3rd server, and so on. # Cf. include/rpl_init.inc # -# $rpl_debug +# $rpl_extra_connections_per_server, $rpl_debug # See include/rpl_init.inc --let $include_filename= rpl_reconnect.inc @@ -32,6 +33,11 @@ if (!$rpl_server_number) --die ERROR IN TEST: you must set $rpl_server_number before you source rpl_connect.inc } +if ($_rpl_extra_connections_per_server == '') +{ + --let $_rpl_extra_connections_per_server= 1 +} + if ($rpl_debug) { @@ -72,10 +78,14 @@ if (!$_rpl_server_number) --source include/rpl_connection.inc --enable_reconnect ---let $_rpl_one= _1 ---let $rpl_connection_name= server_$rpl_server_number$_rpl_one ---source include/rpl_connection.inc ---enable_reconnect +--let $_rpl_connection_number= 1 +while ($_rpl_connection_number <= $_rpl_extra_connections_per_server) +{ + --let $rpl_connection_name= server_$rpl_server_number$underscore$_rpl_connection_number + --source include/rpl_connection.inc + --enable_reconnect + --inc $_rpl_connection_number +} if ($rpl_debug) { @@ -122,10 +132,15 @@ if (!$_rpl_server_number) --source include/wait_until_connected_again.inc --disable_reconnect ---let $rpl_connection_name= server_$rpl_server_number$_rpl_one ---source include/rpl_connection.inc ---source include/wait_until_connected_again.inc ---disable_reconnect +--let $_rpl_connection_number= 1 +while ($_rpl_connection_number <= $_rpl_extra_connections_per_server) +{ + --let $rpl_connection_name= server_$rpl_server_number$underscore$_rpl_connection_number + --source include/rpl_connection.inc + --source include/wait_until_connected_again.inc + --disable_reconnect + --inc $_rpl_connection_number +} --let $include_filename= rpl_reconnect.inc diff --git a/mysql-test/include/start_slave_sql.inc b/mysql-test/include/start_slave_sql.inc new file mode 100644 index 00000000000..9cb66a2eb40 --- /dev/null +++ b/mysql-test/include/start_slave_sql.inc @@ -0,0 +1,39 @@ +# ==== Purpose ==== +# +# Issues START SLAVE SQL_THREAD on the current connection. Then waits +# until the SQL thread has started, or until a timeout is reached. +# +# Please use this instead of 'START SLAVE SQL_THREAD', to reduce the +# risk of races in test cases. +# +# +# ==== Usage ==== +# +# [--let $slave_timeout= NUMBER] +# [--let $rpl_debug= 1] +# --source include/start_slave_sql.inc +# +# Parameters: +# $slave_timeout +# See include/wait_for_slave_param.inc +# +# $rpl_debug +# See include/rpl_init.inc + + +--let $include_filename= start_slave_sql.inc +--source include/begin_include_file.inc + + +if (!$rpl_debug) +{ + --disable_query_log +} + + +START SLAVE SQL_THREAD; +--source include/wait_for_slave_sql_to_start.inc + + +--let $include_filename= start_slave_sql.inc +--source include/end_include_file.inc diff --git a/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result b/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result new file mode 100644 index 00000000000..c601c9a4169 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result @@ -0,0 +1,15 @@ +include/master-slave.inc +[connection master] +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1); +CALL mtr.add_suppression("Disk is full writing"); +CALL mtr.add_suppression("Retry in 60 secs"); +include/stop_slave_sql.inc +SET @@GLOBAL.DEBUG= 'd,simulate_io_thd_wait_for_disk_space'; +INSERT INTO t1 VALUES(2); +include/wait_for_slave_param.inc [Slave_IO_State] +SET @@GLOBAL.DEBUG= '$debug_saved'; +include/assert_grep.inc [Found the disk full error message on the slave] +include/start_slave_sql.inc +DROP TABLE t1; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test b/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test new file mode 100644 index 00000000000..d0c74b94cd1 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test @@ -0,0 +1,71 @@ +# ==== Purpose ==== +# +# Check that the execution of SHOW SLAVE STATUS command is not blocked when IO +# thread is blocked waiting for disk space. +# +# ==== Implementation ==== +# +# Simulate a scenario where IO thread is waiting for disk space while writing +# into the relay log. Execute SHOW SLAVE STATUS command after IO thread is +# blocked waiting for space. The command should not be blocked. +# +# ==== References ==== +# +# Bug#21753696: MAKE SHOW SLAVE STATUS NON BLOCKING IF IO THREAD WAITS FOR +# DISK SPACE +# Bug#20685029: SLAVE IO THREAD SHOULD STOP WHEN DISK IS FULL +# +############################################################################### +--source include/have_debug.inc +--source include/master-slave.inc + +# Generate events to be replicated to the slave +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1); +--sync_slave_with_master + +# Those errors will only happen in the slave +CALL mtr.add_suppression("Disk is full writing"); +CALL mtr.add_suppression("Retry in 60 secs"); + +# Stop the SQL thread to avoid writing on disk +--source include/stop_slave_sql.inc + +# Set the debug option that will simulate disk full +--let $debug_saved= `SELECT @@GLOBAL.DEBUG` +SET @@GLOBAL.DEBUG= 'd,simulate_io_thd_wait_for_disk_space'; + +# Generate events to be replicated to the slave +--connection master +INSERT INTO t1 VALUES(2); + +--connection slave +# Wait until IO thread is queuing events from master +# Notice that this is performed by querying SHOW SLAVE STATUS +--let $slave_param= Slave_IO_State +--let $slave_param_value= Queueing master event to the relay log +--source include/wait_for_slave_param.inc + +# Get the relay log file name, also using SHOW SLAVE STATUS +--let $relay_log_file= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) + +# Restore the debug options to "simulate" freed space on disk +SET @@GLOBAL.DEBUG= '$debug_saved'; + +# There should be a message in the error log of the slave stating +# that it was waiting for space to write on the relay log. +--let $assert_file=$MYSQLTEST_VARDIR/log/mysqld.2.err +# Grep only after the message that the I/O thread has started +--let $assert_only_after= Slave I/O .* connected to master .*replication started in log .* at position +--let $assert_count= 1 +--let $assert_select=Disk is full writing .*$relay_log_file.* +--let $assert_text= Found the disk full error message on the slave +--source include/assert_grep.inc + +# Start the SQL thread to let the slave to sync and finish gracefully +--source include/start_slave_sql.inc + +# Cleanup +--connection master +DROP TABLE t1; +--source include/rpl_end.inc diff --git a/mysys/errors.c b/mysys/errors.c index ddd65836b30..942fd3230c0 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -109,6 +109,7 @@ void init_glob_errs() */ void wait_for_free_space(const char *filename, int errors) { + size_t time_to_sleep= MY_WAIT_FOR_USER_TO_FIX_PANIC; if (!(errors % MY_WAIT_GIVE_USER_A_MESSAGE)) { my_printf_warning(EE(EE_DISK_FULL), @@ -119,10 +120,14 @@ void wait_for_free_space(const char *filename, int errors) } DBUG_EXECUTE_IF("simulate_no_free_space_error", { - (void) sleep(1); - return; + time_to_sleep= 1; }); - (void) sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC); + DBUG_EXECUTE_IF("simulate_io_thd_wait_for_disk_space", + { + time_to_sleep= 1; + }); + + (void) sleep(time_to_sleep); } const char **get_global_errmsgs() diff --git a/mysys/my_write.c b/mysys/my_write.c index ef15e9a55b6..2e68a4dcff3 100644 --- a/mysys/my_write.c +++ b/mysys/my_write.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -24,6 +24,7 @@ size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags) { size_t writtenbytes, written; uint errors; + size_t ToWriteCount; DBUG_ENTER("my_write"); DBUG_PRINT("my",("fd: %d Buffer: %p Count: %lu MyFlags: %d", Filedes, Buffer, (ulong) Count, MyFlags)); @@ -37,11 +38,14 @@ size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags) { DBUG_SET("+d,simulate_file_write_error");}); for (;;) { + ToWriteCount= Count; + DBUG_EXECUTE_IF("simulate_io_thd_wait_for_disk_space", { ToWriteCount= 1; }); #ifdef _WIN32 - writtenbytes= my_win_write(Filedes, Buffer, Count); + writtenbytes= my_win_write(Filedes, Buffer, ToWriteCount); #else - writtenbytes= write(Filedes, Buffer, Count); + writtenbytes= write(Filedes, Buffer, ToWriteCount); #endif + DBUG_EXECUTE_IF("simulate_io_thd_wait_for_disk_space", { errno= ENOSPC; }); DBUG_EXECUTE_IF("simulate_file_write_error", { errno= ENOSPC; diff --git a/sql/log.cc b/sql/log.cc index 9e34532f1ac..eab9a118147 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -37,6 +37,7 @@ #include "log_event.h" // Query_log_event #include "rpl_filter.h" #include "rpl_rli.h" +#include "rpl_mi.h" #include "sql_audit.h" #include "sql_show.h" @@ -4377,13 +4378,22 @@ end: } -bool MYSQL_BIN_LOG::append(Log_event* ev) +#ifdef HAVE_REPLICATION +bool MYSQL_BIN_LOG::append(Log_event* ev, Master_info *mi) { bool error = 0; + mysql_mutex_assert_owner(&mi->data_lock); mysql_mutex_lock(&LOCK_log); DBUG_ENTER("MYSQL_BIN_LOG::append"); DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); + /* + Release data_lock by holding LOCK_log, while writing into the relay log. + If slave IO thread waits here for free space, we don't want + SHOW SLAVE STATUS to hang on mi->data_lock. Note LOCK_log mutex is + sufficient to block SQL thread when IO thread is updating relay log here. + */ + mysql_mutex_unlock(&mi->data_lock); /* Log_event::write() is smart enough to use my_b_write() or my_b_append() depending on the kind of cache we have. @@ -4398,24 +4408,50 @@ bool MYSQL_BIN_LOG::append(Log_event* ev) if (flush_and_sync(0)) goto err; if ((uint) my_b_append_tell(&log_file) > max_size) + { + /* + If rotation is required we must acquire data_lock to protect + description_event from clients executing FLUSH LOGS in parallel. + In order do that we must release the existing LOCK_log so that we + get it once again in proper locking order to avoid dead locks. + i.e data_lock , LOCK_log. + */ + mysql_mutex_unlock(&LOCK_log); + mysql_mutex_lock(&mi->data_lock); + mysql_mutex_lock(&LOCK_log); error= new_file_without_locking(); + /* + After rotation release data_lock, we need the LOCK_log till we signal + the updation. + */ + mysql_mutex_unlock(&mi->data_lock); + } err: - mysql_mutex_unlock(&LOCK_log); signal_update(); // Safe as we don't call close + mysql_mutex_unlock(&LOCK_log); + mysql_mutex_lock(&mi->data_lock); DBUG_RETURN(error); } -bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...) +bool MYSQL_BIN_LOG::appendv(Master_info* mi, const char* buf, uint len,...) { bool error= 0; DBUG_ENTER("MYSQL_BIN_LOG::appendv"); va_list(args); va_start(args,len); + mysql_mutex_assert_owner(&mi->data_lock); + mysql_mutex_lock(&LOCK_log); DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); - mysql_mutex_assert_owner(&LOCK_log); + /* + Release data_lock by holding LOCK_log, while writing into the relay log. + If slave IO thread waits here for free space, we don't want + SHOW SLAVE STATUS to hang on mi->data_lock. Note LOCK_log mutex is + sufficient to block SQL thread when IO thread is updating relay log here. + */ + mysql_mutex_unlock(&mi->data_lock); do { if (my_b_append(&log_file,(uchar*) buf,len)) @@ -4428,13 +4464,34 @@ bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...) DBUG_PRINT("info",("max_size: %lu",max_size)); if (flush_and_sync(0)) goto err; - if ((uint) my_b_append_tell(&log_file) > max_size) + if ((uint) my_b_append_tell(&log_file) > + DBUG_EVALUATE_IF("rotate_slave_debug_group", 500, max_size)) + { + /* + If rotation is required we must acquire data_lock to protect + description_event from clients executing FLUSH LOGS in parallel. + In order do that we must release the existing LOCK_log so that we + get it once again in proper locking order to avoid dead locks. + i.e data_lock , LOCK_log. + */ + mysql_mutex_unlock(&LOCK_log); + mysql_mutex_lock(&mi->data_lock); + mysql_mutex_lock(&LOCK_log); error= new_file_without_locking(); + /* + After rotation release data_lock, we need the LOCK_log till we signal + the updation. + */ + mysql_mutex_unlock(&mi->data_lock); + } err: if (!error) signal_update(); + mysql_mutex_unlock(&LOCK_log); + mysql_mutex_lock(&mi->data_lock); DBUG_RETURN(error); } +#endif bool MYSQL_BIN_LOG::flush_and_sync(bool *synced) { diff --git a/sql/log.h b/sql/log.h index 1fc13afe7d1..7d1c3161ac2 100644 --- a/sql/log.h +++ b/sql/log.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -20,6 +20,7 @@ #include "handler.h" /* my_xid */ class Relay_log_info; +class Master_info; class Format_description_log_event; @@ -454,8 +455,8 @@ public: v stands for vector invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0) */ - bool appendv(const char* buf,uint len,...); - bool append(Log_event* ev); + bool appendv(Master_info* mi, const char* buf,uint len,...); + bool append(Log_event* ev, Master_info* mi); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); diff --git a/sql/slave.cc b/sql/slave.cc index d97a9cdf6e9..31037c453d3 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -1660,7 +1660,7 @@ Waiting for the slave SQL thread to free enough relay log space"); #endif if (rli->sql_force_rotate_relay) { - rotate_relay_log(rli->mi); + rotate_relay_log(rli->mi, true/*need_data_lock=true*/); rli->sql_force_rotate_relay= false; } @@ -1705,7 +1705,7 @@ static void write_ignored_events_info_to_relay_log(THD *thd, Master_info *mi) if (likely((bool)ev)) { ev->server_id= 0; // don't be ignored by slave SQL thread - if (unlikely(rli->relay_log.append(ev))) + if (unlikely(rli->relay_log.append(ev, mi))) mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE), "failed to write a Rotate event" @@ -3605,7 +3605,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev) break; Execute_load_log_event xev(thd,0,0); xev.log_pos = cev->log_pos; - if (unlikely(mi->rli.relay_log.append(&xev))) + if (unlikely(mi->rli.relay_log.append(&xev, mi))) { mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE), @@ -3619,7 +3619,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev) { cev->block = net->read_pos; cev->block_len = num_bytes; - if (unlikely(mi->rli.relay_log.append(cev))) + if (unlikely(mi->rli.relay_log.append(cev, mi))) { mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE), @@ -3634,7 +3634,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev) aev.block = net->read_pos; aev.block_len = num_bytes; aev.log_pos = cev->log_pos; - if (unlikely(mi->rli.relay_log.append(&aev))) + if (unlikely(mi->rli.relay_log.append(&aev, mi))) { mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE), @@ -3713,7 +3713,7 @@ static int process_io_rotate(Master_info *mi, Rotate_log_event *rev) Rotate the relay log makes binlog format detection easier (at next slave start or mysqlbinlog) */ - DBUG_RETURN(rotate_relay_log(mi) /* will take the right mutexes */); + DBUG_RETURN(rotate_relay_log(mi, false/*need_data_lock=false*/)); } /* @@ -3819,7 +3819,7 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf, Log_event::Log_event(const char* buf...) in log_event.cc). */ ev->log_pos+= event_len; /* make log_pos be the pos of the end of the event */ - if (unlikely(rli->relay_log.append(ev))) + if (unlikely(rli->relay_log.append(ev, mi))) { delete ev; mysql_mutex_unlock(&mi->data_lock); @@ -3875,7 +3875,7 @@ static int queue_binlog_ver_3_event(Master_info *mi, const char *buf, inc_pos= event_len; break; } - if (unlikely(rli->relay_log.append(ev))) + if (unlikely(rli->relay_log.append(ev, mi))) { delete ev; mysql_mutex_unlock(&mi->data_lock); @@ -4083,7 +4083,6 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) direct master (an unsupported, useless setup!). */ - mysql_mutex_lock(log_lock); s_id= uint4korr(buf + SERVER_ID_OFFSET); if ((s_id == ::server_id && !mi->rli.replicate_same_server_id) || /* @@ -4116,6 +4115,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) IGNORE_SERVER_IDS it increments mi->master_log_pos as well as rli->group_relay_log_pos. */ + mysql_mutex_lock(log_lock); if (!(s_id == ::server_id && !mi->rli.replicate_same_server_id) || (buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT && buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT && @@ -4127,13 +4127,14 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) rli->ign_master_log_pos_end= mi->master_log_pos; } rli->relay_log.signal_update(); // the slave SQL thread needs to re-check + mysql_mutex_unlock(log_lock); DBUG_PRINT("info", ("master_log_pos: %lu, event originating from %u server, ignored", (ulong) mi->master_log_pos, uint4korr(buf + SERVER_ID_OFFSET))); } else { /* write the event to the relay log */ - if (likely(!(rli->relay_log.appendv(buf,event_len,0)))) + if (likely(!(rli->relay_log.appendv(mi, buf,event_len,0)))) { mi->master_log_pos+= inc_pos; DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); @@ -4143,9 +4144,10 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) { error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; } + mysql_mutex_lock(log_lock); rli->ign_master_log_name_end[0]= 0; // last event is not ignored + mysql_mutex_unlock(log_lock); } - mysql_mutex_unlock(log_lock); skip_relay_logging: @@ -5005,11 +5007,21 @@ err: locks; here we don't, so this function is mainly taking locks). Returns nothing as we cannot catch any error (MYSQL_BIN_LOG::new_file() is void). + + @param mi Master_info for the IO thread. + @param need_data_lock If true, mi->data_lock will be acquired otherwise, + mi->data_lock must be held by the caller. */ -int rotate_relay_log(Master_info* mi) +int rotate_relay_log(Master_info* mi, bool need_data_lock) { DBUG_ENTER("rotate_relay_log"); + if (need_data_lock) + mysql_mutex_lock(&mi->data_lock); + else + { + mysql_mutex_assert_owner(&mi->data_lock); + } Relay_log_info* rli= &mi->rli; int error= 0; @@ -5044,6 +5056,8 @@ int rotate_relay_log(Master_info* mi) */ rli->relay_log.harvest_bytes_written(&rli->log_space_total); end: + if (need_data_lock) + mysql_mutex_unlock(&mi->data_lock); DBUG_RETURN(error); } diff --git a/sql/slave.h b/sql/slave.h index 0374a5d27ae..0cf8adb0315 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -205,7 +205,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, const char** errmsg); void set_slave_thread_options(THD* thd); void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli); -int rotate_relay_log(Master_info* mi); +int rotate_relay_log(Master_info* mi, bool need_data_lock); int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli); pthread_handler_t handle_slave_io(void *arg); diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index 40e350c4ddd..f24f31b6399 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -157,7 +157,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options, { #ifdef HAVE_REPLICATION mysql_mutex_lock(&LOCK_active_mi); - if (rotate_relay_log(active_mi)) + if (rotate_relay_log(active_mi, true/*need_data_lock=true*/)) *write_to_binlog= -1; mysql_mutex_unlock(&LOCK_active_mi); #endif From 32d6db3bfa00e437fbd232b80b73b82a0d30c4ae Mon Sep 17 00:00:00 2001 From: Shishir Jaiswal Date: Tue, 1 Mar 2016 13:05:14 +0530 Subject: [PATCH 05/17] Bug#19920049 - MYSQLD_MULTI MISLEADING WHEN MY_PRINT_DEFAULTS IS NOT FOUND DESCRIPTION =========== If script mysqld_multi and utility my_print_defaults are in the same folder (not included in $PATH) and the former is made to run, it complaints that the mysqld binary is absent eventhough the binary exists. ANALYSIS ======== We've a subroutine my_which() mimicking the behaviour of POSIX "which" command. Its current behaviour is to check for a given argument as follows: - Step 1: Assume the argument to be a command having full fledged absolute path. If it exists "as-is", return the argument (which will be pathname), else proceed to Step 2. - Step 2: Assume the argument to be a plain command with no aboslute path. Try locating it in all of the paths (mentioned in $PATH) one by one. If found return the pathname. If found nowhere, return NULL. Currently when my_which(my_print_defaults) is called, it returns from Step 1 (since utlity exists in current folder) and doesn't proceed to Step 2. This is wrong since the returned value is same as the argument i.e. 'my_print_default' which defies the purpose of this subroutine whose job is to return a pathname either in Step 1 or Step 2. Later when the utility is executed in subroutine defaults_for_group(), it evaluates to NULL and returns the same. This is because the plain command 'my_print_defaults {options} ...' would execute properly only if my_print_defaults exists in one of the paths (in $PATH). In such a case, in the course of the flow it looks onto the variable $mysqld_found which comes out to be NULL and hence ethe error. In this case, call to my_which should fail resulting in script being aborted and thus avoiding this mess. FIX === This utility my_print_defaults should be tested only in Step 2 since it does not have an absolute path. Thus added a condition in Step 1 so that is gets executed iff not called for my_print_defaults thus bypassing it to proceed to Step 2 where the check is made for various paths (in $PATH) --- scripts/mysqld_multi.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/mysqld_multi.sh b/scripts/mysqld_multi.sh index bb218a9980b..d9ea6a117d2 100644 --- a/scripts/mysqld_multi.sh +++ b/scripts/mysqld_multi.sh @@ -1,6 +1,6 @@ #!/usr/bin/perl -# Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public @@ -596,7 +596,11 @@ sub my_which my ($command) = @_; my (@paths, $path); - return $command if (-f $command && -x $command); + # If the argument is not 'my_print_defaults' then it would be of the format + # / + return $command if ($command ne 'my_print_defaults' && -f $command && + -x $command); + @paths = split(':', $ENV{'PATH'}); foreach $path (@paths) { From 767bab4abe7666ee861984bc74ce87200ba23b5d Mon Sep 17 00:00:00 2001 From: Sreeharsha Ramanavarapu Date: Thu, 3 Mar 2016 06:42:12 +0530 Subject: [PATCH 06/17] Bug #18740222: CRASH IN GET_INTERVAL_INFO WITH WEIRDO INTERVALS ISSUE: ------ Some string functions return one or a combination of the parameters as their result. Here the resultant string's charset could be incorrectly set to that of the chosen parameter. This results in incorrect behavior when an ascii string is expected. SOLUTION: --------- Since an ascii string is expected, val_str_ascii should explicitly convert the string. Part of the fix is a backport of Bug#22340858 for mysql-5.5 and mysql-5.6. --- sql/item.cc | 17 +++++++++-------- sql/item_geofunc.cc | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index c482f0e1a04..f4917448dda 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -227,9 +227,6 @@ bool Item::val_bool() */ String *Item::val_str_ascii(String *str) { - if (!(collation.collation->state & MY_CS_NONASCII)) - return val_str(str); - DBUG_ASSERT(str != &str_value); uint errors; @@ -237,11 +234,15 @@ String *Item::val_str_ascii(String *str) if (!res) return 0; - if ((null_value= str->copy(res->ptr(), res->length(), - collation.collation, &my_charset_latin1, - &errors))) - return 0; - + if (!(res->charset()->state & MY_CS_NONASCII)) + str= res; + else + { + if ((null_value= str->copy(res->ptr(), res->length(), collation.collation, + &my_charset_latin1, &errors))) + return 0; + } + return str; } diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 621ddcb1a30..983491211c3 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -179,7 +179,7 @@ String *Item_func_geometry_type::val_str_ascii(String *str) /* String will not move */ str->copy(geom->get_class_info()->m_name.str, geom->get_class_info()->m_name.length, - default_charset()); + &my_charset_latin1); return str; } From 5102a7f278460027fc0ff8d47f2d3f4d72deeacd Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Mon, 7 Mar 2016 18:19:26 +0530 Subject: [PATCH 07/17] Bug#20685029: SLAVE IO THREAD SHOULD STOP WHEN DISK IS FULL Bug#21753696: MAKE SHOW SLAVE STATUS NON BLOCKING IF IO THREAD WAITS FOR DISK SPACE Fixing a post push test issue. --- mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test b/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test index d0c74b94cd1..9eed1089e6d 100644 --- a/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test +++ b/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test @@ -17,6 +17,9 @@ # ############################################################################### --source include/have_debug.inc +# Inorder to grep a specific error pattern in error log a fresh error log +# needs to be generated. +--source include/force_restart.inc --source include/master-slave.inc # Generate events to be replicated to the slave From 6608f84158d3561ef2c2f12a7136aa05cc39d7b4 Mon Sep 17 00:00:00 2001 From: Nisha Gopalakrishnan Date: Mon, 14 Mar 2016 15:20:21 +0530 Subject: [PATCH 08/17] BUG#22594514: HANDLE_FATAL_SIGNAL (SIG=11) IN UNIQUE::~UNIQUE | SQL/UNIQUES.CC:355 Analysis ======== Enabling the sort_buffer_size with a large value can cause operations utilizing the sort buffer like DELETE as mentioned in the bug report to fail. 5.5 and 5.6 versions reports OOM error while in 5.7+, the server crashes. While initializing the mem_root for the sort buffer tree, the block size for the mem_root is determined from the 'sort_buffer_size' value. This unsigned long value is typecasted to unsigned int, hence it becomes zero. Further block_size computation while initializing the mem_root results in a very large block_size value. Hence while trying to allocate a block during the DELETE operation, an OOM error is reported. In case of 5.7+, the PFS instrumentation for memory allocation, overshoots the unsigned value and allocates a block of just one byte. While trying to free the block of the mem_root, the original block_size is used. This triggers the crash since the server tries to free unallocated memory. Fix: ==== In order to restrict usage of such unreasonable sort_buffer_size, the typecast of block size to 'unsigned int' is removed and hence reports OOM error across all versions for sizes exceeding unsigned int range. --- include/my_tree.h | 4 ++-- mysys/tree.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/my_tree.h b/include/my_tree.h index 451e2b72b3c..4457bb1bc85 100644 --- a/include/my_tree.h +++ b/include/my_tree.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -62,7 +62,7 @@ typedef struct st_tree { } TREE; /* Functions on whole tree */ -void init_tree(TREE *tree, ulong default_alloc_size, ulong memory_limit, +void init_tree(TREE *tree, size_t default_alloc_size, ulong memory_limit, int size, qsort_cmp2 compare, my_bool with_delete, tree_element_free free_element, void *custom_arg); void delete_tree(TREE*); diff --git a/mysys/tree.c b/mysys/tree.c index c5bf3681a35..3f7360de40a 100644 --- a/mysys/tree.c +++ b/mysys/tree.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -83,7 +83,7 @@ static void rb_delete_fixup(TREE *tree,TREE_ELEMENT ***parent); static int test_rb_tree(TREE_ELEMENT *element); #endif -void init_tree(TREE *tree, ulong default_alloc_size, ulong memory_limit, +void init_tree(TREE *tree, size_t default_alloc_size, ulong memory_limit, int size, qsort_cmp2 compare, my_bool with_delete, tree_element_free free_element, void *custom_arg) { @@ -127,7 +127,7 @@ void init_tree(TREE *tree, ulong default_alloc_size, ulong memory_limit, } if (!(tree->with_delete=with_delete)) { - init_alloc_root(&tree->mem_root, (uint) default_alloc_size, 0); + init_alloc_root(&tree->mem_root, default_alloc_size, 0); tree->mem_root.min_malloc=(sizeof(TREE_ELEMENT)+tree->size_of_element); } DBUG_VOID_RETURN; From 9e5222ce714ef9597d6b386346aec4f4b5ff6047 Mon Sep 17 00:00:00 2001 From: "mysql-builder@oracle.com" <> Date: Thu, 17 Mar 2016 14:32:08 +0100 Subject: [PATCH 09/17] From 17d32a1d45c5ab38193281c0f5d5202cc2c0ac65 Mon Sep 17 00:00:00 2001 From: "mysql-builder@oracle.com" <> Date: Wed, 23 Mar 2016 12:59:22 +0530 Subject: [PATCH 10/17] From b4afc6a7a94a60980eac15dad385cd29ad1dd816 Mon Sep 17 00:00:00 2001 From: "Sreedhar.S" Date: Thu, 14 Apr 2016 14:18:23 +0530 Subject: [PATCH 11/17] Fix for Bugs#14583183 and 19949163 --- packaging/WiX/mysql_server.wxs.in | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packaging/WiX/mysql_server.wxs.in b/packaging/WiX/mysql_server.wxs.in index 1802baedf3b..10b6f892a2e 100644 --- a/packaging/WiX/mysql_server.wxs.in +++ b/packaging/WiX/mysql_server.wxs.in @@ -2,7 +2,7 @@ xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> - + + + + Installed + Date: Mon, 11 Apr 2016 11:41:47 +0530 Subject: [PATCH 12/17] Bug#22897202: RPL_IO_THD_WAIT_FOR_DISK_SPACE HAS OCCASIONAL FAILURES Analysis: ========= Test script is not ensuring that "assert_grep.inc" should be called only after 'Disk is full' error is written to the error log. Test checks for "Queueing master event to the relay log" state. But this state is set before invoking 'queue_event'. Actual 'Disk is full' error happens at a very lower level. It can happen that we might even reset the debug point before even the actual disk full simulation occurs and the "Disk is full" message will never appear in the error log. In order to guarentee that we must have some mechanism where in after we write "Disk is full" error messge into the error log we must signal the test to execute SSS and then reset the debug point. So that test is deterministic. Fix: === Added debug sync point to make script deterministic. --- .../suite/rpl/r/rpl_io_thd_wait_for_disk_space.result | 2 +- .../suite/rpl/t/rpl_io_thd_wait_for_disk_space.test | 11 ++++------- mysys/errors.c | 3 ++- sql/log.cc | 8 ++++++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result b/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result index c601c9a4169..b11ad4f53bd 100644 --- a/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result +++ b/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result @@ -7,7 +7,7 @@ CALL mtr.add_suppression("Retry in 60 secs"); include/stop_slave_sql.inc SET @@GLOBAL.DEBUG= 'd,simulate_io_thd_wait_for_disk_space'; INSERT INTO t1 VALUES(2); -include/wait_for_slave_param.inc [Slave_IO_State] +SET DEBUG_SYNC='now WAIT_FOR parked'; SET @@GLOBAL.DEBUG= '$debug_saved'; include/assert_grep.inc [Found the disk full error message on the slave] include/start_slave_sql.inc diff --git a/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test b/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test index 9eed1089e6d..6076be60ebc 100644 --- a/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test +++ b/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test @@ -42,16 +42,13 @@ SET @@GLOBAL.DEBUG= 'd,simulate_io_thd_wait_for_disk_space'; --connection master INSERT INTO t1 VALUES(2); ---connection slave -# Wait until IO thread is queuing events from master -# Notice that this is performed by querying SHOW SLAVE STATUS ---let $slave_param= Slave_IO_State ---let $slave_param_value= Queueing master event to the relay log ---source include/wait_for_slave_param.inc +--connection slave1 +SET DEBUG_SYNC='now WAIT_FOR parked'; -# Get the relay log file name, also using SHOW SLAVE STATUS +# Get the relay log file name using SHOW SLAVE STATUS --let $relay_log_file= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) +--connection slave # Restore the debug options to "simulate" freed space on disk SET @@GLOBAL.DEBUG= '$debug_saved'; diff --git a/mysys/errors.c b/mysys/errors.c index 942fd3230c0..b6064460535 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -15,7 +15,7 @@ #include "mysys_priv.h" #include "mysys_err.h" - +#include "m_string.h" #ifndef SHARED_LIBRARY const char *globerrs[GLOBERRS]= @@ -128,6 +128,7 @@ void wait_for_free_space(const char *filename, int errors) }); (void) sleep(time_to_sleep); + DEBUG_SYNC_C("disk_full_reached"); } const char **get_global_errmsgs() diff --git a/sql/log.cc b/sql/log.cc index eab9a118147..a7f05905514 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -4452,6 +4452,14 @@ bool MYSQL_BIN_LOG::appendv(Master_info* mi, const char* buf, uint len,...) sufficient to block SQL thread when IO thread is updating relay log here. */ mysql_mutex_unlock(&mi->data_lock); + DBUG_EXECUTE_IF("simulate_io_thd_wait_for_disk_space", + { + const char act[]= "disk_full_reached SIGNAL parked"; + DBUG_ASSERT(opt_debug_sync_timeout > 0); + DBUG_ASSERT(!debug_sync_set_action(current_thd, + STRING_WITH_LEN(act))); + };); + do { if (my_b_append(&log_file,(uchar*) buf,len)) From fbf44eed3c69dc15047ac2d40c09dd0d16993fb0 Mon Sep 17 00:00:00 2001 From: Karthik Kamath Date: Tue, 19 Apr 2016 14:49:27 +0530 Subject: [PATCH 13/17] BUG#22286421: NULL POINTER DEREFERENCE ANALYSIS: ========= A LEX_STRING structure pointer is processed during the validation of a stored program name. During this processing, there is a possibility of null pointer dereference. FIX: ==== check_routine_name() is invoked by the parser by supplying a non-empty string as the SP name. To avoid any potential calls to check_routine_name() with NULL value, a debug assert has been added to catch such cases. --- sql/sp_head.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 13d1b310599..992e7415f45 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -490,8 +490,9 @@ sp_name::init_qname(THD *thd) bool check_routine_name(LEX_STRING *ident) { - if (!ident || !ident->str || !ident->str[0] || - ident->str[ident->length-1] == ' ') + DBUG_ASSERT(ident != NULL && ident->str != NULL); + + if (!ident->str[0] || ident->str[ident->length-1] == ' ') { my_error(ER_SP_WRONG_NAME, MYF(0), ident->str); return TRUE; From 3b6f9aac02b126db57fa3e3f1873713438d0a950 Mon Sep 17 00:00:00 2001 From: Nisha Gopalakrishnan Date: Fri, 22 Apr 2016 10:25:16 +0530 Subject: [PATCH 14/17] BUG#23135731: INSERT WITH DUPLICATE KEY UPDATE REPORTS INCORRECT ERROR. Analysis ======== INSERT with DUPLICATE KEY UPDATE and REPLACE on a table where foreign key constraint is defined fails with an incorrect 'duplicate entry' error rather than foreign key constraint violation error. As part of the bug fix for BUG#22037930, a new flag 'HA_CHECK_FK_ERROR' was added while checking for non fatal errors to manage FK errors based on the 'IGNORE' flag. For INSERT with DUPLICATE KEY UPDATE and REPLACE queries, the foreign key constraint violation error was marked as non-fatal, even though IGNORE was not set. Hence it continued with the duplicate key processing resulting in an incorrect error. Fix: === Foreign key violation errors are treated as non fatal only when the IGNORE is not set in the above mentioned queries. Hence reports the appropriate foreign key violation error. --- mysql-test/r/insert.result | 15 +++++++++++++++ mysql-test/t/insert.test | 22 ++++++++++++++++++++++ sql/sql_insert.cc | 23 ++++++++++++++++------- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result index 1aa22349593..e840b95f1ba 100644 --- a/mysql-test/r/insert.result +++ b/mysql-test/r/insert.result @@ -724,3 +724,18 @@ ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (` UPDATE t1, t2 SET t1.fld1= t1.fld1 + 3; ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`)) DROP TABLE t2, t1; +# +# BUG#22037930: INSERT IGNORE FAILS TO IGNORE FOREIGN +# KEY CONSTRAINT +CREATE TABLE t1 (fld1 INT PRIMARY KEY) ENGINE= INNODB; +CREATE TABLE t2 (fld1 VARCHAR(10), fld2 INT NOT NULL, +CONSTRAINT fk FOREIGN KEY (fld2) REFERENCES t1(fld1)) ENGINE= INNODB; +# Without patch, reports incorrect error. +INSERT INTO t2 VALUES('abc', 2) ON DUPLICATE KEY UPDATE fld1= 'def'; +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`)) +REPLACE INTO t2 VALUES('abc', 2); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`)) +INSERT IGNORE INTO t2 VALUES('abc', 2) ON DUPLICATE KEY UPDATE fld1= 'def'; +Warnings: +Warning 1452 `test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`) +DROP TABLE t2, t1; diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test index ea89872200c..94cf5d3b475 100644 --- a/mysql-test/t/insert.test +++ b/mysql-test/t/insert.test @@ -591,3 +591,25 @@ UPDATE t1, t2 SET t2.fld2= t2.fld2 + 3; UPDATE t1, t2 SET t1.fld1= t1.fld1 + 3; DROP TABLE t2, t1; + + +--echo # +--echo # BUG#22037930: INSERT IGNORE FAILS TO IGNORE FOREIGN +--echo # KEY CONSTRAINT + +CREATE TABLE t1 (fld1 INT PRIMARY KEY) ENGINE= INNODB; + +CREATE TABLE t2 (fld1 VARCHAR(10), fld2 INT NOT NULL, +CONSTRAINT fk FOREIGN KEY (fld2) REFERENCES t1(fld1)) ENGINE= INNODB; + +--echo # Without patch, reports incorrect error. +--error ER_NO_REFERENCED_ROW_2 +INSERT INTO t2 VALUES('abc', 2) ON DUPLICATE KEY UPDATE fld1= 'def'; +--error ER_NO_REFERENCED_ROW_2 +REPLACE INTO t2 VALUES('abc', 2); + +--enable_warnings +INSERT IGNORE INTO t2 VALUES('abc', 2) ON DUPLICATE KEY UPDATE fld1= 'def'; +--disable_warnings + +DROP TABLE t2, t1; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index a267108c847..dc7cb698476 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1521,16 +1521,25 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) insert_id_for_cur_row= table->file->insert_id_for_cur_row; else table->file->insert_id_for_cur_row= insert_id_for_cur_row; - bool is_duplicate_key_error; - if (table->file->is_fatal_error(error, HA_CHECK_DUP | HA_CHECK_FK_ERROR)) + + /* + If it is a FK constraint violation and 'ignore' flag is set, + report a warning instead of error. + */ + if (info->ignore && !table->file->is_fatal_error(error, + HA_CHECK_FK_ERROR)) + goto ok_or_after_trg_err; + + if (table->file->is_fatal_error(error, HA_CHECK_DUP)) goto err; - is_duplicate_key_error= table->file->is_fatal_error(error, 0); - if (!is_duplicate_key_error) + + if (!table->file->is_fatal_error(error, 0)) { /* - We come here when we had an ignorable error which is not a duplicate - key error. In this we ignore error if ignore flag is set, otherwise - report error as usual. We will not do any duplicate key processing. + We come here when we have an ignorable error which is not a duplicate + key error or FK error(Ex: Partition related errors). In this case we + ignore the error if ignore flag is set, otherwise report error as usual. + We will not do any duplicate key processing. */ if (info->ignore) goto ok_or_after_trg_err; /* Ignoring a not fatal error, return 0 */ From 6768f80c0b1f0b82c5addbe47ce712ded14d0d8c Mon Sep 17 00:00:00 2001 From: Harin Vadodaria Date: Fri, 29 Apr 2016 11:06:41 +0530 Subject: [PATCH 15/17] Bug#21973610 Post push fix : Fixing i_main.mysqlshow failure. --- client/mysqlshow.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/mysqlshow.c b/client/mysqlshow.c index 4d1df00c8fd..f1f8bdaf54b 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -655,7 +655,7 @@ list_table_status(MYSQL *mysql,const char *db,const char *wild) len= sizeof(query); len-= my_snprintf(query, len, "show table status from `%s`", db); if (wild && wild[0] && len) - strxnmov(query + strlen(query), len, " like '", wild, "'", NullS); + strxnmov(query + strlen(query), len - 1, " like '", wild, "'", NullS); if (mysql_query(mysql,query) || !(result=mysql_store_result(mysql))) { fprintf(stderr,"%s: Cannot get status for db: %s, table: %s: %s\n", @@ -688,7 +688,7 @@ list_fields(MYSQL *mysql,const char *db,const char *table, const char *wild) { char query[NAME_LEN + 100]; - int len; + size_t len; MYSQL_RES *result; MYSQL_ROW row; ulong UNINIT_VAR(rows); @@ -718,7 +718,7 @@ list_fields(MYSQL *mysql,const char *db,const char *table, len-= my_snprintf(query, len, "show /*!32332 FULL */ columns from `%s`", table); if (wild && wild[0] && len) - strxnmov(query + strlen(query), len, " like '", wild, "'", NullS); + strxnmov(query + strlen(query), len - 1, " like '", wild, "'", NullS); if (mysql_query(mysql,query) || !(result=mysql_store_result(mysql))) { fprintf(stderr,"%s: Cannot list columns in db: %s, table: %s: %s\n", From 9d72fb4af0d87f6a69a3ccb9202b4029acf2bd56 Mon Sep 17 00:00:00 2001 From: Shishir Jaiswal Date: Mon, 16 May 2016 13:46:49 +0530 Subject: [PATCH 16/17] Bug#21977380 - POSSIBLE BUFFER OVERFLOW ISSUES DESCRIPTION =========== Buffer overflow is reported in a lot of code sections spanning across server, client programs, Regex libraries etc. If not handled appropriately, they can cause abnormal behaviour. ANALYSIS ======== The reported casea are the ones which are likely to result in SEGFAULT, MEMORY LEAK etc. FIX === - sprintf() has been replaced by my_snprintf() to avoid buffer overflow. - my_free() is done after checking if the pointer isn't NULL already and setting it to NULL thereafter at few places. - Buffer is ensured to be large enough to hold the data. - 'unsigned int' (aka 'uint') is replaced with 'size_t' to avoid wraparound. - Memory is freed (if not done so) after its alloced and used. - Inserted assert() for size check in InnoDb memcached code (from 5.6 onwards) - Other minor changes (cherry picked from commit 3487e20959c940cbd24429afa795ebfc8a01e94f) --- client/mysqlcheck.c | 42 +++++++++++++++++++++-------------- client/mysqldump.c | 49 +++++++++++++++++++++++------------------ client/mysqlshow.c | 36 ++++++++++++++++-------------- extra/yassl/src/log.cpp | 4 ++-- regex/split.c | 4 ++++ 5 files changed, 78 insertions(+), 57 deletions(-) diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index a564e871281..55b941e7f1a 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -213,13 +213,13 @@ static int process_selected_tables(char *db, char **table_names, int tables); static int process_all_tables_in_db(char *database); static int process_one_db(char *database); static int use_db(char *database); -static int handle_request_for_tables(char *tables, uint length); +static int handle_request_for_tables(char *tables, size_t length); static int dbConnect(char *host, char *user,char *passwd); static void dbDisconnect(char *host); static void DBerror(MYSQL *mysql, const char *when); static void safe_exit(int error); static void print_result(); -static uint fixed_name_length(const char *name); +static size_t fixed_name_length(const char *name); static char *fix_table_name(char *dest, char *src); int what_to_do = 0; @@ -486,7 +486,7 @@ static int process_selected_tables(char *db, char **table_names, int tables) *end++= ','; } *--end = 0; - handle_request_for_tables(table_names_comma_sep + 1, (uint) (tot_length - 1)); + handle_request_for_tables(table_names_comma_sep + 1, tot_length - 1); my_free(table_names_comma_sep); } else @@ -496,10 +496,10 @@ static int process_selected_tables(char *db, char **table_names, int tables) } /* process_selected_tables */ -static uint fixed_name_length(const char *name) +static size_t fixed_name_length(const char *name) { const char *p; - uint extra_length= 2; /* count the first/last backticks */ + size_t extra_length= 2; /* count the first/last backticks */ for (p= name; *p; p++) { @@ -508,7 +508,7 @@ static uint fixed_name_length(const char *name) else if (*p == '.') extra_length+= 2; } - return (uint) ((p - name) + extra_length); + return (size_t) ((p - name) + extra_length); } @@ -564,7 +564,7 @@ static int process_all_tables_in_db(char *database) */ char *tables, *end; - uint tot_length = 0; + size_t tot_length = 0; while ((row = mysql_fetch_row(res))) tot_length+= fixed_name_length(row[0]) + 2; @@ -622,7 +622,9 @@ static int fix_table_storage_name(const char *name) int rc= 0; if (strncmp(name, "#mysql50#", 9)) return 1; - sprintf(qbuf, "RENAME TABLE `%s` TO `%s`", name, name + 9); + my_snprintf(qbuf, sizeof(qbuf), "RENAME TABLE `%s` TO `%s`", + name, name + 9); + rc= run_query(qbuf); if (verbose) printf("%-50s %s\n", name, rc ? "FAILED" : "OK"); @@ -635,7 +637,8 @@ static int fix_database_storage_name(const char *name) int rc= 0; if (strncmp(name, "#mysql50#", 9)) return 1; - sprintf(qbuf, "ALTER DATABASE `%s` UPGRADE DATA DIRECTORY NAME", name); + my_snprintf(qbuf, sizeof(qbuf), "ALTER DATABASE `%s` UPGRADE DATA DIRECTORY " + "NAME", name); rc= run_query(qbuf); if (verbose) printf("%-50s %s\n", name, rc ? "FAILED" : "OK"); @@ -653,7 +656,7 @@ static int rebuild_table(char *name) ptr= strmov(query, "ALTER TABLE "); ptr= fix_table_name(ptr, name); ptr= strxmov(ptr, " FORCE", NullS); - if (mysql_real_query(sock, query, (uint)(ptr - query))) + if (mysql_real_query(sock, query, (ulong)(ptr - query))) { fprintf(stderr, "Failed to %s\n", query); fprintf(stderr, "Error: %s\n", mysql_error(sock)); @@ -702,10 +705,10 @@ static int disable_binlog() return run_query(stmt); } -static int handle_request_for_tables(char *tables, uint length) +static int handle_request_for_tables(char *tables, size_t length) { char *query, *end, options[100], message[100]; - uint query_length= 0; + size_t query_length= 0, query_size= sizeof(char)*(length+110); const char *op = 0; options[0] = 0; @@ -736,10 +739,14 @@ static int handle_request_for_tables(char *tables, uint length) return fix_table_storage_name(tables); } - if (!(query =(char *) my_malloc((sizeof(char)*(length+110)), MYF(MY_WME)))) + if (!(query =(char *) my_malloc(query_size, MYF(MY_WME)))) + { return 1; + } if (opt_all_in_1) { + DBUG_ASSERT(strlen(op)+strlen(tables)+strlen(options)+8+1 <= query_size); + /* No backticks here as we added them before */ query_length= sprintf(query, "%s TABLE %s %s", op, tables, options); } @@ -750,7 +757,7 @@ static int handle_request_for_tables(char *tables, uint length) ptr= strmov(strmov(query, op), " TABLE "); ptr= fix_table_name(ptr, tables); ptr= strxmov(ptr, " ", options, NullS); - query_length= (uint) (ptr - query); + query_length= (size_t) (ptr - query); } if (mysql_real_query(sock, query, query_length)) { @@ -834,7 +841,10 @@ static void print_result() prev_alter[0]= 0; } else - strcpy(prev_alter, alter_txt); + { + strncpy(prev_alter, alter_txt, MAX_ALTER_STR_SIZE-1); + prev_alter[MAX_ALTER_STR_SIZE-1]= 0; + } } } } @@ -978,7 +988,7 @@ int main(int argc, char **argv) process_databases(argv); if (opt_auto_repair) { - uint i; + size_t i; if (!opt_silent && (tables4repair.elements || tables4rebuild.elements)) puts("\nRepairing tables"); diff --git a/client/mysqldump.c b/client/mysqldump.c index 6c4fec313c5..00265def489 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -86,7 +86,7 @@ static void add_load_option(DYNAMIC_STRING *str, const char *option, const char *option_value); -static ulong find_set(TYPELIB *lib, const char *x, uint length, +static ulong find_set(TYPELIB *lib, const char *x, size_t length, char **err_pos, uint *err_len); static char *alloc_query_str(ulong size); @@ -852,7 +852,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), opt_set_charset= 0; opt_compatible_mode_str= argument; opt_compatible_mode= find_set(&compatible_mode_typelib, - argument, (uint) strlen(argument), + argument, strlen(argument), &err_ptr, &err_len); if (err_len) { @@ -862,7 +862,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } #if !defined(DBUG_OFF) { - uint size_for_sql_mode= 0; + size_t size_for_sql_mode= 0; const char **ptr; for (ptr= compatible_mode_names; *ptr; ptr++) size_for_sql_mode+= strlen(*ptr); @@ -1138,8 +1138,8 @@ static int fetch_db_collation(const char *db_name, break; } - strncpy(db_cl_name, db_cl_row[0], db_cl_size); - db_cl_name[db_cl_size - 1]= 0; /* just in case. */ + strncpy(db_cl_name, db_cl_row[0], db_cl_size-1); + db_cl_name[db_cl_size - 1]= 0; } while (FALSE); @@ -1150,7 +1150,7 @@ static int fetch_db_collation(const char *db_name, static char *my_case_str(const char *str, - uint str_len, + size_t str_len, const char *token, uint token_len) { @@ -1366,7 +1366,7 @@ static int switch_character_set_results(MYSQL *mysql, const char *cs_name) */ static char *cover_definer_clause(const char *stmt_str, - uint stmt_length, + size_t stmt_length, const char *definer_version_str, uint definer_version_length, const char *stmt_version_str, @@ -1548,14 +1548,14 @@ static void dbDisconnect(char *host) } /* dbDisconnect */ -static void unescape(FILE *file,char *pos,uint length) +static void unescape(FILE *file,char *pos, size_t length) { char *tmp; DBUG_ENTER("unescape"); if (!(tmp=(char*) my_malloc(length*2+1, MYF(MY_WME)))) die(EX_MYSQLERR, "Couldn't allocate memory"); - mysql_real_escape_string(&mysql_connection, tmp, pos, length); + mysql_real_escape_string(&mysql_connection, tmp, pos, (ulong)length); fputc('\'', file); fputs(tmp, file); fputc('\'', file); @@ -1669,7 +1669,7 @@ static char *quote_for_like(const char *name, char *buff) Quote '<' '>' '&' '\"' chars and print a string to the xml_file. */ -static void print_quoted_xml(FILE *xml_file, const char *str, ulong len, +static void print_quoted_xml(FILE *xml_file, const char *str, size_t len, my_bool is_attribute_name) { const char *end; @@ -1928,7 +1928,7 @@ static void print_xml_row(FILE *xml_file, const char *row_name, squeezed to a single hyphen. */ -static void print_xml_comment(FILE *xml_file, ulong len, +static void print_xml_comment(FILE *xml_file, size_t len, const char *comment_string) { const char* end; @@ -2045,7 +2045,7 @@ static uint dump_events_for_db(char *db) DBUG_ENTER("dump_events_for_db"); DBUG_PRINT("enter", ("db: '%s'", db)); - mysql_real_escape_string(mysql, db_name_buff, db, strlen(db)); + mysql_real_escape_string(mysql, db_name_buff, db, (ulong)strlen(db)); /* nice comments */ print_comment(sql_file, 0, @@ -2164,6 +2164,11 @@ static uint dump_events_for_db(char *db) (const char *) (query_str != NULL ? query_str : row[3]), (const char *) delimiter); + if(query_str) + { + my_free(query_str); + query_str= NULL; + } restore_time_zone(sql_file, delimiter); restore_sql_mode(sql_file, delimiter); @@ -2257,7 +2262,7 @@ static uint dump_routines_for_db(char *db) DBUG_ENTER("dump_routines_for_db"); DBUG_PRINT("enter", ("db: '%s'", db)); - mysql_real_escape_string(mysql, db_name_buff, db, strlen(db)); + mysql_real_escape_string(mysql, db_name_buff, db, (ulong)strlen(db)); /* nice comments */ print_comment(sql_file, 0, @@ -2311,9 +2316,9 @@ static uint dump_routines_for_db(char *db) if the user has EXECUTE privilege he see routine names, but NOT the routine body of other routines that are not the creator of! */ - DBUG_PRINT("info",("length of body for %s row[2] '%s' is %d", + DBUG_PRINT("info",("length of body for %s row[2] '%s' is %zu", routine_name, row[2] ? row[2] : "(null)", - row[2] ? (int) strlen(row[2]) : 0)); + row[2] ? strlen(row[2]) : 0)); if (row[2] == NULL) { print_comment(sql_file, 1, "\n-- insufficient privileges to %s\n", @@ -3873,7 +3878,7 @@ static int dump_tablespaces_for_tables(char *db, char **table_names, int tables) int i; char name_buff[NAME_LEN*2+3]; - mysql_real_escape_string(mysql, name_buff, db, strlen(db)); + mysql_real_escape_string(mysql, name_buff, db, (ulong)strlen(db)); init_dynamic_string_checked(&where, " AND TABLESPACE_NAME IN (" "SELECT DISTINCT TABLESPACE_NAME FROM" @@ -3886,7 +3891,7 @@ static int dump_tablespaces_for_tables(char *db, char **table_names, int tables) for (i=0 ; imax_length) length=field->max_length; @@ -500,7 +501,8 @@ static int list_tables(MYSQL *mysql,const char *db,const char *table) { const char *header; - uint head_length, counter = 0; + size_t head_length; + uint counter = 0; char query[NAME_LEN + 100], rows[NAME_LEN], fields[16]; MYSQL_FIELD *field; MYSQL_RES *result; @@ -537,7 +539,7 @@ list_tables(MYSQL *mysql,const char *db,const char *table) putchar('\n'); header="Tables"; - head_length=(uint) strlen(header); + head_length= strlen(header); field=mysql_fetch_field(result); if (head_length < field->max_length) head_length=field->max_length; @@ -766,10 +768,10 @@ list_fields(MYSQL *mysql,const char *db,const char *table, *****************************************************************************/ static void -print_header(const char *header,uint head_length,...) +print_header(const char *header,size_t head_length,...) { va_list args; - uint length,i,str_length,pre_space; + size_t length,i,str_length,pre_space; const char *field; va_start(args,head_length); @@ -792,10 +794,10 @@ print_header(const char *header,uint head_length,...) putchar('|'); for (;;) { - str_length=(uint) strlen(field); + str_length= strlen(field); if (str_length > length) str_length=length+1; - pre_space=(uint) (((int) length-(int) str_length)/2)+1; + pre_space= ((length- str_length)/2)+1; for (i=0 ; i < pre_space ; i++) putchar(' '); for (i = 0 ; i < str_length ; i++) @@ -829,11 +831,11 @@ print_header(const char *header,uint head_length,...) static void -print_row(const char *header,uint head_length,...) +print_row(const char *header,size_t head_length,...) { va_list args; const char *field; - uint i,length,field_length; + size_t i,length,field_length; va_start(args,head_length); field=header; length=head_length; @@ -842,7 +844,7 @@ print_row(const char *header,uint head_length,...) putchar('|'); putchar(' '); fputs(field,stdout); - field_length=(uint) strlen(field); + field_length= strlen(field); for (i=field_length ; i <= length ; i++) putchar(' '); if (!(field=va_arg(args,char *))) @@ -856,10 +858,10 @@ print_row(const char *header,uint head_length,...) static void -print_trailer(uint head_length,...) +print_trailer(size_t head_length,...) { va_list args; - uint length,i; + size_t length,i; va_start(args,head_length); length=head_length; @@ -902,7 +904,7 @@ static void print_res_top(MYSQL_RES *result) mysql_field_seek(result,0); while((field = mysql_fetch_field(result))) { - if ((length=(uint) strlen(field->name)) > field->max_length) + if ((length= strlen(field->name)) > field->max_length) field->max_length=length; else length=field->max_length; diff --git a/extra/yassl/src/log.cpp b/extra/yassl/src/log.cpp index 13c68295747..2f112ac35f9 100644 --- a/extra/yassl/src/log.cpp +++ b/extra/yassl/src/log.cpp @@ -1,6 +1,5 @@ /* - Copyright (C) 2000-2007 MySQL AB - Use is subject to license terms + Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 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 @@ -61,6 +60,7 @@ namespace yaSSL { time_t clicks = time(0); char timeStr[32]; + memset(timeStr, 0, sizeof(timeStr)); // get rid of newline strncpy(timeStr, ctime(&clicks), sizeof(timeStr)); unsigned int len = strlen(timeStr); diff --git a/regex/split.c b/regex/split.c index bd2a53c01e3..a3a11f793ed 100644 --- a/regex/split.c +++ b/regex/split.c @@ -163,6 +163,10 @@ char *argv[]; } else if (argc > 3) for (n = atoi(argv[3]); n > 0; n--) { + if(sizeof(buf)-1 < strlen(argv[1])) + { + exit(EXIT_FAILURE); + } (void) strcpy(buf, argv[1]); (void) split(buf, fields, MNF, argv[2]); } From ef3f09f0c9e62ea1bf86b33b5d97e954b3ae34fe Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Fri, 13 May 2016 16:42:45 +0530 Subject: [PATCH 17/17] Bug#23251517: SEMISYNC REPLICATION HANGING Revert following bug fix: Bug#20685029: SLAVE IO THREAD SHOULD STOP WHEN DISK IS FULL Bug#21753696: MAKE SHOW SLAVE STATUS NON BLOCKING IF IO THREAD WAITS FOR DISK SPACE This fix results in a deadlock between slave IO thread and SQL thread. (cherry picked from commit e3fea6c6dbb36c6ab21c4ab777224560e9608b53) --- mysql-test/include/assert_grep.inc | 154 ------------------ mysql-test/include/rpl_init.inc | 31 +--- mysql-test/include/rpl_reconnect.inc | 33 +--- mysql-test/include/start_slave_sql.inc | 39 ----- .../r/rpl_io_thd_wait_for_disk_space.result | 15 -- .../rpl/t/rpl_io_thd_wait_for_disk_space.test | 71 -------- mysys/errors.c | 14 +- mysys/my_write.c | 8 +- sql/log.cc | 75 +-------- sql/log.h | 5 +- sql/slave.cc | 38 ++--- sql/slave.h | 2 +- sql/sql_reload.cc | 2 +- 13 files changed, 41 insertions(+), 446 deletions(-) delete mode 100644 mysql-test/include/assert_grep.inc delete mode 100644 mysql-test/include/start_slave_sql.inc delete mode 100644 mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result delete mode 100644 mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test diff --git a/mysql-test/include/assert_grep.inc b/mysql-test/include/assert_grep.inc deleted file mode 100644 index a980a6d73b1..00000000000 --- a/mysql-test/include/assert_grep.inc +++ /dev/null @@ -1,154 +0,0 @@ -# ==== Purpose ==== -# -# Grep a file for a pattern, produce a single string out of the -# matching lines, and assert that the string matches a given regular -# expression. -# -# ==== Usage ==== -# -# --let $assert_text= TEXT -# --let $assert_file= FILE -# --let $assert_select= REGEX -# [--let $assert_match= REGEX | --let $assert_count= NUMBER] -# [--let $assert_only_after= REGEX] -# --source include/assert_grep.inc -# -# Parameters: -# -# $assert_text -# Text that describes what is being checked. This text is written to -# the query log so it should not contain non-deterministic elements. -# -# $assert_file -# File to search. -# -# $assert_select -# All lines matching this text will be checked. -# -# $assert_match -# The script will find all lines that match $assert_select, -# concatenate them to a long string, and assert that it matches -# $assert_match. -# -# $assert_count -# Instead of asserting that the selected lines match -# $assert_match, assert that there were exactly $assert_count -# matching lines. -# -# $assert_only_after -# Reset all the lines matched and the counter when finding this pattern. -# It is useful for searching things in the mysqld.err log file just -# after the last server restart for example (discarding the log content -# of previous server executions). - - -if (!$assert_text) -{ - --die !!!ERROR IN TEST: you must set $assert_text -} -if (!$assert_file) -{ - --die !!!ERROR IN TEST: you must set $assert_file -} -if (!$assert_select) -{ - --die !!!ERROR IN TEST: you must set $assert_select -} -if ($assert_match == '') -{ - if ($assert_count == '') - { - --die !!!ERROR IN TEST: you must set either $assert_match or $assert_count - } -} -if ($assert_match != '') -{ - if ($assert_count != '') - { - --echo assert_text='$assert_text' assert_count='$assert_count' - --die !!!ERROR IN TEST: you must set only one of $assert_match or $assert_count - } -} - - ---let $include_filename= assert_grep.inc [$assert_text] ---source include/begin_include_file.inc - - ---let _AG_ASSERT_TEXT= $assert_text ---let _AG_ASSERT_FILE= $assert_file ---let _AG_ASSERT_SELECT= $assert_select ---let _AG_ASSERT_MATCH= $assert_match ---let _AG_ASSERT_COUNT= $assert_count ---let _AG_OUT= `SELECT CONCAT('$MYSQLTEST_VARDIR/tmp/_ag_', UUID())` ---let _AG_ASSERT_ONLY_AFTER= $assert_only_after - - ---perl - use strict; - use warnings; - my $file= $ENV{'_AG_ASSERT_FILE'}; - my $assert_select= $ENV{'_AG_ASSERT_SELECT'}; - my $assert_match= $ENV{'_AG_ASSERT_MATCH'}; - my $assert_count= $ENV{'_AG_ASSERT_COUNT'}; - my $assert_only_after= $ENV{'_AG_ASSERT_ONLY_AFTER'}; - my $out= $ENV{'_AG_OUT'}; - - my $result= ''; - my $count= 0; - open(FILE, "$file") or die("Error $? opening $file: $!\n"); - while () { - my $line = $_; - if ($assert_only_after && $line =~ /$assert_only_after/) { - $result = ""; - $count = 0; - } - if ($line =~ /$assert_select/) { - if ($assert_count ne '') { - $count++; - } - else { - $result .= $line; - } - } - } - close(FILE) or die("Error $? closing $file: $!"); - open OUT, "> $out" or die("Error $? opening $out: $!"); - if ($assert_count ne '' && ($count != $assert_count)) { - print OUT ($count) or die("Error $? writing $out: $!"); - } - elsif ($assert_count eq '' && $result !~ /$assert_match/) { - print OUT ($result) or die("Error $? writing $out: $!"); - } - else { - print OUT ("assert_grep.inc ok"); - } - close OUT or die("Error $? closing $out: $!"); -EOF - - ---let $_ag_outcome= `SELECT LOAD_FILE('$_AG_OUT')` -if ($_ag_outcome != 'assert_grep.inc ok') -{ - --source include/show_rpl_debug_info.inc - --echo include/assert_grep.inc failed! - --echo assert_text: '$assert_text' - --echo assert_file: '$assert_file' - --echo assert_select: '$assert_select' - --echo assert_match: '$assert_match' - --echo assert_count: '$assert_count' - --echo assert_only_after: '$assert_only_after' - if ($assert_match != '') - { - --echo matching lines: '$_ag_outcome' - } - if ($assert_count != '') - { - --echo number of matching lines: $_ag_outcome - } - --die assert_grep.inc failed. -} - - ---let $include_filename= include/assert_grep.inc [$assert_text] ---source include/end_include_file.inc diff --git a/mysql-test/include/rpl_init.inc b/mysql-test/include/rpl_init.inc index 820bc8e9016..2abfd901b03 100644 --- a/mysql-test/include/rpl_init.inc +++ b/mysql-test/include/rpl_init.inc @@ -43,7 +43,6 @@ # # [--let $rpl_server_count= 7] # --let $rpl_topology= 1->2->3->1->4, 2->5, 6->7 -# [--let $rpl_extra_connections_per_server= 1] # [--let $rpl_check_server_ids= 1] # [--let $rpl_skip_change_master= 1] # [--let $rpl_skip_start_slave= 1] @@ -66,12 +65,6 @@ # want to specify the empty topology (no server replicates at # all), you have to set $rpl_topology=none. # -# $rpl_extra_connections_per_server -# By default, this script creates connections server_N and -# server_N_1. If you can set this variable to a number, the -# script creates: -# server_N, server_N_1, ..., server_N_$rpl_extra_connections_per_server -# # $rpl_check_server_ids # If $rpl_check_server_ids is set, this script checks that the # @@server_id of all servers are different. This is normally @@ -146,17 +139,8 @@ if (!$SERVER_MYPORT_4) # Check that $rpl_server_count is set if (!$rpl_server_count) { - --let $rpl_server_count= `SELECT REPLACE('$rpl_topology', '->', ',')` - if (`SELECT LOCATE(',', '$rpl_server_count')`) - { - --let $rpl_server_count= `SELECT GREATEST($rpl_server_count)` - } -} - ---let $_rpl_extra_connections_per_server= $rpl_extra_connections_per_server -if ($_rpl_extra_connections_per_server == '') -{ - --let $_rpl_extra_connections_per_server= 1 + --let $_compute_rpl_server_count= `SELECT REPLACE('$rpl_topology', '->', ',')` + --let $rpl_server_count= `SELECT GREATEST($_compute_rpl_server_count)` } @@ -175,20 +159,15 @@ if (!$rpl_debug) # Create two connections to each server; reset master/slave, select # database, set autoinc variables. --let $_rpl_server= $rpl_server_count ---let $underscore= _ +--let $_rpl_one= _1 while ($_rpl_server) { # Connect. --let $rpl_server_number= $_rpl_server --let $rpl_connection_name= server_$_rpl_server --source include/rpl_connect.inc - --let $_rpl_connection_number= 1 - while ($_rpl_connection_number <= $_rpl_extra_connections_per_server) - { - --let $rpl_connection_name= server_$_rpl_server$underscore$_rpl_connection_number - --source include/rpl_connect.inc - --inc $_rpl_connection_number - } + --let $rpl_connection_name= server_$_rpl_server$_rpl_one + --source include/rpl_connect.inc # Configure server. --let $rpl_connection_name= server_$_rpl_server diff --git a/mysql-test/include/rpl_reconnect.inc b/mysql-test/include/rpl_reconnect.inc index 673f382bac0..cdbbd0a1bf1 100644 --- a/mysql-test/include/rpl_reconnect.inc +++ b/mysql-test/include/rpl_reconnect.inc @@ -12,7 +12,6 @@ # ==== Usage ==== # # --let $rpl_server_number= N -# [--let $rpl_extra_connections_per_server= 1] # [--let $rpl_debug= 1] # --source include/rpl_reconnect.inc # @@ -22,7 +21,7 @@ # master server, 2 the slave server, 3 the 3rd server, and so on. # Cf. include/rpl_init.inc # -# $rpl_extra_connections_per_server, $rpl_debug +# $rpl_debug # See include/rpl_init.inc --let $include_filename= rpl_reconnect.inc @@ -33,11 +32,6 @@ if (!$rpl_server_number) --die ERROR IN TEST: you must set $rpl_server_number before you source rpl_connect.inc } -if ($_rpl_extra_connections_per_server == '') -{ - --let $_rpl_extra_connections_per_server= 1 -} - if ($rpl_debug) { @@ -78,14 +72,10 @@ if (!$_rpl_server_number) --source include/rpl_connection.inc --enable_reconnect ---let $_rpl_connection_number= 1 -while ($_rpl_connection_number <= $_rpl_extra_connections_per_server) -{ - --let $rpl_connection_name= server_$rpl_server_number$underscore$_rpl_connection_number - --source include/rpl_connection.inc - --enable_reconnect - --inc $_rpl_connection_number -} +--let $_rpl_one= _1 +--let $rpl_connection_name= server_$rpl_server_number$_rpl_one +--source include/rpl_connection.inc +--enable_reconnect if ($rpl_debug) { @@ -132,15 +122,10 @@ if (!$_rpl_server_number) --source include/wait_until_connected_again.inc --disable_reconnect ---let $_rpl_connection_number= 1 -while ($_rpl_connection_number <= $_rpl_extra_connections_per_server) -{ - --let $rpl_connection_name= server_$rpl_server_number$underscore$_rpl_connection_number - --source include/rpl_connection.inc - --source include/wait_until_connected_again.inc - --disable_reconnect - --inc $_rpl_connection_number -} +--let $rpl_connection_name= server_$rpl_server_number$_rpl_one +--source include/rpl_connection.inc +--source include/wait_until_connected_again.inc +--disable_reconnect --let $include_filename= rpl_reconnect.inc diff --git a/mysql-test/include/start_slave_sql.inc b/mysql-test/include/start_slave_sql.inc deleted file mode 100644 index 9cb66a2eb40..00000000000 --- a/mysql-test/include/start_slave_sql.inc +++ /dev/null @@ -1,39 +0,0 @@ -# ==== Purpose ==== -# -# Issues START SLAVE SQL_THREAD on the current connection. Then waits -# until the SQL thread has started, or until a timeout is reached. -# -# Please use this instead of 'START SLAVE SQL_THREAD', to reduce the -# risk of races in test cases. -# -# -# ==== Usage ==== -# -# [--let $slave_timeout= NUMBER] -# [--let $rpl_debug= 1] -# --source include/start_slave_sql.inc -# -# Parameters: -# $slave_timeout -# See include/wait_for_slave_param.inc -# -# $rpl_debug -# See include/rpl_init.inc - - ---let $include_filename= start_slave_sql.inc ---source include/begin_include_file.inc - - -if (!$rpl_debug) -{ - --disable_query_log -} - - -START SLAVE SQL_THREAD; ---source include/wait_for_slave_sql_to_start.inc - - ---let $include_filename= start_slave_sql.inc ---source include/end_include_file.inc diff --git a/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result b/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result deleted file mode 100644 index b11ad4f53bd..00000000000 --- a/mysql-test/suite/rpl/r/rpl_io_thd_wait_for_disk_space.result +++ /dev/null @@ -1,15 +0,0 @@ -include/master-slave.inc -[connection master] -CREATE TABLE t1(a INT); -INSERT INTO t1 VALUES(1); -CALL mtr.add_suppression("Disk is full writing"); -CALL mtr.add_suppression("Retry in 60 secs"); -include/stop_slave_sql.inc -SET @@GLOBAL.DEBUG= 'd,simulate_io_thd_wait_for_disk_space'; -INSERT INTO t1 VALUES(2); -SET DEBUG_SYNC='now WAIT_FOR parked'; -SET @@GLOBAL.DEBUG= '$debug_saved'; -include/assert_grep.inc [Found the disk full error message on the slave] -include/start_slave_sql.inc -DROP TABLE t1; -include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test b/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test deleted file mode 100644 index 6076be60ebc..00000000000 --- a/mysql-test/suite/rpl/t/rpl_io_thd_wait_for_disk_space.test +++ /dev/null @@ -1,71 +0,0 @@ -# ==== Purpose ==== -# -# Check that the execution of SHOW SLAVE STATUS command is not blocked when IO -# thread is blocked waiting for disk space. -# -# ==== Implementation ==== -# -# Simulate a scenario where IO thread is waiting for disk space while writing -# into the relay log. Execute SHOW SLAVE STATUS command after IO thread is -# blocked waiting for space. The command should not be blocked. -# -# ==== References ==== -# -# Bug#21753696: MAKE SHOW SLAVE STATUS NON BLOCKING IF IO THREAD WAITS FOR -# DISK SPACE -# Bug#20685029: SLAVE IO THREAD SHOULD STOP WHEN DISK IS FULL -# -############################################################################### ---source include/have_debug.inc -# Inorder to grep a specific error pattern in error log a fresh error log -# needs to be generated. ---source include/force_restart.inc ---source include/master-slave.inc - -# Generate events to be replicated to the slave -CREATE TABLE t1(a INT); -INSERT INTO t1 VALUES(1); ---sync_slave_with_master - -# Those errors will only happen in the slave -CALL mtr.add_suppression("Disk is full writing"); -CALL mtr.add_suppression("Retry in 60 secs"); - -# Stop the SQL thread to avoid writing on disk ---source include/stop_slave_sql.inc - -# Set the debug option that will simulate disk full ---let $debug_saved= `SELECT @@GLOBAL.DEBUG` -SET @@GLOBAL.DEBUG= 'd,simulate_io_thd_wait_for_disk_space'; - -# Generate events to be replicated to the slave ---connection master -INSERT INTO t1 VALUES(2); - ---connection slave1 -SET DEBUG_SYNC='now WAIT_FOR parked'; - -# Get the relay log file name using SHOW SLAVE STATUS ---let $relay_log_file= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) - ---connection slave -# Restore the debug options to "simulate" freed space on disk -SET @@GLOBAL.DEBUG= '$debug_saved'; - -# There should be a message in the error log of the slave stating -# that it was waiting for space to write on the relay log. ---let $assert_file=$MYSQLTEST_VARDIR/log/mysqld.2.err -# Grep only after the message that the I/O thread has started ---let $assert_only_after= Slave I/O .* connected to master .*replication started in log .* at position ---let $assert_count= 1 ---let $assert_select=Disk is full writing .*$relay_log_file.* ---let $assert_text= Found the disk full error message on the slave ---source include/assert_grep.inc - -# Start the SQL thread to let the slave to sync and finish gracefully ---source include/start_slave_sql.inc - -# Cleanup ---connection master -DROP TABLE t1; ---source include/rpl_end.inc diff --git a/mysys/errors.c b/mysys/errors.c index b6064460535..a6e2e300a1f 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -15,7 +15,7 @@ #include "mysys_priv.h" #include "mysys_err.h" -#include "m_string.h" + #ifndef SHARED_LIBRARY const char *globerrs[GLOBERRS]= @@ -109,7 +109,6 @@ void init_glob_errs() */ void wait_for_free_space(const char *filename, int errors) { - size_t time_to_sleep= MY_WAIT_FOR_USER_TO_FIX_PANIC; if (!(errors % MY_WAIT_GIVE_USER_A_MESSAGE)) { my_printf_warning(EE(EE_DISK_FULL), @@ -120,15 +119,10 @@ void wait_for_free_space(const char *filename, int errors) } DBUG_EXECUTE_IF("simulate_no_free_space_error", { - time_to_sleep= 1; + (void) sleep(1); + return; }); - DBUG_EXECUTE_IF("simulate_io_thd_wait_for_disk_space", - { - time_to_sleep= 1; - }); - - (void) sleep(time_to_sleep); - DEBUG_SYNC_C("disk_full_reached"); + (void) sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC); } const char **get_global_errmsgs() diff --git a/mysys/my_write.c b/mysys/my_write.c index 2e68a4dcff3..f092420756e 100644 --- a/mysys/my_write.c +++ b/mysys/my_write.c @@ -24,7 +24,6 @@ size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags) { size_t writtenbytes, written; uint errors; - size_t ToWriteCount; DBUG_ENTER("my_write"); DBUG_PRINT("my",("fd: %d Buffer: %p Count: %lu MyFlags: %d", Filedes, Buffer, (ulong) Count, MyFlags)); @@ -38,14 +37,11 @@ size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags) { DBUG_SET("+d,simulate_file_write_error");}); for (;;) { - ToWriteCount= Count; - DBUG_EXECUTE_IF("simulate_io_thd_wait_for_disk_space", { ToWriteCount= 1; }); #ifdef _WIN32 - writtenbytes= my_win_write(Filedes, Buffer, ToWriteCount); + writtenbytes= my_win_write(Filedes, Buffer, Count); #else - writtenbytes= write(Filedes, Buffer, ToWriteCount); + writtenbytes= write(Filedes, Buffer, Count); #endif - DBUG_EXECUTE_IF("simulate_io_thd_wait_for_disk_space", { errno= ENOSPC; }); DBUG_EXECUTE_IF("simulate_file_write_error", { errno= ENOSPC; diff --git a/sql/log.cc b/sql/log.cc index a7f05905514..42937ec1682 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -37,7 +37,6 @@ #include "log_event.h" // Query_log_event #include "rpl_filter.h" #include "rpl_rli.h" -#include "rpl_mi.h" #include "sql_audit.h" #include "sql_show.h" @@ -4378,22 +4377,13 @@ end: } -#ifdef HAVE_REPLICATION -bool MYSQL_BIN_LOG::append(Log_event* ev, Master_info *mi) +bool MYSQL_BIN_LOG::append(Log_event* ev) { bool error = 0; - mysql_mutex_assert_owner(&mi->data_lock); mysql_mutex_lock(&LOCK_log); DBUG_ENTER("MYSQL_BIN_LOG::append"); DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); - /* - Release data_lock by holding LOCK_log, while writing into the relay log. - If slave IO thread waits here for free space, we don't want - SHOW SLAVE STATUS to hang on mi->data_lock. Note LOCK_log mutex is - sufficient to block SQL thread when IO thread is updating relay log here. - */ - mysql_mutex_unlock(&mi->data_lock); /* Log_event::write() is smart enough to use my_b_write() or my_b_append() depending on the kind of cache we have. @@ -4408,58 +4398,24 @@ bool MYSQL_BIN_LOG::append(Log_event* ev, Master_info *mi) if (flush_and_sync(0)) goto err; if ((uint) my_b_append_tell(&log_file) > max_size) - { - /* - If rotation is required we must acquire data_lock to protect - description_event from clients executing FLUSH LOGS in parallel. - In order do that we must release the existing LOCK_log so that we - get it once again in proper locking order to avoid dead locks. - i.e data_lock , LOCK_log. - */ - mysql_mutex_unlock(&LOCK_log); - mysql_mutex_lock(&mi->data_lock); - mysql_mutex_lock(&LOCK_log); error= new_file_without_locking(); - /* - After rotation release data_lock, we need the LOCK_log till we signal - the updation. - */ - mysql_mutex_unlock(&mi->data_lock); - } err: - signal_update(); // Safe as we don't call close mysql_mutex_unlock(&LOCK_log); - mysql_mutex_lock(&mi->data_lock); + signal_update(); // Safe as we don't call close DBUG_RETURN(error); } -bool MYSQL_BIN_LOG::appendv(Master_info* mi, const char* buf, uint len,...) +bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...) { bool error= 0; DBUG_ENTER("MYSQL_BIN_LOG::appendv"); va_list(args); va_start(args,len); - mysql_mutex_assert_owner(&mi->data_lock); - mysql_mutex_lock(&LOCK_log); DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); - /* - Release data_lock by holding LOCK_log, while writing into the relay log. - If slave IO thread waits here for free space, we don't want - SHOW SLAVE STATUS to hang on mi->data_lock. Note LOCK_log mutex is - sufficient to block SQL thread when IO thread is updating relay log here. - */ - mysql_mutex_unlock(&mi->data_lock); - DBUG_EXECUTE_IF("simulate_io_thd_wait_for_disk_space", - { - const char act[]= "disk_full_reached SIGNAL parked"; - DBUG_ASSERT(opt_debug_sync_timeout > 0); - DBUG_ASSERT(!debug_sync_set_action(current_thd, - STRING_WITH_LEN(act))); - };); - + mysql_mutex_assert_owner(&LOCK_log); do { if (my_b_append(&log_file,(uchar*) buf,len)) @@ -4472,34 +4428,13 @@ bool MYSQL_BIN_LOG::appendv(Master_info* mi, const char* buf, uint len,...) DBUG_PRINT("info",("max_size: %lu",max_size)); if (flush_and_sync(0)) goto err; - if ((uint) my_b_append_tell(&log_file) > - DBUG_EVALUATE_IF("rotate_slave_debug_group", 500, max_size)) - { - /* - If rotation is required we must acquire data_lock to protect - description_event from clients executing FLUSH LOGS in parallel. - In order do that we must release the existing LOCK_log so that we - get it once again in proper locking order to avoid dead locks. - i.e data_lock , LOCK_log. - */ - mysql_mutex_unlock(&LOCK_log); - mysql_mutex_lock(&mi->data_lock); - mysql_mutex_lock(&LOCK_log); + if ((uint) my_b_append_tell(&log_file) > max_size) error= new_file_without_locking(); - /* - After rotation release data_lock, we need the LOCK_log till we signal - the updation. - */ - mysql_mutex_unlock(&mi->data_lock); - } err: if (!error) signal_update(); - mysql_mutex_unlock(&LOCK_log); - mysql_mutex_lock(&mi->data_lock); DBUG_RETURN(error); } -#endif bool MYSQL_BIN_LOG::flush_and_sync(bool *synced) { diff --git a/sql/log.h b/sql/log.h index 7d1c3161ac2..4d13e4d0720 100644 --- a/sql/log.h +++ b/sql/log.h @@ -20,7 +20,6 @@ #include "handler.h" /* my_xid */ class Relay_log_info; -class Master_info; class Format_description_log_event; @@ -455,8 +454,8 @@ public: v stands for vector invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0) */ - bool appendv(Master_info* mi, const char* buf,uint len,...); - bool append(Log_event* ev, Master_info* mi); + bool appendv(const char* buf,uint len,...); + bool append(Log_event* ev); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); diff --git a/sql/slave.cc b/sql/slave.cc index 31037c453d3..acf68e231f3 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1660,7 +1660,7 @@ Waiting for the slave SQL thread to free enough relay log space"); #endif if (rli->sql_force_rotate_relay) { - rotate_relay_log(rli->mi, true/*need_data_lock=true*/); + rotate_relay_log(rli->mi); rli->sql_force_rotate_relay= false; } @@ -1705,7 +1705,7 @@ static void write_ignored_events_info_to_relay_log(THD *thd, Master_info *mi) if (likely((bool)ev)) { ev->server_id= 0; // don't be ignored by slave SQL thread - if (unlikely(rli->relay_log.append(ev, mi))) + if (unlikely(rli->relay_log.append(ev))) mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE), "failed to write a Rotate event" @@ -3605,7 +3605,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev) break; Execute_load_log_event xev(thd,0,0); xev.log_pos = cev->log_pos; - if (unlikely(mi->rli.relay_log.append(&xev, mi))) + if (unlikely(mi->rli.relay_log.append(&xev))) { mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE), @@ -3619,7 +3619,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev) { cev->block = net->read_pos; cev->block_len = num_bytes; - if (unlikely(mi->rli.relay_log.append(cev, mi))) + if (unlikely(mi->rli.relay_log.append(cev))) { mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE), @@ -3634,7 +3634,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev) aev.block = net->read_pos; aev.block_len = num_bytes; aev.log_pos = cev->log_pos; - if (unlikely(mi->rli.relay_log.append(&aev, mi))) + if (unlikely(mi->rli.relay_log.append(&aev))) { mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE), @@ -3713,7 +3713,7 @@ static int process_io_rotate(Master_info *mi, Rotate_log_event *rev) Rotate the relay log makes binlog format detection easier (at next slave start or mysqlbinlog) */ - DBUG_RETURN(rotate_relay_log(mi, false/*need_data_lock=false*/)); + DBUG_RETURN(rotate_relay_log(mi) /* will take the right mutexes */); } /* @@ -3819,7 +3819,7 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf, Log_event::Log_event(const char* buf...) in log_event.cc). */ ev->log_pos+= event_len; /* make log_pos be the pos of the end of the event */ - if (unlikely(rli->relay_log.append(ev, mi))) + if (unlikely(rli->relay_log.append(ev))) { delete ev; mysql_mutex_unlock(&mi->data_lock); @@ -3875,7 +3875,7 @@ static int queue_binlog_ver_3_event(Master_info *mi, const char *buf, inc_pos= event_len; break; } - if (unlikely(rli->relay_log.append(ev, mi))) + if (unlikely(rli->relay_log.append(ev))) { delete ev; mysql_mutex_unlock(&mi->data_lock); @@ -4083,6 +4083,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) direct master (an unsupported, useless setup!). */ + mysql_mutex_lock(log_lock); s_id= uint4korr(buf + SERVER_ID_OFFSET); if ((s_id == ::server_id && !mi->rli.replicate_same_server_id) || /* @@ -4115,7 +4116,6 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) IGNORE_SERVER_IDS it increments mi->master_log_pos as well as rli->group_relay_log_pos. */ - mysql_mutex_lock(log_lock); if (!(s_id == ::server_id && !mi->rli.replicate_same_server_id) || (buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT && buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT && @@ -4127,14 +4127,13 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) rli->ign_master_log_pos_end= mi->master_log_pos; } rli->relay_log.signal_update(); // the slave SQL thread needs to re-check - mysql_mutex_unlock(log_lock); DBUG_PRINT("info", ("master_log_pos: %lu, event originating from %u server, ignored", (ulong) mi->master_log_pos, uint4korr(buf + SERVER_ID_OFFSET))); } else { /* write the event to the relay log */ - if (likely(!(rli->relay_log.appendv(mi, buf,event_len,0)))) + if (likely(!(rli->relay_log.appendv(buf,event_len,0)))) { mi->master_log_pos+= inc_pos; DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); @@ -4144,10 +4143,9 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) { error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; } - mysql_mutex_lock(log_lock); rli->ign_master_log_name_end[0]= 0; // last event is not ignored - mysql_mutex_unlock(log_lock); } + mysql_mutex_unlock(log_lock); skip_relay_logging: @@ -5007,21 +5005,11 @@ err: locks; here we don't, so this function is mainly taking locks). Returns nothing as we cannot catch any error (MYSQL_BIN_LOG::new_file() is void). - - @param mi Master_info for the IO thread. - @param need_data_lock If true, mi->data_lock will be acquired otherwise, - mi->data_lock must be held by the caller. */ -int rotate_relay_log(Master_info* mi, bool need_data_lock) +int rotate_relay_log(Master_info* mi) { DBUG_ENTER("rotate_relay_log"); - if (need_data_lock) - mysql_mutex_lock(&mi->data_lock); - else - { - mysql_mutex_assert_owner(&mi->data_lock); - } Relay_log_info* rli= &mi->rli; int error= 0; @@ -5056,8 +5044,6 @@ int rotate_relay_log(Master_info* mi, bool need_data_lock) */ rli->relay_log.harvest_bytes_written(&rli->log_space_total); end: - if (need_data_lock) - mysql_mutex_unlock(&mi->data_lock); DBUG_RETURN(error); } diff --git a/sql/slave.h b/sql/slave.h index 0cf8adb0315..7bf136694cc 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -205,7 +205,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, const char** errmsg); void set_slave_thread_options(THD* thd); void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli); -int rotate_relay_log(Master_info* mi, bool need_data_lock); +int rotate_relay_log(Master_info* mi); int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli); pthread_handler_t handle_slave_io(void *arg); diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index f24f31b6399..b29cc9a9433 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -157,7 +157,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options, { #ifdef HAVE_REPLICATION mysql_mutex_lock(&LOCK_active_mi); - if (rotate_relay_log(active_mi, true/*need_data_lock=true*/)) + if (rotate_relay_log(active_mi)) *write_to_binlog= -1; mysql_mutex_unlock(&LOCK_active_mi); #endif