Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions mysql-test/suite/ndb_rpl/r/ndb_rpl_bug_rondb-475.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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 connection metadata repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START REPLICA; see the 'START REPLICA Syntax' in the MySQL Manual for more information.
[connection master]
CREATE TABLE t1 (a INT) ENGINE=NDB;
CREATE TABLE t2 (a INT) ENGINE=NDB;
CREATE USER 'ndb_u1'@'localhost' IDENTIFIED by 'pass1';
GRANT NDB_STORED_USER ON *.* TO 'ndb_u1'@'localhost';
SHOW TABLES;
Tables_in_test
t2
SELECT user, host FROM mysql.user WHERE user LIKE 'ndb_%';
user host
ndb_u1 localhost
SELECT grantee FROM information_schema.user_privileges
WHERE privilege_type='NDB_STORED_USER';
grantee
'ndb_u1'@'localhost'
DROP TABLE t1;
DROP TABLE t2;
DROP USER ndb_u1@localhost;
include/rpl_end.inc
1 change: 1 addition & 0 deletions mysql-test/suite/ndb_rpl/t/ndb_rpl_bug_rondb-475-slave.opt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--replicate-ignore-table=test.t1
1 change: 1 addition & 0 deletions mysql-test/suite/ndb_rpl/t/ndb_rpl_bug_rondb-475.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!include suite/ndb_rpl/my.cnf
37 changes: 37 additions & 0 deletions mysql-test/suite/ndb_rpl/t/ndb_rpl_bug_rondb-475.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This is a regression test for bug:
# RONDB-475 Mysqld Segfault when reversing replication role

# This bug causes mysqld on the replica to crash when replicating a GRANT
# NDB_STORED_USER statement while replication filtering is active.

# For this test case, replication filtering is activated in file
# suite/ndb_rpl/t/ndb_rpl_bug_rondb-475-slave.opt

--source include/have_ndb.inc
--source suite/ndb_rpl/ndb_master-slave.inc

# Create tables and user on master then execute GRANT NDB_STORED_USER
connection master;
CREATE TABLE t1 (a INT) ENGINE=NDB;
CREATE TABLE t2 (a INT) ENGINE=NDB;
CREATE USER 'ndb_u1'@'localhost' IDENTIFIED by 'pass1';
GRANT NDB_STORED_USER ON *.* TO 'ndb_u1'@'localhost';
--source include/wait_for_ndb_committed_to_binlog.inc
sync_slave_with_master;

# Check that t2 and ndb_u1 replicates but not t1
connection slave;
SHOW TABLES;
SELECT user, host FROM mysql.user WHERE user LIKE 'ndb_%';
SELECT grantee FROM information_schema.user_privileges
WHERE privilege_type='NDB_STORED_USER';

# Cleanup
connection master;
DROP TABLE t1;
DROP TABLE t2;
DROP USER ndb_u1@localhost;
--source include/wait_for_ndb_committed_to_binlog.inc
sync_slave_with_master;

