summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorhezx@mail.hezx.com <>2008-03-14 11:35:41 +0800
committerhezx@mail.hezx.com <>2008-03-14 11:35:41 +0800
commita3d02647e643270ea2e7deecf8fb4127834e214d (patch)
tree8e93396e4d62cd6f4ab2aded6aa2244ca753e04d /sql
parent49915eb88ab75f635a962b09d19ba4acc5d6ce91 (diff)
downloadmariadb-git-a3d02647e643270ea2e7deecf8fb4127834e214d.tar.gz
BUG#33029 5.0 to 5.1 replication fails on dup key when inserting
using a trig in SP For all 5.0 and up to 5.1.12 exclusive, when a stored routine or trigger caused an INSERT into an AUTO_INCREMENT column, the generated AUTO_INCREMENT value should not be written into the binary log, which means if a statement does not generate AUTO_INCREMENT value itself, there will be no Intvar event (SET INSERT_ID) associated with it even if one of the stored routine or trigger caused generation of such a value. And meanwhile, when executing a stored routine or trigger, it would ignore the INSERT_ID value even if there is a INSERT_ID value available set by a SET INSERT_ID statement. Starting from MySQL 5.1.12, the generated AUTO_INCREMENT value is written into the binary log, and the value will be used if available when executing the stored routine or trigger. Prior fix of this bug in MySQL 5.0 and prior MySQL 5.1.12 (referenced as the buggy versions in the text below), when a statement that generates AUTO_INCREMENT value by the top statement was executed in the body of a SP, all statements in the SP after this statement would be treated as if they had generated AUTO_INCREMENT by the top statement. When a statement that did not generate AUTO_INCREMENT value by the top statement but by a function/trigger called by it, an erroneous Intvar event would be associated with the statement, this erroneous INSERT_ID value wouldn't cause problem when replicating between masters and slaves of 5.0.x or prior 5.1.12, because the erroneous INSERT_ID value was not used when executing functions/triggers. But when replicating from buggy versions to 5.1.12 or newer, which will use the INSERT_ID value in functions/triggers, the erroneous value will be used, which would cause duplicate entry error and cause the slave to stop. The patch for 5.1 fixed it to ignore the SET INSERT_ID value when executing functions/triggers if it is replicating from a master of buggy versions, another patch for 5.0 fixed it not to generate the erroneous Intvar event.
Diffstat (limited to 'sql')
-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
5 files changed, 93 insertions, 12 deletions
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; }