From 41e0d0a3ab76fcd78b7375ab28cd5e8c4e71bdf9 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Oct 2009 07:19:36 +0800 Subject: Bug #45677 Slave stops with Duplicate entry for key PRIMARY when using trigger The problem is that there is only one autoinc value associated with the query when binlogging. If more than one autoinc values are used in the query, the autoinc values after the first one can be inserted wrongly on slave. So these autoinc values can become inconsistent on master and slave. The problem is resolved by marking all the statements that invoke a trigger or call a function that updated autoinc fields as unsafe, and will switch to row-format in Mixed mode. Actually, the statement is safe if just one autoinc value is used in sub-statement, but it's impossible to check how many autoinc values are used in sub-statement.) mysql-test/suite/rpl/r/rpl_auto_increment_update_failure.result: Test result for bug#45677 mysql-test/suite/rpl/t/rpl_auto_increment_update_failure.test: Added test to verify the following two properties: P1) insert/update in an autoinc column causes statement to be logged in row format if binlog_format=mixed P2) if binlog_format=mixed, and a trigger or function contains two or more inserts/updates in a table that has an autoinc column, then the slave should not go out of sync, even if there are concurrent transactions. sql/sql_base.cc: Added function 'has_write_table_with_auto_increment' to check if one (or more) write tables have auto_increment columns. Removed function 'has_two_write_locked_tables_with_auto_increment', because the function is included in function 'has_write_table_with_auto_increment'. --- sql/sql_base.cc | 48 +++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) (limited to 'sql/sql_base.cc') diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f55d3fc5006..89fe6dc4b41 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -106,7 +106,7 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, bool send_refresh); static bool -has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables); +has_write_table_with_auto_increment(TABLE_LIST *tables); extern "C" uchar *table_cache_key(const uchar *record, size_t *length, @@ -5277,15 +5277,17 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; /* - If we have >= 2 different tables to update with auto_inc columns, - statement-based binlogging won't work. We can solve this problem in - mixed mode by switching to row-based binlogging: + A query that modifies autoinc column in sub-statement can make the + master and slave inconsistent. + We can solve these problems in mixed mode by switching to binlogging + if at least one updated table is used by sub-statement */ - if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED && - has_two_write_locked_tables_with_auto_increment(tables)) + /* The BINLOG_FORMAT_MIXED judgement is saved for suppressing + warnings, but it will be removed by fixing bug#45827 */ + if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED && tables && + has_write_table_with_auto_increment(thd->lex->first_not_own_table())) { thd->lex->set_stmt_unsafe(); - thd->set_current_stmt_binlog_row_based_if_mixed(); } } @@ -8815,47 +8817,31 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table /* - Tells if two (or more) tables have auto_increment columns and we want to - lock those tables with a write lock. + Check if one (or more) write tables have auto_increment columns. - SYNOPSIS - has_two_write_locked_tables_with_auto_increment - tables Table list + @param[in] tables Table list + + @retval 0 if at least one write tables has an auto_increment column + @retval 1 otherwise NOTES: Call this function only when you have established the list of all tables which you'll want to update (including stored functions, triggers, views inside your statement). - - RETURN - 0 No - 1 Yes */ static bool -has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables) +has_write_table_with_auto_increment(TABLE_LIST *tables) { - char *first_table_name= NULL, *first_db; - LINT_INIT(first_db); - for (TABLE_LIST *table= tables; table; table= table->next_global) { /* we must do preliminary checks as table->table may be NULL */ if (!table->placeholder() && table->table->found_next_number_field && (table->lock_type >= TL_WRITE_ALLOW_WRITE)) - { - if (first_table_name == NULL) - { - first_table_name= table->table_name; - first_db= table->db; - DBUG_ASSERT(first_db); - } - else if (strcmp(first_db, table->db) || - strcmp(first_table_name, table->table_name)) - return 1; - } + return 1; } + return 0; } -- cgit v1.2.1