diff options
-rw-r--r-- | mysql-test/include/show_binlog_events.inc | 7 | ||||
-rw-r--r-- | mysql-test/std_data/bug33029-slave-relay-bin.000001 | bin | 0 -> 3933 bytes | |||
-rw-r--r-- | mysql-test/suite/binlog/r/binlog_auto_increment_bug33029.result | 33 | ||||
-rw-r--r-- | mysql-test/suite/binlog/t/binlog_auto_increment_bug33029-master.opt | 1 | ||||
-rw-r--r-- | mysql-test/suite/binlog/t/binlog_auto_increment_bug33029.test | 36 | ||||
-rw-r--r-- | sql/slave.cc | 30 | ||||
-rw-r--r-- | sql/slave.h | 3 | ||||
-rw-r--r-- | sql/sql_class.cc | 50 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/structs.h | 21 |
10 files changed, 169 insertions, 13 deletions
diff --git a/mysql-test/include/show_binlog_events.inc b/mysql-test/include/show_binlog_events.inc index fcdf84102aa..a80dc75df7d 100644 --- a/mysql-test/include/show_binlog_events.inc +++ b/mysql-test/include/show_binlog_events.inc @@ -1,4 +1,9 @@ ---let $binlog_start=106 +# $binlog_start can be set by caller or take a default value + +if (!$binlog_start) +{ + let $binlog_start=106; +} --replace_result $binlog_start <binlog_start> --replace_column 2 # 4 # 5 # --replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /file_id=[0-9]+/file_id=#/ diff --git a/mysql-test/std_data/bug33029-slave-relay-bin.000001 b/mysql-test/std_data/bug33029-slave-relay-bin.000001 Binary files differnew file mode 100644 index 00000000000..326d3bb62ab --- /dev/null +++ b/mysql-test/std_data/bug33029-slave-relay-bin.000001 diff --git a/mysql-test/suite/binlog/r/binlog_auto_increment_bug33029.result b/mysql-test/suite/binlog/r/binlog_auto_increment_bug33029.result new file mode 100644 index 00000000000..79b3af85072 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_auto_increment_bug33029.result @@ -0,0 +1,33 @@ +change master to +MASTER_HOST='dummy.localdomain', +RELAY_LOG_FILE='slave-relay-bin.000001', +RELAY_LOG_POS=4; +start slave sql_thread; +select MASTER_POS_WAIT('master-bin.000001', 3776); +# Result on slave +SELECT * FROM t1; +id +5 +6 +7 +8 +9 +10 +11 +SELECT * FROM t2; +id +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 diff --git a/mysql-test/suite/binlog/t/binlog_auto_increment_bug33029-master.opt b/mysql-test/suite/binlog/t/binlog_auto_increment_bug33029-master.opt new file mode 100644 index 00000000000..74e71a8e558 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_auto_increment_bug33029-master.opt @@ -0,0 +1 @@ +--replicate-same-server-id --relay-log=slave-relay-bin --skip-slave-start diff --git a/mysql-test/suite/binlog/t/binlog_auto_increment_bug33029.test b/mysql-test/suite/binlog/t/binlog_auto_increment_bug33029.test new file mode 100644 index 00000000000..0f0ecdd06a0 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_auto_increment_bug33029.test @@ -0,0 +1,36 @@ +# BUG#33029 5.0 to 5.1 replication fails on dup key when inserting +# using a trig in SP + +# For all 5.0 up to 5.0.58 exclusive, and 5.1 up to 5.1.12 exclusive, +# if one statement in a SP generated AUTO_INCREMENT value by the top +# statement, all statements after it would be considered generated +# AUTO_INCREMENT value by the top statement, and a erroneous INSERT_ID +# value might be associated with these statement, which could cause +# duplicate entry error and stop the slave. + +# Test if the slave can replicate from such a buggy master + +# The bug33029-slave-relay-bin.000001 file is the +# slave-replay-bin.000003 file generated by run the +# rpl_auto_increment_bug33029.test with clean up statements at the end +# of the test case removed on a buggy 5.0 server + +copy_file $MYSQL_TEST_DIR/std_data/bug33029-slave-relay-bin.000001 $MYSQLTEST_VARDIR/master-data/slave-relay-bin.000001; + +write_file $MYSQLTEST_VARDIR/master-data/slave-relay-bin.index; +slave-relay-bin.000001 +EOF + +change master to + MASTER_HOST='dummy.localdomain', + RELAY_LOG_FILE='slave-relay-bin.000001', + RELAY_LOG_POS=4; + +start slave sql_thread; +disable_result_log; +select MASTER_POS_WAIT('master-bin.000001', 3776); +enable_result_log; + +echo # Result on slave; +SELECT * FROM t1; +SELECT * FROM t2; diff --git a/sql/slave.cc b/sql/slave.cc index e3a333f31d1..96419a00f18 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -4026,9 +4026,10 @@ end: has a certain bug. @param rli Relay_log_info which tells the master's version @param bug_id Number of the bug as found in bugs.mysql.com + @param report bool report error message, default TRUE @return TRUE if master has the bug, FALSE if it does not. */ -bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) +bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id, bool report) { struct st_version_range_for_one_bug { uint bug_id; @@ -4038,7 +4039,9 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) static struct st_version_range_for_one_bug versions_for_all_bugs[]= { {24432, { 5, 0, 24 }, { 5, 0, 38 } }, - {24432, { 5, 1, 12 }, { 5, 1, 17 } } + {24432, { 5, 1, 12 }, { 5, 1, 17 } }, + {33029, { 5, 0, 0 }, { 5, 0, 58 } }, + {33029, { 5, 1, 0 }, { 5, 1, 12 } }, }; const uchar *master_ver= rli->relay_log.description_event_for_exec->server_version_split; @@ -4054,6 +4057,9 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) (memcmp(introduced_in, master_ver, 3) <= 0) && (memcmp(fixed_in, master_ver, 3) > 0)) { + if (!report) + return TRUE; + // a short message for SHOW SLAVE STATUS (message length constraints) my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from" " http://bugs.mysql.com/bug.php?id=%u" @@ -4085,6 +4091,26 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) return FALSE; } +/** + BUG#33029, For all 5.0 up to 5.0.58 exclusive, and 5.1 up to 5.1.12 + exclusive, if one statement in a SP generated AUTO_INCREMENT value + by the top statement, all statements after it would be considered + generated AUTO_INCREMENT value by the top statement, and a + erroneous INSERT_ID value might be associated with these statement, + which could cause duplicate entry error and stop the slave. + + Detect buggy master to work around. + */ +bool rpl_master_erroneous_autoinc(THD *thd) +{ + if (active_mi && active_mi->rli.sql_thd == thd) + { + Relay_log_info *rli= &active_mi->rli; + return rpl_master_has_bug(rli, 33029, FALSE); + } + return FALSE; +} + #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION template class I_List_iterator<i_string>; template class I_List_iterator<i_string_pair>; diff --git a/sql/slave.h b/sql/slave.h index b4f1b7f7467..80d267e5b27 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -165,7 +165,8 @@ int fetch_master_table(THD* thd, const char* db_name, const char* table_name, bool show_master_info(THD* thd, Master_info* mi); bool show_binlog_info(THD* thd); -bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id); +bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id, bool report=TRUE); +bool rpl_master_erroneous_autoinc(THD* thd); const char *print_slave_db_safe(const char *db); int check_expected_error(THD* thd, Relay_log_info const *rli, int error_code); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 044ea70e994..10c65f18a35 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -28,6 +28,7 @@ #include "mysql_priv.h" #include "rpl_rli.h" #include "rpl_record.h" +#include "slave.h" #include <my_bitmap.h> #include "log_event.h" #include <m_ctype.h> @@ -2827,6 +2828,18 @@ extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all) void THD::reset_sub_statement_state(Sub_statement_state *backup, uint new_state) { +#ifndef EMBEDDED_LIBRARY + /* BUG#33029, if we are replicating from a buggy master, reset + auto_inc_intervals_forced to prevent substatement + (triggers/functions) from using erroneous INSERT_ID value + */ + if (rpl_master_erroneous_autoinc(this)) + { + backup->auto_inc_intervals_forced= auto_inc_intervals_forced; + auto_inc_intervals_forced.empty(); + } +#endif + backup->options= options; backup->in_sub_stmt= in_sub_stmt; backup->enable_slow_log= enable_slow_log; @@ -2864,6 +2877,18 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, void THD::restore_sub_statement_state(Sub_statement_state *backup) { +#ifndef EMBEDDED_LIBRARY + /* BUG#33029, if we are replicating from a buggy master, restore + auto_inc_intervals_forced so that the top statement can use the + INSERT_ID value set before this statement. + */ + if (rpl_master_erroneous_autoinc(this)) + { + auto_inc_intervals_forced= backup->auto_inc_intervals_forced; + backup->auto_inc_intervals_forced.empty(); + } +#endif + /* To save resources we want to release savepoints which were created during execution of function or trigger before leaving their savepoint @@ -3569,17 +3594,24 @@ bool Discrete_intervals_list::append(ulonglong start, ulonglong val, { /* it cannot, so need to add a new interval */ Discrete_interval *new_interval= new Discrete_interval(start, val, incr); - if (unlikely(new_interval == NULL)) // out of memory - DBUG_RETURN(1); - DBUG_PRINT("info",("adding new auto_increment interval")); - if (head == NULL) - head= current= new_interval; - else - tail->next= new_interval; - tail= new_interval; - elements++; + DBUG_RETURN(append(new_interval)); } DBUG_RETURN(0); } +bool Discrete_intervals_list::append(Discrete_interval *new_interval) +{ + DBUG_ENTER("Discrete_intervals_list::append"); + if (unlikely(new_interval == NULL)) + DBUG_RETURN(1); + DBUG_PRINT("info",("adding new auto_increment interval")); + if (head == NULL) + head= current= new_interval; + else + tail->next= new_interval; + tail= new_interval; + elements++; + DBUG_RETURN(0); +} + #endif /* !defined(MYSQL_CLIENT) */ diff --git a/sql/sql_class.h b/sql/sql_class.h index b660c615920..dfd9879b98f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -911,6 +911,7 @@ public: ulonglong first_successful_insert_id_in_prev_stmt; ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row; Discrete_interval auto_inc_interval_for_cur_row; + Discrete_intervals_list auto_inc_intervals_forced; ulonglong limit_found_rows; ha_rows cuted_fields, sent_row_count, examined_row_count; ulong client_capabilities; diff --git a/sql/structs.h b/sql/structs.h index f14cca3c5db..809175fdde4 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -314,8 +314,27 @@ private: */ Discrete_interval *current; uint elements; // number of elements + + /* helper function for copy construct and assignment operator */ + void copy_(const Discrete_intervals_list& from) + { + for (Discrete_interval *i= from.head; i; i= i->next) + { + Discrete_interval j= *i; + append(&j); + } + } public: Discrete_intervals_list() : head(NULL), current(NULL), elements(0) {}; + Discrete_intervals_list(const Discrete_intervals_list& from) + { + copy_(from); + } + void operator=(const Discrete_intervals_list& from) + { + empty(); + copy_(from); + } void empty_no_free() { head= current= NULL; @@ -331,6 +350,7 @@ public: } empty_no_free(); } + const Discrete_interval* get_next() { Discrete_interval *tmp= current; @@ -340,6 +360,7 @@ public: } ~Discrete_intervals_list() { empty(); }; bool append(ulonglong start, ulonglong val, ulonglong incr); + bool append(Discrete_interval *interval); ulonglong minimum() const { return (head ? head->minimum() : 0); }; ulonglong maximum() const { return (head ? tail->maximum() : 0); }; uint nb_elements() const { return elements; } |