summaryrefslogtreecommitdiff
path: root/sql/log_event.cc
diff options
context:
space:
mode:
authorLuis Soares <luis.soares@oracle.com>2011-07-14 12:15:24 +0100
committerLuis Soares <luis.soares@oracle.com>2011-07-14 12:15:24 +0100
commitce8077d8d37e37f4b007f64c5ca301d096699db8 (patch)
tree2d2044fad381e74cdb87c8b0d5c28501e45dbbda /sql/log_event.cc
parent93aba6e6689ee109bd236a741ac519ae931cb8fa (diff)
downloadmariadb-git-ce8077d8d37e37f4b007f64c5ca301d096699db8.tar.gz
BUG#11753004: 44360: REPLICATION FAILED
The server crashes if it processes table map events that are corrupted, especially if they map different tables to the same identifier. This could happen, for instance, due to BUG 56226. We fix this by checking whether the table map has already been mapped before actually applying the event. If it has been mapped with different settings an error is raised and the slave SQL thread stops. If it has been mapped with same settings the event is skipped. If the table is set to be ignored by the filtering rules, there is no change in behavior: the event is skipped and ids are not checked.
Diffstat (limited to 'sql/log_event.cc')
-rw-r--r--sql/log_event.cc148
1 files changed, 136 insertions, 12 deletions
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 1f58c7ed1bf..82a6d9e8ffc 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -8222,6 +8222,97 @@ Table_map_log_event::~Table_map_log_event()
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+enum enum_tbl_map_status
+{
+ /* no duplicate identifier found */
+ OK_TO_PROCESS= 0,
+
+ /* this table map must be filtered out */
+ FILTERED_OUT= 1,
+
+ /* identifier mapping table with different properties */
+ SAME_ID_MAPPING_DIFFERENT_TABLE= 2,
+
+ /* a duplicate identifier was found mapping the same table */
+ SAME_ID_MAPPING_SAME_TABLE= 3
+};
+
+/*
+ Checks if this table map event should be processed or not. First
+ it checks the filtering rules, and then looks for duplicate identifiers
+ in the existing list of rli->tables_to_lock.
+
+ It checks that there hasn't been any corruption by verifying that there
+ are no duplicate entries with different properties.
+
+ In some cases, some binary logs could get corrupted, showing several
+ tables mapped to the same table_id, 0 (see: BUG#56226). Thus we do this
+ early sanity check for such cases and avoid that the server crashes
+ later.
+
+ In some corner cases, the master logs duplicate table map events, i.e.,
+ same id, same database name, same table name (see: BUG#37137). This is
+ different from the above as it's the same table that is mapped again
+ to the same identifier. Thus we cannot just check for same ids and
+ assume that the event is corrupted we need to check every property.
+
+ NOTE: in the event that BUG#37137 ever gets fixed, this extra check
+ will still be valid because we would need to support old binary
+ logs anyway.
+
+ @param rli The relay log info reference.
+ @param table_list A list element containing the table to check against.
+ @return OK_TO_PROCESS
+ if there was no identifier already in rli->tables_to_lock
+
+ FILTERED_OUT
+ if the event is filtered according to the filtering rules
+
+ SAME_ID_MAPPING_DIFFERENT_TABLE
+ if the same identifier already maps a different table in
+ rli->tables_to_lock
+
+ SAME_ID_MAPPING_SAME_TABLE
+ if the same identifier already maps the same table in
+ rli->tables_to_lock.
+*/
+static enum_tbl_map_status
+check_table_map(Relay_log_info const *rli, RPL_TABLE_LIST *table_list)
+{
+ DBUG_ENTER("check_table_map");
+ enum_tbl_map_status res= OK_TO_PROCESS;
+
+ if (rli->sql_thd->slave_thread /* filtering is for slave only */ &&
+ (!rpl_filter->db_ok(table_list->db) ||
+ (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list))))
+ res= FILTERED_OUT;
+ else
+ {
+ for(RPL_TABLE_LIST *ptr= static_cast<RPL_TABLE_LIST*>(rli->tables_to_lock);
+ ptr;
+ ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_local))
+ {
+ if (ptr->table_id == table_list->table_id)
+ {
+
+ if (strcmp(ptr->db, table_list->db) ||
+ strcmp(ptr->alias, table_list->table_name) ||
+ ptr->lock_type != TL_WRITE) // the ::do_apply_event always sets TL_WRITE
+ res= SAME_ID_MAPPING_DIFFERENT_TABLE;
+ else
+ res= SAME_ID_MAPPING_SAME_TABLE;
+
+ break;
+ }
+ }
+ }
+
+ DBUG_PRINT("debug", ("check of table map ended up with: %u", res));
+
+ DBUG_RETURN(res);
+}
+
int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
{
RPL_TABLE_LIST *table_list;
@@ -8248,20 +8339,13 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
table_list->alias= table_list->table_name = tname_mem;
table_list->lock_type= TL_WRITE;
table_list->next_global= table_list->next_local= 0;
- table_list->table_id= m_table_id;
+ table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id);
table_list->updating= 1;
strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
strmov(table_list->table_name, m_tblnam);
-
- int error= 0;
-
- if (rli->sql_thd->slave_thread /* filtering is for slave only */ &&
- (!rpl_filter->db_ok(table_list->db) ||
- (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list))))
- {
- my_free(memory, MYF(MY_WME));
- }
- else
+ DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id));
+ enum_tbl_map_status tblmap_status= check_table_map(rli, table_list);
+ if (tblmap_status == OK_TO_PROCESS)
{
DBUG_ASSERT(thd->lex->query_tables != table_list);
@@ -8290,8 +8374,48 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
const_cast<Relay_log_info*>(rli)->tables_to_lock_count++;
/* 'memory' is freed in clear_tables_to_lock */
}
+ else // FILTERED_OUT, SAME_ID_MAPPING_*
+ {
+ /*
+ If mapped already but with different properties, we raise an
+ error.
+ If mapped already but with same properties we skip the event.
+ If filtered out we skip the event.
- DBUG_RETURN(error);
+ In all three cases, we need to free the memory previously
+ allocated.
+ */
+ if (tblmap_status == SAME_ID_MAPPING_DIFFERENT_TABLE)
+ {
+ /*
+ Something bad has happened. We need to stop the slave as strange things
+ could happen if we proceed: slave crash, wrong table being updated, ...
+ As a consequence we push an error in this case.
+ */
+
+ char buf[256];
+
+ my_snprintf(buf, sizeof(buf),
+ "Found table map event mapping table id %u which "
+ "was already mapped but with different settings.",
+ table_list->table_id);
+
+ if (thd->slave_thread)
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR), buf);
+ else
+ /*
+ For the cases in which a 'BINLOG' statement is set to
+ execute in a user session
+ */
+ my_printf_error(ER_SLAVE_FATAL_ERROR, ER(ER_SLAVE_FATAL_ERROR),
+ MYF(0), buf);
+ }
+
+ my_free(memory, MYF(0));
+ }
+
+ DBUG_RETURN(tblmap_status == SAME_ID_MAPPING_DIFFERENT_TABLE);
}
Log_event::enum_skip_reason