From e6a50e41da5e9e55031b978fba19a2b3ec4928b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Wed, 2 Oct 2019 13:24:34 +0300 Subject: [PATCH] MDEV-20051: Add new mode to wsrep_OSU_method in which Galera checks storage engine of the effected table Introduced a new wsrep_strict_ddl configuration variable in which Galera checks storage engine of the effected table. If table is not InnoDB (only storage engine currently fully supporting Galera replication) DDL-statement will return error code: ER_GALERA_REPLICATION_NOT_SUPPORTED eng "DDL-statement is forbidden as table storage engine does not support Galera replication" However, when wsrep_replicate_myisam=ON we allow DDL-statements to MyISAM tables. If effected table is allowed storage engine Galera will run normal TOI. This new setting should be for now set globally on all nodes in a cluster. When this setting is set following DDL-clauses accessing tables not supporting Galera replication are refused: * CREATE TABLE (e.g. CREATE TABLE t1(a int) engine=Aria * ALTER TABLE * TRUNCATE TABLE * CREATE VIEW * CREATE TRIGGER * CREATE INDEX * DROP INDEX * RENAME TABLE * DROP TABLE Statements on PROCEDURE, EVENT, FUNCTION are allowed as effected tables are known only at execution. Furthermore, USER, ROLE, SERVER, DATABASE statements are also allowed as they do not really have effected table. --- include/mysql/service_wsrep.h | 6 +- include/wsrep.h | 21 +- .../suite/galera/r/wsrep_strict_ddl.result | 194 ++++++++++++++++++ .../suite/galera/t/wsrep_strict_ddl.test | 133 ++++++++++++ .../suite/sys_vars/r/sysvars_wsrep.result | 15 ++ .../sys_vars/r/wsrep_strict_ddl_basic.result | 45 ++++ .../sys_vars/t/wsrep_strict_ddl_basic.test | 42 ++++ sql/service_wsrep.cc | 23 ++- sql/share/errmsg-utf8.txt | 2 + sql/sql_alter.cc | 6 +- sql/sql_parse.cc | 39 +++- sql/sql_plugin_services.ic | 1 + sql/sql_rename.cc | 7 + sql/sql_table.cc | 24 ++- sql/sql_trigger.cc | 8 +- sql/sql_truncate.cc | 20 +- sql/sql_view.cc | 9 + sql/sys_vars.cc | 10 +- sql/wsrep_dummy.cc | 2 + sql/wsrep_mysqld.cc | 159 +++++++++----- sql/wsrep_mysqld.h | 11 +- 21 files changed, 690 insertions(+), 87 deletions(-) create mode 100644 mysql-test/suite/galera/r/wsrep_strict_ddl.result create mode 100644 mysql-test/suite/galera/t/wsrep_strict_ddl.test create mode 100644 mysql-test/suite/sys_vars/r/wsrep_strict_ddl_basic.result create mode 100644 mysql-test/suite/sys_vars/t/wsrep_strict_ddl_basic.test diff --git a/include/mysql/service_wsrep.h b/include/mysql/service_wsrep.h index 30ed7246eac..8d132212d77 100644 --- a/include/mysql/service_wsrep.h +++ b/include/mysql/service_wsrep.h @@ -84,6 +84,7 @@ extern struct wsrep_service_st { my_bool (*wsrep_get_debug_func)(); void (*wsrep_commit_ordered_func)(MYSQL_THD thd); my_bool (*wsrep_thd_is_applying_func)(const MYSQL_THD thd); + ulong (*wsrep_OSU_method_get_func)(const MYSQL_THD thd); my_bool (*wsrep_thd_has_ignored_error_func)(const MYSQL_THD thd); void (*wsrep_thd_set_ignored_error_func)(MYSQL_THD thd, my_bool val); } *wsrep_service; @@ -126,9 +127,9 @@ extern struct wsrep_service_st { #define wsrep_get_debug() wsrep_service->wsrep_get_debug_func() #define wsrep_commit_ordered(T) wsrep_service->wsrep_commit_ordered_func(T) #define wsrep_thd_is_applying(T) wsrep_service->wsrep_thd_is_applying_func(T) +#define wsrep_OSU_method_get(T) wsrep_service->wsrep_OSU_method_get_func(T) #define wsrep_thd_has_ignored_error(T) wsrep_service->wsrep_thd_has_ignored_error_func(T) #define wsrep_thd_set_ignored_error(T,V) wsrep_service->wsrep_thd_set_ignored_error_func(T,V) - #else #define MYSQL_SERVICE_WSREP_STATIC_INCLUDED @@ -220,9 +221,8 @@ extern "C" my_bool wsrep_get_debug(); extern "C" void wsrep_commit_ordered(MYSQL_THD thd); extern "C" my_bool wsrep_thd_is_applying(const MYSQL_THD thd); - +extern "C" ulong wsrep_OSU_method_get(const MYSQL_THD thd); extern "C" my_bool wsrep_thd_has_ignored_error(const MYSQL_THD thd); extern "C" void wsrep_thd_set_ignored_error(MYSQL_THD thd, my_bool val); - #endif #endif /* MYSQL_SERVICE_WSREP_INCLUDED */ diff --git a/include/wsrep.h b/include/wsrep.h index dbdb746409b..f8a1863b966 100644 --- a/include/wsrep.h +++ b/include/wsrep.h @@ -19,18 +19,27 @@ #include #ifdef WITH_WSREP + #define IF_WSREP(A,B) A + #define DBUG_ASSERT_IF_WSREP(A) DBUG_ASSERT(A) #define WSREP_MYSQL_DB (char *)"mysql" + #define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \ - if (WSREP_ON && WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) \ + if (WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) \ goto wsrep_error_label; -#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_) \ +#define WSREP_TO_ISOLATION_BEGIN_CREATE(db_, table_, table_list_, create_info_) \ + if (WSREP(thd) && \ + wsrep_to_isolation_begin(thd, db_, table_, \ + table_list_, NULL, create_info_)) \ + goto wsrep_error_label; + +#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_, create_info_) \ if (WSREP(thd) && wsrep_thd_is_local(thd) && \ wsrep_to_isolation_begin(thd, db_, table_, \ - table_list_, alter_info_)) \ + table_list_, alter_info_, create_info_)) \ goto wsrep_error_label; #define WSREP_TO_ISOLATION_END \ @@ -56,14 +65,12 @@ * (e.g. embedded) */ #define IF_WSREP(A,B) B -//#define DBUG_ASSERT_IF_WSREP(A) #define WSREP_DEBUG(...) -//#define WSREP_INFO(...) -//#define WSREP_WARN(...) #define WSREP_ERROR(...) #define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) do { } while(0) -#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_) +#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_, create_info_) #define WSREP_TO_ISOLATION_END +#define WSREP_TO_ISOLATION_BEGIN_CREATE(db_, table_, table_list_, create_info_) #define WSREP_TO_ISOLATION_BEGIN_WRTCHK(db_, table_, table_list_) #define WSREP_SYNC_WAIT(thd_, before_) #endif /* WITH_WSREP */ diff --git a/mysql-test/suite/galera/r/wsrep_strict_ddl.result b/mysql-test/suite/galera/r/wsrep_strict_ddl.result new file mode 100644 index 00000000000..6d25ec35549 --- /dev/null +++ b/mysql-test/suite/galera/r/wsrep_strict_ddl.result @@ -0,0 +1,194 @@ +connection node_2; +connection node_1; +call mtr.add_suppression("WSREP: ALTER TABLE isolation failure"); +connection node_1; +SET GLOBAL binlog_format='ROW'; +create table before_t1(a int, count int, b int, key(b)) engine=Aria; +INSERT INTO before_t1 values (1,1,1); +set @@global.wsrep_strict_ddl=ON; +select @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +1 +connection node_2; +set @@global.wsrep_strict_ddl=ON; +select @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +1 +connection node_1; +CREATE TABLE t1(a int) engine=Aria; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW WARNINGS; +Level Code Message +Error 4165 DDL-statement is forbidden as table storage engine does not support Galera replication +Warning 1031 WSREP: wsrep_strict_ddl=true and storage engine does not support Galera replication. +connection node_2; +SHOW CREATE TABLE t1; +ERROR 42S02: Table 'test.t1' doesn't exist +connection node_1; +CREATE TABLE t2(a int not null primary key) engine=InnoDB; +ALTER TABLE t2 engine=MyISAM; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW WARNINGS; +Level Code Message +Error 4165 DDL-statement is forbidden as table storage engine does not support Galera replication +Warning 1031 WSREP: wsrep_strict_ddl=true and storage engine does not support Galera replication. +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +connection node_2; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +connection node_1; +TRUNCATE TABLE before_t1; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SELECT * FROM before_t1; +a count b +1 1 1 +connection node_2; +SET SESSION wsrep_sync_wait=15; +SELECT @@wsrep_sync_wait; +@@wsrep_sync_wait +15 +SELECT * FROM before_t1; +a count b +connection node_1; +CREATE VIEW x1 AS SELECT * FROM before_t1; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW CREATE VIEW x1; +ERROR 42S02: Table 'test.x1' doesn't exist +connection node_2; +SHOW CREATE VIEW x1; +ERROR 42S02: Table 'test.x1' doesn't exist +connection node_1; +CREATE DEFINER=`root`@`localhost` TRIGGER increment_before_t1 +AFTER INSERT ON before_t1 FOR EACH ROW +UPDATE before_t1 SET before_t1.count = before_t1.count+1; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW CREATE TRIGGER increment_before_t1; +ERROR HY000: Trigger does not exist +connection node_2; +SHOW CREATE TRIGGER increment_before_t1; +ERROR HY000: Trigger does not exist +connection node_1; +CREATE INDEX xx2 ON before_t1(a); +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +connection node_2; +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +connection node_1; +DROP INDEX b ON before_t1; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +connection node_2; +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +connection node_1; +ALTER TABLE before_t1 ADD COLUMN f int; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +connection node_2; +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +connection node_1; +RENAME TABLE before_t1 to after_t1; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +SHOW CREATE TABLE after_t1; +ERROR 42S02: Table 'test.after_t1' doesn't exist +connection node_2; +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +SHOW CREATE TABLE after_t1; +ERROR 42S02: Table 'test.after_t1' doesn't exist +connection node_1; +DROP TABLE before_t1; +ERROR HY000: DDL-statement is forbidden as table storage engine does not support Galera replication +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +connection node_2; +SHOW CREATE TABLE before_t1; +Table Create Table +before_t1 CREATE TABLE `before_t1` ( + `a` int(11) DEFAULT NULL, + `count` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 +connection node_1; +set @@global.wsrep_strict_ddl=OFF; +select @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +0 +connection node_2; +set @@global.wsrep_strict_ddl=OFF; +select @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +0 +DROP TABLE t2; +DROP TABLE before_t1; diff --git a/mysql-test/suite/galera/t/wsrep_strict_ddl.test b/mysql-test/suite/galera/t/wsrep_strict_ddl.test new file mode 100644 index 00000000000..c0a0cd756ba --- /dev/null +++ b/mysql-test/suite/galera/t/wsrep_strict_ddl.test @@ -0,0 +1,133 @@ +--source include/galera_cluster.inc + +call mtr.add_suppression("WSREP: ALTER TABLE isolation failure"); + +--connection node_1 +SET GLOBAL binlog_format='ROW'; +create table before_t1(a int, count int, b int, key(b)) engine=Aria; +INSERT INTO before_t1 values (1,1,1); + +set @@global.wsrep_strict_ddl=ON; +select @@global.wsrep_strict_ddl; + +--connection node_2 +set @@global.wsrep_strict_ddl=ON; +select @@global.wsrep_strict_ddl; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +CREATE TABLE t1(a int) engine=Aria; +SHOW WARNINGS; + +--connection node_2 +--error ER_NO_SUCH_TABLE +SHOW CREATE TABLE t1; + +--connection node_1 +CREATE TABLE t2(a int not null primary key) engine=InnoDB; + +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +ALTER TABLE t2 engine=MyISAM; +SHOW WARNINGS; +SHOW CREATE TABLE t2; + +--connection node_2 +SHOW CREATE TABLE t2; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +TRUNCATE TABLE before_t1; +SELECT * FROM before_t1; + +--connection node_2 +SET SESSION wsrep_sync_wait=15; +SELECT @@wsrep_sync_wait; +SELECT * FROM before_t1; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +CREATE VIEW x1 AS SELECT * FROM before_t1; +--error ER_NO_SUCH_TABLE +SHOW CREATE VIEW x1; + +--connection node_2 +--error ER_NO_SUCH_TABLE +SHOW CREATE VIEW x1; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +CREATE DEFINER=`root`@`localhost` TRIGGER increment_before_t1 + AFTER INSERT ON before_t1 FOR EACH ROW + UPDATE before_t1 SET before_t1.count = before_t1.count+1; + +--error ER_TRG_DOES_NOT_EXIST +SHOW CREATE TRIGGER increment_before_t1; + +--connection node_2 + +--error ER_TRG_DOES_NOT_EXIST +SHOW CREATE TRIGGER increment_before_t1; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +CREATE INDEX xx2 ON before_t1(a); +SHOW CREATE TABLE before_t1; + +--connection node_2 +SHOW CREATE TABLE before_t1; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +DROP INDEX b ON before_t1; +SHOW CREATE TABLE before_t1; + +--connection node_2 +SHOW CREATE TABLE before_t1; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +ALTER TABLE before_t1 ADD COLUMN f int; +SHOW CREATE TABLE before_t1; + +--connection node_2 +SHOW CREATE TABLE before_t1; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +RENAME TABLE before_t1 to after_t1; +SHOW CREATE TABLE before_t1; +--error ER_NO_SUCH_TABLE +SHOW CREATE TABLE after_t1; + +--connection node_2 +SHOW CREATE TABLE before_t1; +--error ER_NO_SUCH_TABLE +SHOW CREATE TABLE after_t1; + +--connection node_1 +--error ER_GALERA_REPLICATION_NOT_SUPPORTED +DROP TABLE before_t1; + +SHOW CREATE TABLE before_t1; + +--connection node_2 +SHOW CREATE TABLE before_t1; + +# +# PROCEDURE, EVENT, FUNCTION +# Unfortunately accessed tables are opened only +# in SP execution so no hope at CREATE + +# +# USER, ROLE, SERVER, DATABASE not really storage engine objects +# + +--connection node_1 +set @@global.wsrep_strict_ddl=OFF; +select @@global.wsrep_strict_ddl; + +--connectIon node_2 +set @@global.wsrep_strict_ddl=OFF; +select @@global.wsrep_strict_ddl; +DROP TABLE t2; +DROP TABLE before_t1; diff --git a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result index a1d66e4d112..328fa1fee16 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result +++ b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result @@ -706,6 +706,21 @@ ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED GLOBAL_VALUE_PATH NULL +VARIABLE_NAME WSREP_STRICT_DDL +SESSION_VALUE NULL +GLOBAL_VALUE OFF +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE OFF +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT Reject DDL on effected tables not supporting Galera replication +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT OPTIONAL +GLOBAL_VALUE_PATH NULL VARIABLE_NAME WSREP_SYNC_WAIT SESSION_VALUE 0 GLOBAL_VALUE 0 diff --git a/mysql-test/suite/sys_vars/r/wsrep_strict_ddl_basic.result b/mysql-test/suite/sys_vars/r/wsrep_strict_ddl_basic.result new file mode 100644 index 00000000000..6d686f9de2e --- /dev/null +++ b/mysql-test/suite/sys_vars/r/wsrep_strict_ddl_basic.result @@ -0,0 +1,45 @@ +# +# wsrep_strict_ddl +# +# save the initial value +SET @wsrep_strict_ddl_global_saved = @@global.wsrep_strict_ddl; +# default +SELECT @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +0 + +# scope +SELECT @@session.wsrep_strict_ddl; +ERROR HY000: Variable 'wsrep_strict_ddl' is a GLOBAL variable +SET @@global.wsrep_strict_ddl=OFF; +SELECT @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +0 +SET @@global.wsrep_strict_ddl=ON; +SELECT @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +1 + +# valid values +SET @@global.wsrep_strict_ddl='OFF'; +SELECT @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +0 +SET @@global.wsrep_strict_ddl=ON; +SELECT @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +1 +SET @@global.wsrep_strict_ddl=default; +SELECT @@global.wsrep_strict_ddl; +@@global.wsrep_strict_ddl +0 + +# invalid values +SET @@global.wsrep_strict_ddl=NULL; +ERROR 42000: Variable 'wsrep_strict_ddl' can't be set to the value of 'NULL' +SET @@global.wsrep_strict_ddl='junk'; +ERROR 42000: Variable 'wsrep_strict_ddl' can't be set to the value of 'junk' + +# restore the initial value +SET @@global.wsrep_strict_ddl = @wsrep_strict_ddl_global_saved; +# End of test diff --git a/mysql-test/suite/sys_vars/t/wsrep_strict_ddl_basic.test b/mysql-test/suite/sys_vars/t/wsrep_strict_ddl_basic.test new file mode 100644 index 00000000000..04d20ff3db5 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/wsrep_strict_ddl_basic.test @@ -0,0 +1,42 @@ +--source include/have_wsrep.inc + +--echo # +--echo # wsrep_strict_ddl +--echo # + +--echo # save the initial value +SET @wsrep_strict_ddl_global_saved = @@global.wsrep_strict_ddl; + +--echo # default +SELECT @@global.wsrep_strict_ddl; + +--echo +--echo # scope +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.wsrep_strict_ddl; +SET @@global.wsrep_strict_ddl=OFF; +SELECT @@global.wsrep_strict_ddl; +SET @@global.wsrep_strict_ddl=ON; +SELECT @@global.wsrep_strict_ddl; + +--echo +--echo # valid values +SET @@global.wsrep_strict_ddl='OFF'; +SELECT @@global.wsrep_strict_ddl; +SET @@global.wsrep_strict_ddl=ON; +SELECT @@global.wsrep_strict_ddl; +SET @@global.wsrep_strict_ddl=default; +SELECT @@global.wsrep_strict_ddl; + +--echo +--echo # invalid values +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.wsrep_strict_ddl=NULL; +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.wsrep_strict_ddl='junk'; + +--echo +--echo # restore the initial value +SET @@global.wsrep_strict_ddl = @wsrep_strict_ddl_global_saved; + +--echo # End of test diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index 68de55b5778..8a547710143 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -52,13 +52,14 @@ extern "C" const char* wsrep_thd_transaction_state_str(const THD *thd) return wsrep::to_c_string(thd->wsrep_cs().transaction().state()); } - extern "C" const char *wsrep_thd_query(const THD *thd) { - if (thd) + if (!thd) + return "NULL"; + + switch(thd->lex->sql_command) { - switch(thd->lex->sql_command) - { + // Mask away some security related details from error log case SQLCOM_CREATE_USER: return "CREATE USER"; case SQLCOM_GRANT: @@ -67,12 +68,10 @@ extern "C" const char *wsrep_thd_query(const THD *thd) return "REVOKE"; case SQLCOM_SET_OPTION: if (thd->lex->definer) - return "SET PASSWORD"; + return "SET PASSWORD"; /* fallthrough */ default: - if (thd->query()) - return thd->query(); - } + return (thd->query() ? thd->query() : "NULL"); } return "NULL"; } @@ -321,3 +320,11 @@ extern "C" void wsrep_thd_set_ignored_error(THD *thd, my_bool val) { thd->wsrep_has_ignored_error= val; } + +extern "C" ulong wsrep_OSU_method_get(const MYSQL_THD thd) +{ + if (thd) + return(thd->variables.wsrep_OSU_method); + else + return(global_system_variables.wsrep_OSU_method); +} diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index dbff2b204d8..78b6cfa63c8 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7943,3 +7943,5 @@ ER_WARN_HISTORY_ROW_START_TIME eng "Table `%s.%s` history row start '%s' is later than row end '%s'" ER_PART_STARTS_BEYOND_INTERVAL eng "%`s: STARTS is later than query time, first history partition may exceed INTERVAL value" +ER_GALERA_REPLICATION_NOT_SUPPORTED + eng "DDL-statement is forbidden as table storage engine does not support Galera replication" \ No newline at end of file diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index f3dd46b5139..0828e1b7ba8 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -505,9 +505,9 @@ bool Sql_cmd_alter_table::execute(THD *thd) (!thd->is_current_stmt_binlog_format_row() || !thd->find_temporary_table(first_table))) { - WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL), - (lex->name.str ? lex->name.str : NULL), - first_table, &alter_info); + WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : first_table->db.str), + (lex->name.str ? lex->name.str : first_table->table_name.str), + first_table, &alter_info, used_engine ? &create_info : NULL); thd->variables.auto_increment_offset = 1; thd->variables.auto_increment_increment = 1; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 141dfd9be65..e8b5e319773 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3079,6 +3079,7 @@ mysql_create_routine(THD *thd, LEX *lex) return true; WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + if (!lex->sphead->m_handler->sp_create_routine(thd, lex->sphead)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -4147,7 +4148,6 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_one_table_access(thd, INDEX_ACL, all_tables)) goto error; /* purecov: inspected */ - WSREP_TO_ISOLATION_BEGIN(first_table->db.str, first_table->table_name.str, NULL); bzero((char*) &create_info, sizeof(create_info)); create_info.db_type= 0; @@ -4155,6 +4155,8 @@ mysql_execute_command(THD *thd) create_info.default_table_charset= thd->variables.collation_database; create_info.alter_info= &alter_info; + WSREP_TO_ISOLATION_BEGIN(first_table->db.str, first_table->table_name.str, NULL); + res= mysql_alter_table(thd, &first_table->db, &first_table->table_name, &create_info, first_table, &alter_info, 0, (ORDER*) 0, 0); @@ -4857,6 +4859,7 @@ mysql_execute_command(THD *thd) slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT) lex->create_info.set(DDL_options_st::OPT_IF_EXISTS); +#ifdef WITH_WSREP if (WSREP(thd)) { for (TABLE_LIST *table= all_tables; table; table= table->next_global) @@ -4870,7 +4873,8 @@ mysql_execute_command(THD *thd) } } } - +#endif /* WITH_WSREP */ + /* DDL and binlog write order are protected by metadata locks. */ res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table(), lex->table_type == TABLE_TYPE_SEQUENCE); @@ -5070,7 +5074,9 @@ mysql_execute_command(THD *thd) (CREATE_ACL | DROP_ACL) : CREATE_ACL, &lex->name)) break; + WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL); + res= mysql_create_db(thd, &lex->name, lex->create_info, &lex->create_info); break; @@ -5079,7 +5085,9 @@ mysql_execute_command(THD *thd) { if (prepare_db_action(thd, DROP_ACL, &lex->name)) break; + WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL); + res= mysql_rm_db(thd, &lex->name, lex->if_exists()); break; } @@ -5111,7 +5119,9 @@ mysql_execute_command(THD *thd) res= 1; break; } + WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL); + res= mysql_upgrade_db(thd, db); if (!res) my_ok(thd); @@ -5122,7 +5132,9 @@ mysql_execute_command(THD *thd) LEX_CSTRING *db= &lex->name; if (prepare_db_action(thd, ALTER_ACL, db)) break; + WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL); + res= mysql_alter_db(thd, db, &lex->create_info); break; } @@ -5196,6 +5208,7 @@ mysql_execute_command(THD *thd) break; #ifdef HAVE_DLOPEN WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + if (!(res = mysql_create_function(thd, &lex->udf))) my_ok(thd); #else @@ -5213,7 +5226,9 @@ mysql_execute_command(THD *thd) "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + /* Conditionally writes to binlog */ if (!(res= mysql_create_user(thd, lex->users_list, lex->sql_command == SQLCOM_CREATE_ROLE))) @@ -5226,8 +5241,10 @@ mysql_execute_command(THD *thd) if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; - /* Conditionally writes to binlog */ + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + + /* Conditionally writes to binlog */ if (!(res= mysql_drop_user(thd, lex->users_list, lex->sql_command == SQLCOM_DROP_ROLE))) my_ok(thd); @@ -5239,8 +5256,10 @@ mysql_execute_command(THD *thd) if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; - /* Conditionally writes to binlog */ + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + + /* Conditionally writes to binlog */ if (lex->sql_command == SQLCOM_ALTER_USER) res= mysql_alter_user(thd, lex->users_list); else @@ -5255,16 +5274,19 @@ mysql_execute_command(THD *thd) check_global_access(thd,CREATE_USER_ACL)) break; - /* Conditionally writes to binlog */ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + + /* Conditionally writes to binlog */ if (!(res = mysql_revoke_all(thd, lex->users_list))) my_ok(thd); break; } + case SQLCOM_REVOKE_ROLE: case SQLCOM_GRANT_ROLE: { WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + if (!(res= mysql_grant_role(thd, lex->users_list, lex->sql_command != SQLCOM_GRANT_ROLE))) my_ok(thd); @@ -5611,6 +5633,7 @@ mysql_execute_command(THD *thd) case SQLCOM_DROP_PACKAGE: case SQLCOM_DROP_PACKAGE_BODY: if (drop_routine(thd, lex)) + goto error; break; case SQLCOM_SHOW_CREATE_PROC: @@ -5676,8 +5699,10 @@ mysql_execute_command(THD *thd) { if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; - /* Conditionally writes to binlog. */ + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + + /* Conditionally writes to binlog. */ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); break; } @@ -5983,7 +6008,7 @@ finish: #ifdef WITH_WSREP thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK; - + WSREP_TO_ISOLATION_END; /* Force release of transactional locks if not in active MST and wsrep is on. diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic index 3faabcf5032..2db426e7b57 100644 --- a/sql/sql_plugin_services.ic +++ b/sql/sql_plugin_services.ic @@ -175,6 +175,7 @@ static struct wsrep_service_st wsrep_handler = { wsrep_get_debug, wsrep_commit_ordered, wsrep_thd_is_applying, + wsrep_OSU_method_get, wsrep_thd_has_ignored_error, wsrep_thd_set_ignored_error }; diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 1c6cf52963a..8f818e321dd 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -286,6 +286,13 @@ do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db, if (ha_table_exists(thd, &ren_table->db, &old_alias, &hton) && hton) { DBUG_ASSERT(!thd->locked_tables_mode); + +#ifdef WITH_WSREP + if (WSREP(thd) && hton && + !wsrep_should_replicate_ddl(thd, hton->db_type)) + DBUG_RETURN(1); +#endif + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, ren_table->db.str, ren_table->table_name.str); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e4a33dd99cd..240f001f7de 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2443,6 +2443,16 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { char *end; int frm_delete_error= 0; + +#ifdef WITH_WSREP + if (WSREP(thd) && + !wsrep_should_replicate_ddl(thd, table_type->db_type)) + { + error= 1; + goto err; + } +#endif + /* It could happen that table's share in the table definition cache is the only thing that keeps the engine plugin loaded @@ -9461,6 +9471,17 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, &alter_prelocking_strategy); thd->open_options&= ~HA_OPEN_FOR_ALTER; +#ifdef WITH_WSREP + if (WSREP(thd) && + (thd->lex->sql_command == SQLCOM_ALTER_TABLE || + thd->lex->sql_command == SQLCOM_CREATE_INDEX || + thd->lex->sql_command == SQLCOM_DROP_INDEX) && + !wsrep_should_replicate_ddl(thd, table_list->table->s->db_type()->db_type)) + DBUG_RETURN(true); +#endif + + DEBUG_SYNC(thd, "alter_table_after_open_tables"); + TABLE *table= table_list->table; bool versioned= table && table->versioned(); @@ -11503,7 +11524,8 @@ bool Sql_cmd_create_table_like::execute(THD *thd) (!thd->is_current_stmt_binlog_format_row() || !create_info.tmp_table())) { - WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, NULL); + WSREP_TO_ISOLATION_BEGIN_CREATE(create_table->db.str, create_table->table_name.str, + create_table, &create_info); } /* Regular CREATE TABLE */ res= mysql_create_table(thd, create_table, &create_info, &alter_info); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 5802d2c811e..0ae3d58b11f 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -507,9 +507,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) goto end; } -#ifdef WITH_WSREP WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, tables); -#endif /* We should have only one table in table list. */ DBUG_ASSERT(tables->next_global == 0); @@ -550,6 +548,12 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) } table= tables->table; +#ifdef WITH_WSREP + if (WSREP(thd) && + !wsrep_should_replicate_ddl(thd, table->s->db_type()->db_type)) + goto wsrep_error_label; +#endif + /* Later on we will need it to downgrade the lock */ mdl_ticket= table->mdl_ticket; diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index 8e078ff02af..a47822e43ca 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -303,6 +303,12 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, versioned= table->versioned(); hton= table->file->ht; +#ifdef WITH_WSREP + if (WSREP(thd) && + !wsrep_should_replicate_ddl(thd, hton->db_type)) + DBUG_RETURN(TRUE); +#endif + table_ref->mdl_request.ticket= table->mdl_ticket; } else @@ -320,6 +326,15 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, versioned= share->versioned; sequence= share->table_type == TABLE_TYPE_SEQUENCE; hton= share->db_type(); +#ifdef WITH_WSREP + if (WSREP(thd) && + hton != view_pseudo_hton && + !wsrep_should_replicate_ddl(thd, hton->db_type)) + { + tdc_release_share(share); + DBUG_RETURN(TRUE); + } +#endif tdc_release_share(share); @@ -417,9 +432,10 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) #ifdef WITH_WSREP if (WSREP(thd) && - wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, 0)) - DBUG_RETURN(TRUE); + wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, NULL)) + DBUG_RETURN(TRUE); #endif /* WITH_WSREP */ + if (lock_table(thd, table_ref, &hton_can_recreate)) DBUG_RETURN(TRUE); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 12dfb8bc59d..f47e910c0fe 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -37,6 +37,7 @@ #include "sql_derived.h" #include "sql_cte.h" // check_dependencies_in_with_clauses() #include "opt_trace.h" +#include "wsrep_mysqld.h" #define MD5_BUFF_LENGTH 33 @@ -454,6 +455,14 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, goto err; } +#ifdef WITH_WSREP + if(!wsrep_should_replicate_ddl_iterate(thd, static_cast(tables))) + { + res= TRUE; + goto err; + } +#endif + view= lex->unlink_first_table(&link_to_local); if (check_db_dir_existence(view->db.str)) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index ab382304973..4dfec12ee29 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -5685,12 +5685,20 @@ static Sys_var_enum Sys_wsrep_OSU_method( static PolyLock_mutex PLock_wsrep_desync(&LOCK_wsrep_desync); static Sys_var_mybool Sys_wsrep_desync ( "wsrep_desync", "To desynchronize the node from the cluster", - GLOBAL_VAR(wsrep_desync), + GLOBAL_VAR(wsrep_desync), CMD_LINE(OPT_ARG), DEFAULT(FALSE), &PLock_wsrep_desync, NOT_IN_BINLOG, ON_CHECK(wsrep_desync_check), ON_UPDATE(wsrep_desync_update)); +static Sys_var_mybool Sys_wsrep_strict_ddl ( + "wsrep_strict_ddl", "Reject DDL on effected tables not supporting Galera replication", + GLOBAL_VAR(wsrep_strict_ddl), + CMD_LINE(OPT_ARG), DEFAULT(FALSE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, + ON_CHECK(0), + ON_UPDATE(0)); + static const char *wsrep_reject_queries_names[]= { "NONE", "ALL", "ALL_KILL", NullS }; static Sys_var_enum Sys_wsrep_reject_queries( "wsrep_reject_queries", "Variable to set to reject queries", diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index b87e807c7c9..ffc6acc8bd9 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -144,3 +144,5 @@ my_bool wsrep_thd_has_ignored_error(const THD*) void wsrep_thd_set_ignored_error(THD*, my_bool) { } +ulong wsrep_OSU_method_get(const THD*) +{ return 0;} \ No newline at end of file diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index a3c5e91eb24..ff01473e816 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -96,6 +96,9 @@ my_bool wsrep_restart_slave; // Should mysql slave thread be // restarted, when node joins back? my_bool wsrep_desync; // De(re)synchronize the node from the // cluster +my_bool wsrep_strict_ddl; // Reject DDL to + // effected tables not + // supporting Galera replication long wsrep_slave_threads; // No. of slave appliers threads ulong wsrep_retry_autocommit; // Retry aborted autocommit trx ulong wsrep_max_ws_size; // Max allowed ws (RBR buffer) size @@ -1296,11 +1299,11 @@ static bool wsrep_prepare_key_for_isolation(const char* db, } static bool wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db, - Alter_info* alter_info, + const Alter_info* alter_info, wsrep_key_arr_t* ka) { Key *key; - List_iterator key_iterator(alter_info->key_list); + List_iterator key_iterator(const_cast(alter_info)->key_list); while ((key= key_iterator++)) { if (key->type == Key::FOREIGN_KEY) @@ -1436,12 +1439,12 @@ wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table, wsrep::key_array wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db, - Alter_info* alter_info) + const Alter_info* alter_info) { wsrep::key_array ret; Key *key; - List_iterator key_iterator(alter_info->key_list); + List_iterator key_iterator(const_cast(alter_info)->key_list); while ((key= key_iterator++)) { if (key->type == Key::FOREIGN_KEY) @@ -1463,7 +1466,7 @@ wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db, wsrep::key_array wsrep_prepare_keys_for_toi(const char* db, const char* table, const TABLE_LIST* table_list, - Alter_info* alter_info) + const Alter_info* alter_info) { wsrep::key_array ret; if (db || table) @@ -1735,6 +1738,51 @@ static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len) /* Forward declarations. */ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len); +bool wsrep_should_replicate_ddl_iterate(THD* thd, const TABLE_LIST* table_list) +{ + if (WSREP(thd)) + { + for (const TABLE_LIST* it= table_list; it; it= it->next_global) + { + if (!wsrep_should_replicate_ddl(thd, it->table->s->db_type()->db_type)) + return false; + } + } + return true; +} + +bool wsrep_should_replicate_ddl(THD* thd, + const enum legacy_db_type db_type) +{ + if (!wsrep_strict_ddl) + return true; + + switch (db_type) + { + case DB_TYPE_INNODB: + return true; + break; + case DB_TYPE_MYISAM: + if (wsrep_replicate_myisam) + return true; + else + WSREP_DEBUG("wsrep OSU failed for %s", wsrep_thd_query(thd)); + break; + case DB_TYPE_ARIA: + /* if (wsrep_replicate_aria) */ + /* fallthrough */ + default: + WSREP_DEBUG("wsrep OSU failed for %s", wsrep_thd_query(thd)); + break; + } + + /* STRICT, treat as error */ + my_error(ER_GALERA_REPLICATION_NOT_SUPPORTED, MYF(0)); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_ILLEGAL_HA, + "WSREP: wsrep_strict_ddl=true and storage engine does not support Galera replication."); + return false; +} /* Decide if statement should run in TOI. @@ -1745,24 +1793,28 @@ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len); should be rewritten at later time for replication to contain only non-temporary tables. */ -static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, - const TABLE_LIST *table_list) +bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, + const TABLE_LIST *table_list, + const HA_CREATE_INFO* create_info) { DBUG_ASSERT(!table || db); DBUG_ASSERT(table_list || db); LEX* lex= thd->lex; SELECT_LEX* select_lex= lex->first_select_lex(); - TABLE_LIST* first_table= select_lex->table_list.first; + const TABLE_LIST* first_table= select_lex->table_list.first; switch (lex->sql_command) { case SQLCOM_CREATE_TABLE: - DBUG_ASSERT(!table_list); if (thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) { return false; } + if (!wsrep_should_replicate_ddl(thd, create_info->db_type->db_type)) + { + return false; + } /* If mariadb master has replicated a CTAS, we should not replicate the create table part separately as TOI, but to replicate both create table and following inserts @@ -1771,7 +1823,8 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, as TOI. We have to do relay log event lookup to see if row events follow the create table event. */ - if (thd->slave_thread && !(thd->rgi_slave->gtid_ev_flags2 & Gtid_log_event::FL_STANDALONE)) + if (thd->slave_thread && + !(thd->rgi_slave->gtid_ev_flags2 & Gtid_log_event::FL_STANDALONE)) { /* this is CTAS, either empty or populated table */ ulonglong event_size = 0; @@ -1797,7 +1850,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, } /* no next async replication event */ return true; - + break; case SQLCOM_CREATE_VIEW: DBUG_ASSERT(!table_list); @@ -1806,7 +1859,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, If any of the remaining tables refer to temporary table error is returned to client, so TOI can be skipped */ - for (TABLE_LIST* it= first_table->next_global; it; it= it->next_global) + for (const TABLE_LIST* it= first_table->next_global; it; it= it->next_global) { if (thd->find_temporary_table(it)) { @@ -1814,7 +1867,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, } } return true; - + break; case SQLCOM_CREATE_TRIGGER: DBUG_ASSERT(first_table); @@ -1824,15 +1877,12 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, return false; } return true; - - case SQLCOM_DROP_TRIGGER: - DBUG_ASSERT(table_list); - if (thd->find_temporary_table(table_list)) - { + break; + case SQLCOM_ALTER_TABLE: + if (create_info && + !wsrep_should_replicate_ddl(thd, create_info->db_type->db_type)) return false; - } - return true; - + /* fallthrough */ default: if (table && !thd->find_temporary_table(db, table)) { @@ -1841,7 +1891,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, if (table_list) { - for (TABLE_LIST* table= first_table; table; table= table->next_global) + for (const TABLE_LIST* table= first_table; table; table= table->next_global) { if (!thd->find_temporary_table(table->db.str, table->table_name.str)) { @@ -1849,11 +1899,12 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, } } } + return !(table || table_list); + break; } } - static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len) { String log_query; @@ -1962,14 +2013,16 @@ fail: */ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, const TABLE_LIST* table_list, - Alter_info* alter_info) + const Alter_info* alter_info, + const HA_CREATE_INFO* create_info) { - DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI); + DBUG_ASSERT(wsrep_OSU_method_get(thd) == WSREP_OSU_TOI); - WSREP_DEBUG("TOI Begin"); - if (wsrep_can_run_in_toi(thd, db, table, table_list) == false) + WSREP_DEBUG("TOI Begin: %s", wsrep_thd_query(thd)); + + if (wsrep_can_run_in_toi(thd, db, table, table_list, create_info) == false) { - WSREP_DEBUG("No TOI for %s", WSREP_QUERY(thd)); + WSREP_DEBUG("No TOI for %s", wsrep_thd_query(thd)); return 1; } @@ -1979,6 +2032,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, int rc; buf_err= wsrep_TOI_event_buf(thd, &buf, &buf_len); + if (buf_err) { WSREP_ERROR("Failed to create TOI event buf: %d", buf_err); my_message(ER_UNKNOWN_ERROR, @@ -1987,6 +2041,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, MYF(0)); return -1; } + struct wsrep_buf buff= { buf, buf_len }; wsrep::key_array key_array= @@ -1997,7 +2052,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, /* non replicated DDL, affecting temporary tables only */ WSREP_DEBUG("TO isolation skipped, sql: %s." "Only temporary tables affected.", - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); if (buf) my_free(buf); return -1; } @@ -2005,6 +2060,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, thd_proc_info(thd, "acquiring total order isolation"); wsrep::client_state& cs(thd->wsrep_cs()); + int ret= cs.enter_toi_local(key_array, wsrep::const_buffer(buff.ptr, buff.len)); @@ -2012,7 +2068,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, { DBUG_ASSERT(cs.current_error()); WSREP_DEBUG("to_execute_start() failed for %llu: %s, seqno: %lld", - thd->thread_id, WSREP_QUERY(thd), + thd->thread_id, wsrep_thd_query(thd), (long long)wsrep_thd_trx_seqno(thd)); /* jump to error handler in mysql_execute_command() */ @@ -2023,7 +2079,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, "Maximum size exceeded.", ret, (thd->db.str ? thd->db.str : "(null)"), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); my_error(ER_ERROR_DURING_COMMIT, MYF(0), WSREP_SIZE_EXCEEDED); break; default: @@ -2031,7 +2087,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, "Check wsrep connection state and retry the query.", ret, (thd->db.str ? thd->db.str : "(null)"), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); if (!thd->is_error()) { my_error(ER_LOCK_DEADLOCK, MYF(0), "WSREP replication failed. Check " @@ -2079,7 +2135,7 @@ static void wsrep_TOI_end(THD *thd) { wsrep::client_state& client_state(thd->wsrep_cs()); DBUG_ASSERT(wsrep_thd_is_local_toi(thd)); WSREP_DEBUG("TO END: %lld: %s", client_state.toi_meta().seqno().get(), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); wsrep_gtid_server.signal_waiters(thd->wsrep_current_gtid_seqno, false); @@ -2104,7 +2160,7 @@ static void wsrep_TOI_end(THD *thd) { else { WSREP_WARN("TO isolation end failed for: %d, schema: %s, sql: %s", - ret, (thd->db.str ? thd->db.str : "(null)"), WSREP_QUERY(thd)); + ret, (thd->db.str ? thd->db.str : "(null)"), wsrep_thd_query(thd)); } } } @@ -2112,7 +2168,7 @@ static void wsrep_TOI_end(THD *thd) { static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) { WSREP_DEBUG("RSU BEGIN: %lld, : %s", wsrep_thd_trx_seqno(thd), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); if (thd->wsrep_cs().begin_rsu(5000)) { WSREP_WARN("RSU begin failed"); @@ -2127,7 +2183,7 @@ static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) static void wsrep_RSU_end(THD *thd) { WSREP_DEBUG("RSU END: %lld : %s", wsrep_thd_trx_seqno(thd), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); if (thd->wsrep_cs().end_rsu()) { WSREP_WARN("Failed to end RSU, server may need to be restarted"); @@ -2137,7 +2193,8 @@ static void wsrep_RSU_end(THD *thd) int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list, - Alter_info* alter_info) + const Alter_info* alter_info, + const HA_CREATE_INFO* create_info) { /* No isolation for applier or replaying threads. @@ -2162,14 +2219,14 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, if (thd->global_read_lock.is_acquired()) { WSREP_DEBUG("Aborting TOI: Global Read-Lock (FTWRL) in place: %s %llu", - WSREP_QUERY(thd), thd->thread_id); + wsrep_thd_query(thd), thd->thread_id); return -1; } if (wsrep_debug && thd->mdl_context.has_locks()) { WSREP_DEBUG("thread holds MDL locks at TI begin: %s %llu", - WSREP_QUERY(thd), thd->thread_id); + wsrep_thd_query(thd), thd->thread_id); } /* @@ -2187,24 +2244,24 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, if (thd->variables.wsrep_on && wsrep_thd_is_local(thd)) { - switch (thd->variables.wsrep_OSU_method) { + switch (wsrep_OSU_method_get(thd)) { case WSREP_OSU_TOI: - ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info); + ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info, create_info); break; case WSREP_OSU_RSU: ret= wsrep_RSU_begin(thd, db_, table_); break; default: WSREP_ERROR("Unsupported OSU method: %lu", - thd->variables.wsrep_OSU_method); + wsrep_OSU_method_get(thd)); ret= -1; break; } switch (ret) { - case 0: /* wsrep_TOI_begin sould set toi mode */ break; + case 0: /* wsrep_TOI_begin should set toi mode */ break; case 1: - /* TOI replication skipped, treat as success */ - ret= 0; + /* TOI replication skipped, treat as success */ + ret= 0; break; case -1: /* TOI replication failed, treat as error */ @@ -2221,12 +2278,12 @@ void wsrep_to_isolation_end(THD *thd) wsrep_thd_is_in_rsu(thd)); if (wsrep_thd_is_local_toi(thd)) { - DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI); + DBUG_ASSERT(wsrep_OSU_method_get(thd) == WSREP_OSU_TOI); wsrep_TOI_end(thd); } else if (wsrep_thd_is_in_rsu(thd)) { - DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_RSU); + DBUG_ASSERT(wsrep_OSU_method_get(thd) == WSREP_OSU_RSU); wsrep_RSU_end(thd); } else @@ -2678,17 +2735,17 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table, if (create_info->tmp_table()) { /* CREATE TEMPORARY TABLE LIKE must be skipped from replication */ - WSREP_DEBUG("CREATE TEMPORARY TABLE LIKE... skipped replication\n %s", + WSREP_DEBUG("CREATE TEMPORARY TABLE LIKE... skipped replication\n %s", thd->query()); } else if (!(thd->find_temporary_table(src_table))) { /* this is straight CREATE TABLE LIKE... with no tmp tables */ - WSREP_TO_ISOLATION_BEGIN(table->db.str, table->table_name.str, NULL); + WSREP_TO_ISOLATION_BEGIN_CREATE(table->db.str, table->table_name.str, table, create_info); } else { - /* here we have CREATE TABLE LIKE + /* here we have CREATE TABLE LIKE the temporary table definition will be needed in slaves to enable the create to succeed */ @@ -2707,7 +2764,7 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table, thd->wsrep_TOI_pre_query= query.ptr(); thd->wsrep_TOI_pre_query_len= query.length(); - WSREP_TO_ISOLATION_BEGIN(table->db.str, table->table_name.str, NULL); + WSREP_TO_ISOLATION_BEGIN_CREATE(table->db.str, table->table_name.str, table, create_info); thd->wsrep_TOI_pre_query= NULL; thd->wsrep_TOI_pre_query_len= 0; diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 077e5602025..b51dc308646 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -98,6 +98,7 @@ extern ulong wsrep_running_applier_threads; extern ulong wsrep_running_rollbacker_threads; extern bool wsrep_new_cluster; extern bool wsrep_gtid_mode; +extern my_bool wsrep_strict_ddl; enum enum_wsrep_reject_types { WSREP_REJECT_NONE, /* nothing rejected */ @@ -108,7 +109,7 @@ enum enum_wsrep_reject_types { enum enum_wsrep_OSU_method { WSREP_OSU_TOI, WSREP_OSU_RSU, - WSREP_OSU_NONE, + WSREP_OSU_NONE }; enum enum_wsrep_sync_wait { @@ -360,9 +361,15 @@ extern PSI_thread_key key_wsrep_sst_donor_monitor; struct TABLE_LIST; class Alter_info; +struct HA_CREATE_INFO; + int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list, - Alter_info* alter_info= NULL); + const Alter_info* alter_info= NULL, + const HA_CREATE_INFO* create_info= NULL); + +bool wsrep_should_replicate_ddl(THD* thd, const enum legacy_db_type db_type); +bool wsrep_should_replicate_ddl_iterate(THD* thd, const TABLE_LIST* table_list); void wsrep_to_isolation_end(THD *thd);