summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/include/show_binlog_events.inc7
-rw-r--r--mysql-test/std_data/bug33029-slave-relay-bin.000001bin0 -> 3933 bytes
-rw-r--r--mysql-test/suite/binlog/r/binlog_auto_increment_bug33029.result33
-rw-r--r--mysql-test/suite/binlog/t/binlog_auto_increment_bug33029-master.opt1
-rw-r--r--mysql-test/suite/binlog/t/binlog_auto_increment_bug33029.test36
-rw-r--r--sql/slave.cc30
-rw-r--r--sql/slave.h3
-rw-r--r--sql/sql_class.cc50
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/structs.h21
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
new file mode 100644
index 00000000000..326d3bb62ab
--- /dev/null
+++ b/mysql-test/std_data/bug33029-slave-relay-bin.000001
Binary files differ
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; }