1. We are using fully-qualified table names (rpl_bug1475107working.test).
DROP TABLE IF EXISTS db1.table1;
2. We are using "USE db1;" statement (rpl_bug1475107working.test).
USE db1;
...
DROP TABLE IF EXISTS table1;
The difference in behavior turned out to be with mysql_execute_command() at sql_parse.cc:2609
The following code fragment behaves differently in case (1) and (2)
The problem is that in case (1) "thd->db" is set to a database name which is not even mentioned in the original SQL statement "DROP TABLE IF EXISTS db1.table1;". In case of running via MTR, it was set to "test". So, rpl_filter->db_ok(thd->db) checking returned "true" as database "test" is not a name in the replication ignore list. Therefore, "DBUG_RETURN(0)" was skipped.
Later, at sql_parse.cc:2785
if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
!(lex->sql_command == SQLCOM_SET_OPTION) &&
!(lex->sql_command == SQLCOM_DROP_TABLE && lex->drop_temporary && lex->drop_if_exists) && all_tables_not_ok(thd, all_tables))
{
/* we warn the slave SQL thread */
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
this function set error in thd->m_stmt_da to ER_SLAVE_IGNORED_TABLE and immediately returned "0".
Case (2) is completely different. thd->db was set to "db1" and therefore function execution terminated at
if(... && !rpl_filter->db_ok(thd->db))
DBUG_RETURN(0);
However, it did not change anything in "thd->m_stmt_da". "sql_errno" there was still 0.
After that, in Query_log_event::do_apply_event() we compare master error with slave error
log_event.cc:4968
if ((expected_error && expected_error != actual_error && !concurrency_error_code(expected_error)) && !ignored_error_code(actual_error) && !ignored_error_code(expected_error))
{
rli->report(ERROR_LEVEL, 0, "\
Query caused different errors on master and slave. \
Error on master: message (format)='%s' error code=%d ; \
Error on slave: actual message='%s', error code=%d. \
Default database: '%s'. Query: '%s'", ER_SAFE(expected_error), expected_error, actual_error ? thd->get_stmt_da()->message() : "no error", actual_error, print_slave_db_safe(db), query_arg);
To sum up, for the "non-failing" case (1) it works only because we pass invalid database name in rpl_filter->db_ok(). At the same time, "failing" case (2) fails only because sql_errno wasn't changed (was still zero) when the statement was ignored.
Consider 2 test cases included in /github. com/percona- ysorokin/ percona- server/ commit/ 5749da6de8e4d1e cd4970ad3288637 9b3bb80c57
https:/
1. We are using fully-qualified table names (rpl_bug1475107 working. test). working. test).
DROP TABLE IF EXISTS db1.table1;
2. We are using "USE db1;" statement (rpl_bug1475107
USE db1;
...
DROP TABLE IF EXISTS table1;
The difference in behavior turned out to be with mysql_execute_ command( ) at sql_parse.cc:2609
The following code fragment behaves differently in case (1) and (2)
sql_parse.cc:2694 thd->slave_ thread) )
lex->sql_ command != SQLCOM_COMMIT &&
lex->sql_ command != SQLCOM_SAVEPOINT &&
lex->sql_ command != SQLCOM_ROLLBACK &&
lex->sql_ command != SQLCOM_ ROLLBACK_ TO_SAVEPOINT &&
!rpl_filter- >db_ok( thd->db) ) RETURN( 0);
#ifdef HAVE_REPLICATION
if (unlikely(
{
// Database filters.
if (lex->sql_command != SQLCOM_BEGIN &&
DBUG_
The problem is that in case (1) "thd->db" is set to a database name which is not even mentioned in the original SQL statement "DROP TABLE IF EXISTS db1.table1;". In case of running via MTR, it was set to "test". So, rpl_filter- >db_ok( thd->db) checking returned "true" as database "test" is not a name in the replication ignore list. Therefore, "DBUG_RETURN(0)" was skipped. UPDATE_ MULTI) &&
lex- >drop_temporary && lex->drop_ if_exists) &&
all_tables_ not_ok( thd, all_tables)) message( ER_SLAVE_ IGNORED_ TABLE, ER(ER_SLAVE_ IGNORED_ TABLE), MYF(0));
Later, at sql_parse.cc:2785
if (!(lex->sql_command == SQLCOM_
!(lex->sql_command == SQLCOM_SET_OPTION) &&
!(lex->sql_command == SQLCOM_DROP_TABLE &&
{
/* we warn the slave SQL thread */
my_
this function set error in thd->m_stmt_da to ER_SLAVE_ IGNORED_ TABLE and immediately returned "0".
Case (2) is completely different. thd->db was set to "db1" and therefore function execution terminated at >db_ok( thd->db) ) event:: do_apply_ event() we compare master error with slave error
if(... && !rpl_filter-
DBUG_RETURN(0);
However, it did not change anything in "thd->m_stmt_da". "sql_errno" there was still 0.
After that, in Query_log_
log_event.cc:4968
if ((expected_error && expected_error != actual_error &&
!concurrency_ error_code( expected_ error)) &&
!ignored_ error_code( actual_ error) &&
!ignored_ error_code( expected_ error)) >report( ERROR_LEVEL, 0,
"\
ER_SAFE( expected_ error),
expected_ error,
actual_ error ? thd->get_ stmt_da( )->message( ) : "no error",
actual_ error,
print_ slave_db_ safe(db) , query_arg);
{
rli-
Query caused different errors on master and slave. \
Error on master: message (format)='%s' error code=%d ; \
Error on slave: actual message='%s', error code=%d. \
Default database: '%s'. Query: '%s'",
To sum up, for the "non-failing" case (1) it works only because we pass invalid database name in rpl_filter- >db_ok( ). At the same time, "failing" case (2) fails only because sql_errno wasn't changed (was still zero) when the statement was ignored.