--source include/rpl_end.inc
1 change: 1 addition & 0 deletions sql/event_scheduler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ void pre_init_event_thread(THD *thd) {
thd->security_context()->set_user_ptr(STRING_WITH_LEN("event_scheduler"));
thd->get_protocol_classic()->get_net()->read_timeout = replica_net_timeout;
thd->slave_thread = false;
thd->override_slave_filtering = THD::NO_OVERRIDE_SLAVE_FILTERING;
thd->variables.option_bits |= OPTION_AUTO_IS_NULL;
thd->get_protocol_classic()->set_client_capabilities(CLIENT_MULTI_RESULTS);

Expand Down
1 change: 1 addition & 0 deletions sql/rpl_replica.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4061,6 +4061,7 @@ int init_replica_thread(THD *thd, SLAVE_THD_TYPE thd_type) {
: SYSTEM_THREAD_SLAVE_IO;
thd->get_protocol_classic()->init_net(nullptr);
thd->slave_thread = true;
thd->override_slave_filtering = THD::NO_OVERRIDE_SLAVE_FILTERING;
thd->enable_slow_log = opt_log_slow_replica_statements;
set_slave_thread_options(thd);

Expand Down
1 change: 1 addition & 0 deletions sql/sql_class.cc
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,7 @@ THD::THD(bool enable_plugins)
lex->set_current_query_block(nullptr);
m_lock_usec = 0L;
slave_thread = false;
override_slave_filtering = THD::NO_OVERRIDE_SLAVE_FILTERING;
memset(&variables, 0, sizeof(variables));
m_thread_id = Global_THD_manager::reserved_thread_id;
file_id = 0;
Expand Down
12 changes: 12 additions & 0 deletions sql/sql_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -2697,6 +2697,18 @@ class THD : public MDL_context_owner,
/// instead /sven
bool slave_thread;

// override_slave_filtering is used to indicate that we want to execute a
// query that normally would be suppressed in a slave thread.
enum enum_override_slave_filtering {
// Use a couple of semi-random constants to ensure initialization. They are
// chosen to encode "OSFY" and "OSFN" (meaning "override slave filtering
// yes/no") to aid debugging.
// Debug search terms: OSFY, OSFN, YFSO, NFSO
OVERRIDE_SLAVE_FILTERING = 0x4f534659,
NO_OVERRIDE_SLAVE_FILTERING = 0x4f53464e,
};
enum enum_override_slave_filtering override_slave_filtering;

uchar password;

private:
Expand Down
10 changes: 9 additions & 1 deletion sql/sql_parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3011,6 +3011,12 @@ int mysql_execute_command(THD *thd, bool first_level) {

CONDITIONAL_SYNC_POINT_FOR_TIMESTAMP("before_execute_command");

DBUG_ASSERT(thd->override_slave_filtering ==
THD::NO_OVERRIDE_SLAVE_FILTERING ||
(thd->override_slave_filtering ==
THD::OVERRIDE_SLAVE_FILTERING &&
thd->slave_thread));

/*
If there is a CREATE TABLE...START TRANSACTION command which
is not yet committed or rollbacked, then we should allow only
Expand Down Expand Up @@ -3207,12 +3213,14 @@ int mysql_execute_command(THD *thd, bool first_level) {
in 5.0 there are no SET statements in the binary log)
- DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we
have stale files on slave caused by exclusion of one tmp table).
- Slave filtering is overridden, e.g. since the query is internal.
*/
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)) {
all_tables_not_ok(thd, all_tables) &&
thd->override_slave_filtering == THD::NO_OVERRIDE_SLAVE_FILTERING) {
/* we warn the slave SQL thread */
my_error(ER_REPLICA_IGNORED_TABLE, MYF(0));
binlog_gtid_end_transaction(thd);
Expand Down
21 changes: 21 additions & 0 deletions storage/ndb/plugin/ndb_stored_grants.cc
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,29 @@ void ThreadContext::deserialize_users(std::string &str) {
/* returns false on success */
bool ThreadContext::exec_sql(const std::string &statement) {
assert(m_closed);

/* Many queries can be ignored if we are a slave thread. Since this query is
executed for internal purposes, we always want it to execute. We therefore
set a flag to override suppression of query execution in slave thread. */
DBUG_ASSERT(m_thd->override_slave_filtering ==
THD::NO_OVERRIDE_SLAVE_FILTERING);
if(m_thd->slave_thread) {
m_thd->override_slave_filtering = THD::OVERRIDE_SLAVE_FILTERING;
}

/* execute_query_iso() returns false on success */
m_closed = execute_query_iso(statement, nullptr);

/* Restore the flag for overriding suppression of query execution */
if(m_thd->slave_thread) {
DBUG_ASSERT(m_thd->override_slave_filtering ==
THD::OVERRIDE_SLAVE_FILTERING);
m_thd->override_slave_filtering = THD::NO_OVERRIDE_SLAVE_FILTERING;
} else {
DBUG_ASSERT(m_thd->override_slave_filtering ==
THD::NO_OVERRIDE_SLAVE_FILTERING);
}

return m_closed;
}

Expand Down
6 changes: 6 additions & 0 deletions storage/ndb/plugin/ndb_thd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ Ndb *check_ndb_in_thd(THD *thd, bool validate_ndb) {
if (!thd_ndb->recycle_ndb()) return nullptr;
}

DBUG_ASSERT(thd->override_slave_filtering ==
THD::NO_OVERRIDE_SLAVE_FILTERING ||
(thd->override_slave_filtering ==
THD::OVERRIDE_SLAVE_FILTERING &&
thd->slave_thread));

return thd_ndb->ndb;
}

Expand Down