Permalink
Browse files

Bug#20797764: FAILED CREATE VIEW IS BINLOGGED, AND NOT

FILTERED OUT

Problem:
========
Replication slave choke on statement that should be ignored
instead.

Analysis:
========
There are two problems in the above mentioned bug.
1) Failed CREATE VIEW statement is written to the binary log
2) When a statement which has failed on master is received
on slave with an expected error and if the statement is
skipped on slave due to replication filter we still try to
compare the expected error with actual error that happened
on slave. Which is incorrect.

Fix:
===
For the first problem since CREATE VIEW statement cannot
result in partial view creation, it should not be binlogged
if the view creation fails. We check for the return status
of view creation and avoid writing to binlog on error.

For the second problem if a statement with expected error is
received on slave and only if the statement is not filtered
then only compare it with actual error that happened on
slave.
  • Loading branch information...
Sujatha Sivakumar
Sujatha Sivakumar committed Aug 12, 2015
1 parent af7b2b0 commit b127c273f4f66a69761089043db3c1d6a49e2d36
@@ -0,0 +1,57 @@
include/master-slave.inc
Warnings:
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
[connection master]
"Test case1"
[connection master]
CREATE DATABASE db_a;
CREATE DATABASE db_b;
include/sync_slave_sql_with_master.inc
FLUSH LOCAL RELAY LOGS;
[connection master]
USE db_b;
CREATE VIEW view_b AS SELECT NULL;
include/sync_slave_sql_with_master.inc
[connection master]
CREATE VIEW view_b AS SELECT NULL;
ERROR 42S01: Table 'view_b' already exists
include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query # # CREATE DATABASE db_a
master-bin.000001 # Query # # CREATE DATABASE db_b
master-bin.000001 # Query # # use `db_b`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `view_b` AS SELECT NULL
include/sync_slave_sql_with_master.inc
include/show_relaylog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
slave-relay-bin.000003 # Format_desc # # SERVER_VERSION, BINLOG_VERSION
slave-relay-bin.000003 # Query # # use `db_b`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `view_b` AS SELECT NULL
EXPLAIN view_b;
ERROR 42S02: Table 'test.view_b' doesn't exist
[connection master]
DROP DATABASE db_a;
DROP DATABASE db_b;
include/sync_slave_sql_with_master.inc
include/rpl_reset.inc
"Test case2"
[connection master]
CREATE DATABASE db_a;
CREATE DATABASE db_b;
include/sync_slave_sql_with_master.inc
[connection master]
USE db_b;
CREATE TABLE t2 (f INT PRIMARY KEY) ENGINE=MYISAM;
INSERT INTO t2 VALUES (1);
INSERT INTO t2 VALUES (2);
INSERT INTO t2 VALUES (3);
INSERT INTO t2 VALUES (5);
include/sync_slave_sql_with_master.inc
[connection master]
UPDATE t2 SET f=f+2 WHERE f>=2;
ERROR 23000: Duplicate entry '5' for key 'PRIMARY'
include/sync_slave_sql_with_master.inc
[connection master]
DROP DATABASE db_a;
DROP DATABASE db_b;
include/sync_slave_sql_with_master.inc
include/rpl_end.inc
@@ -0,0 +1 @@
--replicate-do-db=db_a
@@ -0,0 +1,85 @@
###############################################################################
# Bug#20797764: FAILED CREATE VIEW IS BINLOGGED, AND NOT FILTERED OUT
#
# Problem:
# ========
# Replication slave choke on statement that should be ignored instead.
#
# Test:
# =====
# Create two database db_a and db_b. Add a replicate-do-db=db_a filter on
# slave. On master create a view view_b on master on db_b database. This
# statement will be replicated and ignored on slave. Re-executing same CREATE
# VIEW statement on master will result in an error. As part of test we prove
# that failed CREATE VIEW statement is not binlogged and slave will not error
# out saying "query caused different errors on master and slave". Slave should
# be in sync with master.
###############################################################################
--source include/master-slave.inc
echo "Test case1";
--source include/rpl_connection_master.inc
CREATE DATABASE db_a;
CREATE DATABASE db_b;
--source include/sync_slave_sql_with_master.inc
FLUSH LOCAL RELAY LOGS;
--source include/rpl_connection_master.inc
USE db_b;
CREATE VIEW view_b AS SELECT NULL;
--source include/sync_slave_sql_with_master.inc
--source include/rpl_connection_master.inc
--error ER_TABLE_EXISTS_ERROR
CREATE VIEW view_b AS SELECT NULL;
# Prove that CREATE VIEW statement is binlogged only once
--source include/show_binlog_events.inc
# Prove that slave doesnot error out and is in sync with master
--source include/sync_slave_sql_with_master.inc
# Prove that CREATE VIEW statement was received by slave
--let $binlog_file= LAST
--source include/show_relaylog_events.inc
# Prove that the CREATE VIEW statement is ignored by slave
--error ER_NO_SUCH_TABLE
EXPLAIN view_b;
--source include/rpl_connection_master.inc
DROP DATABASE db_a;
DROP DATABASE db_b;
--source include/sync_slave_sql_with_master.inc
--source include/rpl_reset.inc
echo "Test case2";
# Create two databases db_a and db_b. Execute a partial update statement such
# that it results in an error on master and still gets binlogged. When the
# statement is received on slave it should not cause the slave to break.
# Without fix slave will error out with following error message.
# Last_Error Query caused different errors on master and slave. Error on
# master: message (format)='Duplicate entry '%-.192s' for key %d' error
# code=1062 ; Error on slave: actual message='no error', error code=0. Default
# database: 'db_b'. Query: 'update t2 set f=f+2 where f>=2'.
--source include/rpl_connection_master.inc
CREATE DATABASE db_a;
CREATE DATABASE db_b;
--source include/sync_slave_sql_with_master.inc
--source include/rpl_connection_master.inc
USE db_b;
CREATE TABLE t2 (f INT PRIMARY KEY) ENGINE=MYISAM;
INSERT INTO t2 VALUES (1);
INSERT INTO t2 VALUES (2);
INSERT INTO t2 VALUES (3);
INSERT INTO t2 VALUES (5);
--source include/sync_slave_sql_with_master.inc
--source include/rpl_connection_master.inc
--error ER_DUP_ENTRY
UPDATE t2 SET f=f+2 WHERE f>=2;
--source include/sync_slave_sql_with_master.inc
--source include/rpl_connection_master.inc
DROP DATABASE db_a;
DROP DATABASE db_b;
--source include/sync_slave_sql_with_master.inc
--source include/rpl_end.inc
View
@@ -4913,7 +4913,13 @@ START SLAVE; . Query: '%s'", expected_error, thd->query());
DBUG_PRINT("info",("expected_error: %d sql_errno: %d",
expected_error, actual_error));
if ((expected_error && expected_error != actual_error &&
/*
If a statement with expected error is received on slave and if the
statement is not filtered on the slave, only then compare the expected
error with the actual error that happened on slave.
*/
if ((expected_error && rpl_filter->db_ok(thd->db) &&
expected_error != actual_error &&
!concurrency_error_code(expected_error)) &&
!ignored_error_code(actual_error) &&
!ignored_error_code(expected_error))
View
@@ -692,53 +692,53 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
*/
if (!res)
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name, false);
if (mysql_bin_log.is_open())
{
String buff;
const LEX_STRING command[3]=
{{ C_STRING_WITH_LEN("CREATE ") },
{ C_STRING_WITH_LEN("ALTER ") },
{ C_STRING_WITH_LEN("CREATE OR REPLACE ") }};
buff.append(command[thd->lex->create_view_mode].str,
command[thd->lex->create_view_mode].length);
view_store_options(thd, views, &buff);
buff.append(STRING_WITH_LEN("VIEW "));
/* Test if user supplied a db (ie: we did not use thd->db) */
if (views->db && views->db[0] &&
(thd->db == NULL || strcmp(views->db, thd->db)))
{
append_identifier(thd, &buff, views->db,
views->db_length);
buff.append('.');
}
append_identifier(thd, &buff, views->table_name,
views->table_name_length);
if (lex->view_list.elements)
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name, false);
if (mysql_bin_log.is_open())
{
List_iterator_fast<LEX_STRING> names(lex->view_list);
LEX_STRING *name;
int i;
for (i= 0; (name= names++); i++)
String buff;
const LEX_STRING command[3]=
{{ C_STRING_WITH_LEN("CREATE ") },
{ C_STRING_WITH_LEN("ALTER ") },
{ C_STRING_WITH_LEN("CREATE OR REPLACE ") }};
buff.append(command[thd->lex->create_view_mode].str,
command[thd->lex->create_view_mode].length);
view_store_options(thd, views, &buff);
buff.append(STRING_WITH_LEN("VIEW "));
/* Test if user supplied a db (ie: we did not use thd->db) */
if (views->db && views->db[0] &&
(thd->db == NULL || strcmp(views->db, thd->db)))
{
buff.append(i ? ", " : "(");
append_identifier(thd, &buff, name->str, name->length);
append_identifier(thd, &buff, views->db,
views->db_length);
buff.append('.');
}
buff.append(')');
}
buff.append(STRING_WITH_LEN(" AS "));
buff.append(views->source.str, views->source.length);
append_identifier(thd, &buff, views->table_name,
views->table_name_length);
if (lex->view_list.elements)
{
List_iterator_fast<LEX_STRING> names(lex->view_list);
LEX_STRING *name;
int i;
int errcode= query_error_code(thd, TRUE);
thd->add_to_binlog_accessed_dbs(views->db);
if (thd->binlog_query(THD::STMT_QUERY_TYPE,
buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcode))
res= TRUE;
}
for (i= 0; (name= names++); i++)
{
buff.append(i ? ", " : "(");
append_identifier(thd, &buff, name->str, name->length);
}
buff.append(')');
}
buff.append(STRING_WITH_LEN(" AS "));
buff.append(views->source.str, views->source.length);
int errcode= query_error_code(thd, TRUE);
thd->add_to_binlog_accessed_dbs(views->db);
if (thd->binlog_query(THD::STMT_QUERY_TYPE,
buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcode))
res= TRUE;
}
}
if (mode != VIEW_CREATE_NEW)
query_cache_invalidate3(thd, view, 0);
if (res)

0 comments on commit b127c27

Please sign in to comment.